Binary search in file by index - php

Im trying to make a script to get information in a file. The data in the file has this format:
#index|info1|info2|info3|info4|#index2|info21|info22|info23|info24
So I have to make a binary search direct on the file, look for the index and get all its information. For example:
$index = "index2";
Output: index2 info21 info22 info24
I tried:
$file = "file.txt";
// open the file for reading
$myfile = fopen($file, "r") or die("File cannot be openned");
$indexToSearch = "9780857293039";
$begging = 0;
$end = filesize($file) / sizeof($indexToSearch) - 1;
while($begging <= $end) {
$middle = ($end + $begging) / 2; );
$line = fread($myfile, $middle);
if(strcmp($line,$indexToSearch) == 0) {
echo "found";
break;
} else
if(strcmp($line,$indexToSearch) > 0) {
$end = $middle - 1;
} else {
$begging = $middle + 1;
}
}
fseek($myfile, $middle);
echo "<br><br>FINAL: ".fgets($myfile);

Related

How to implement pagination using CSV and PHP [duplicate]

I have a small script that displays blog posts from a text file, how can I add pagination so that it only shows 5 blog posts at a time?
Here is the script:
<html>
<head>
<title>blog</title>
</head>
<body>
<?php
$mode = 0;
if ($mode == 0) { $opFile = "blogfile.txt"; }
$fp = fopen($opFile,"r") or die("Error Reading File");
$data = fread($fp, filesize($opFile));
fclose($fp);
$line = explode("\n", $data);
$i=count($line);
for ($n=0 ; $n < $i-1 ; $n++ ) {
$blog = explode("|", $line[$n]);
if (isset($blog[0]))
{
echo "<div class=\"blog-post\">";
echo "<p class=\"blog-title\">".$blog[1]."</p>";
echo "<p class=\"blog-message\">".$blog[2]."</p>";
echo "<p class=\"blog-date\">Posted: " .$blog[0]."</p>";
echo "<div style=\"clear: both;\"></div>";
echo "</div>";
}
}
?>
</body>
</html>
And here is the text file:
Feb 17 2010|Title|Blog post content here|[end]
Feb 17 2010|Title|Blog post content here|[end]
Feb 17 2010|Title|Blog post content here|[end]
Feb 17 2010|Title|Blog post content here|[end]
Any help is greatly appreciated!
Something like this:
<html>
<head>
<title>blog</title>
</head>
<body>
<?php
$POSTS_PER_PAGE = 10;
//Not sure what this is for, but I left it?
$mode = 0;
if ($mode == 0) { $opFile = "blogfile.txt"; }
//Explode the textfile into lines
$lines = file($opFile);
$posts = array();
foreach($lines as $line) {
//Ignore blank lines
if($line != "") {
//Explode each non-empty line
$post = explode("|", $line);
//Store the blog post
array_push($posts, $post)
}
}
//Output the pagination links
echo "<div class=\"blog-pagination\">";
for($i = 1; $i < ceil(count($posts) / $POSTS_PER_PAGE; $i++) {
echo '' + $i + ' ';
}
echo "</div>";
//Assume the user wants the first page if it's not specified
if(!isset($_GET['page'])) {
$_GET['page'] = 1;
}
//Figure out the first and last posts on this page
$first_post = ($_GET['page'] - 1) * $POSTS_PER_PAGE;
$last_post = $_GET['page'] * $POSTS_PER_PAGE - 1;
//Display the requested posts
for($i = $first_post; $i <= $last_post; $i++) {
echo "<div class=\"blog-post\">";
echo "<p class=\"blog-title\">".$blog[1]."</p>";
echo "<p class=\"blog-message\">".$blog[2]."</p>";
echo "<p class=\"blog-date\">Posted: " .$blog[0]."</p>";
echo "<div style=\"clear: both;\"></div>";
echo "</div>";
}
?>
(This is completely untested, but hopefully you can take it from here!)
This worked in my tests:
define('MAX_PER_PAGE',10);
// sanity checks for per-page and page index
$numPosts = ctype_digit((string)$_GET['perpage']) ? $_GET['perpage'] : 5;
$ostart = $start = max(1, ctype_digit((string)$_GET['page']) ? $_GET['page'] : 1) - 1;
$mode = 0;
if ($mode == 0) {
$file = "blogfile.txt";
}
// read the file into an array, strip newlines and ignore empty lines
file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES | FILE_TEXT);
// sort array (see custom function at bottom)
usort($line, 'blogsort');
$lines = count($line);
// get total number of pages
$numPages = ceil($lines / $numPosts);
// additional sanity checks (also sets $ostart if it was invalid; used later)
$numPosts = min(MAX_PER_PAGE, max(1, $numPosts));
if ($start * $numPosts > $lines ) {
$ostart = $start = max(0, $lines - $numPosts);
}
else {
$start *= $numPosts;
}
// Only grab the part of the array we need
$sliced = array_slice($line, $start, $numPosts);
// loop through posts, but break early if we run out
for ($n = 0; $n < $numPosts && isset($sliced[$n]); $n++ ) {
$blog = explode("|", $sliced[$n]);
if (isset($blog[0])) {
echo "<div class=\"blog-post\">\n",
"<p class=\"blog-title\">{$blog[1]}</p>\n",
"<p class=\"blog-message\">{$blog[2]}</p>\n",
"<p class=\"blog-date\">Posted: {$blog[0]}</p>\n",
"<div style=\"clear: both;\"></div>\n",
"</div>\n\n";
}
}
// back link
if ($ostart > 0) {
echo "← Older";
}
else {
echo "None Older";
}
echo " || ";
// forward link
if ($ostart + 1 < $numPages) {
$next = $ostart + 2;
echo "Newer →";
}
else {
echo "None Newer";
}
function blogsort($a, $b) {
$dateA = strtotime(substr($a, 0, strpos($a, '|')));
$dateB = strtotime(substr($b, 0, strpos($b, '|')));
if ($dateA == $dateB) {
return 0;
}
elseif ($dateA > $dateB) {
return -1;
}
else {
return 1;
}
}

PHP: Read from certain point in file

Similar to: How to read only 5 last line of the text file in PHP?
I have a large log file and I want to be able to show 100 lines from position X in the file.
I need to use fseek rather than file() because the log file is too large.
I have a similar function but it will only read from the end of the file. How can it be modified so that a start position can be specified as well? I would also need to start at the end of the file.
function read_line($filename, $lines, $revers = false)
{
$offset = -1;
$i = 0;
$fp = #fopen($filename, "r");
while( $lines && fseek($fp, $offset, SEEK_END) >= 0 ) {
$c = fgetc($fp);
if($c == "\n" || $c == "\r"){
$lines--;
if($revers){
$read[$i] = strrev($read[$i]);
$i++;
}
}
if($revers) $read[$i] .= $c;
else $read .= $c;
$offset--;
}
fclose ($fp);
if($revers){
if($read[$i] == "\n" || $read[$i] == "\r")
array_pop($read);
else $read[$i] = strrev($read[$i]);
return implode('',$read);
}
return strrev(rtrim($read,"\n\r"));
}
What I'm trying to do is create a web based log viewer that will start from the end of the file and display 100 lines, and when pressing the "Next" button, the next 100 lines preceding it will be shown.
If you're on Unix, you can utilize the sed tool. For example: to get line 10-20 from a file:
sed -n 10,20p errors.log
And you can do this in your script:
<?php
$page = 1;
$limit = 100;
$off = ($page * $limit) - ($limit - 1);
exec("sed -n $off,".($limit+$off-1)."p errors.log", $out);
print_r($out);
The lines are available in $out array.
This uses fseek to read 100 lines of a file starting from a specified offset. If the offset is greater than the number of lines in the log, the first 100 lines are read.
In your application, you could pass the current offset through the query string for prev and next and base the next offset on that. You could also store and pass the current file position for more efficiency.
<?php
$GLOBALS["interval"] = 100;
read_log();
function read_log()
{
$fp = fopen("log", "r");
$offset = determine_offset();
$interval = $GLOBALS["interval"];
if (seek_to_offset($fp, $offset) != -1)
{
show_next_button($offset, $interval);
}
$lines = array();
for ($ii = 0; $ii < $interval; $ii++)
{
$lines[] = trim(fgets($fp));
}
echo "<pre>";
print_r(array_reverse($lines));
}
// Get the offset from the query string or default to the interval
function determine_offset()
{
$interval = $GLOBALS["interval"];
if (isset($_GET["offset"]))
{
return intval($_GET["offset"]) + $interval;
}
return $interval;
}
function show_next_button($offset, $interval)
{
$next_offset = $offset + $interval;
echo "Next";
}
// Seek to the end of the file, then seek backward $offset lines
function seek_to_offset($fp, $offset)
{
fseek($fp, 0, SEEK_END);
for ($ii = 0; $ii < $offset; $ii++)
{
if (seek_to_previous_line($fp) == -1)
{
rewind($fp);
return -1;
}
}
}
// Seek backward by char until line break
function seek_to_previous_line($fp)
{
fseek($fp, -2, SEEK_CUR);
while (fgetc($fp) != "\n")
{
if (fseek($fp, -2, SEEK_CUR) == -1)
{
return -1;
}
}
}
Is "position X" measured in lines or bytes? If lines, you can easily use SplFileObject to seek to a certain line and then read 100 lines:
$file = new SplFileObject('log.txt');
$file->seek(199); // go to line 200
for($i = 0; $i < 100 and $file->valid(); $i++, $file->next())
{
echo $file->current();
}
If position X is measured in bytes, isn't it a simple matter of changing your initial $offset = -1 to a different value?
I would do it as followed:
function readFileFunc($tempFile){
if(#!file_exists($tempFile)){
return FALSE;
}else{
return file($tempFile);
}
}
$textArray = readFileFunc('./data/yourTextfile.txt');
$slicePos = count($textArray)-101;
if($slicePos < 0){
$slicePos = 0;
}
$last100 = array_slice($textArray, $slicePos);
$last100 = implode('<br />', $last100);
echo $last100;

PHP - Convert single byte to Integer (PCX Graphic Format)

I'm working on a project that involves talking directly with Zebra printers. Currently, I am trying to convert images to the GRF format using something called "ztools". ZTools appears to be ancient and doesn't always convert the graphic correctly.
I've began digging up information on the PCX file format it is converting from and now find myself in byte land. This is my reference at this time: PCX Technical Reference
So, I have a basic PCX file I saved from photoshop that is 2x2 and would look like this:
10
01
This is where I am stuck, however. I've never worked with bytes and am attempting to read the pcx file with PHP using fopen("file", "rb"); and fread. However, it seems no matter what I do, I get a bunch of zeros. Anyone know what I need to do to convert the bytes to there numeric equivalents?
This is my weak attempt:
<?php
$file = "test.pcx";
// Open the file for binary reading (b flag)
$handle = fopen($file, "rb");
while (!feof($handle)) {
$contents = fread($handle, 1);
$contents = $contents >> 8;
echo $contents >> 8;
$content .= $contents;
}
fclose($handle);
I was able to get the header information accurately with this:
<?php
$file = "test.pcx";
function intToBits($int) {
$return = "00000000";
if ($int <= 255 && $int >= 0) {
$check = 128;
$x = 0;
while ($int > 0) {
if ($int > $check) {
$return[$x] = "1";
}
$int -= $check;
$check /= 2;
$x++;
}
}
return $return;
}
// Open the file for binary reading (b flag)
$handle = fopen($file, "rb");
$PCX_MAP = array();
$PCX_MAP['Manufacturer'][0] = 1; // Manufacturer
$PCX_MAP['Version'] = 1; // Version Info
$PCX_MAP['Encoding'] = 1; // Encoding
$PCX_MAP['BitsPerPixel'] = 1; // BitsPerPixel
$PCX_MAP['Xmin'] = 2; // Window Xmin
$PCX_MAP['Ymin'] = 2; // Window Ymin
$PCX_MAP['Xmax'] = 2; // Window Xmax
$PCX_MAP['Ymax'] = 2; // Window Ymax
$PCX_MAP['HDpi'] = 2; // HDpi (Resolution)
$PCX_MAP['VDpi'] = 2; // VDpi (Resolution)
$PCX_MAP['colormap'] = 48; // Colormap
$PCX_MAP['Reserved'] = 1; // Reserved = 0
$PCX_MAP['NumberColorPlanes'] = 1; // Number of color planes
$PCX_MAP['BytesPerLine'] = 2; // Bytes Per Line
$PCX_MAP['PalleteInfo'] = 2; // Palette Info
$PCX_MAP['HorizontalScreenSize'] = 2; // H Screen Size
$PCX_MAP['VerticalScreenSize'] = 2; // V Screen Size
$PCX_MAP['Filler'] = 54; // Filler
$length = reset($PCX_MAP);
while (!feof($handle)) {
if ($length !== false) {
$contents = fread($handle, $length);
$contents = ord($contents);
echo key($PCX_MAP) . " : {$contents}\n";
$content .= $contents;
$length = next($PCX_MAP);
}
else {
// Get the rest 1 By 1
$contents = fread($handle, 1);
$contents = ord($contents);
$contents = intToBits($contents);
echo $contents ."\n";
}
}
fclose($handle);
/*
$data = file_get_contents($file);
for($i = 0; $i < strlen($data); ++$i) {
$char = ord($data[$i]);
echo "Byte $i: $char\n";
}*/
However, I am still attempting to parse the image data.
This currently returns this:
Manufacturer : 10
Version : 5
Encoding : 1
BitsPerPixel : 1
Xmin : 0
Ymin : 0
Xmax : 3
Ymax : 1
HDpi : 200
VDpi : 200
colormap : 15
Reserved : 0
NumberColorPlanes : 1
BytesPerLine : 2
PalleteInfo : 1
HorizontalScreenSize : 0
VerticalScreenSize : 0
Filler : 0
10000000
11000000
11111110
00000000
11000000
11111110
00000000
The header information is correct, but I'm not sure about the data after filler.
The graphic ran in this instance is a 4 x 2 - 0 means white, 1 means black
0101
1010
Try this:
while (!feof($handle)) {
$contents = fread($handle, 1);
$contents = $contents && 0xFF;
echo $contents;
$content .= $contents;
}

Paginate text file with PHP

I have a small script that displays blog posts from a text file, how can I add pagination so that it only shows 5 blog posts at a time?
Here is the script:
<html>
<head>
<title>blog</title>
</head>
<body>
<?php
$mode = 0;
if ($mode == 0) { $opFile = "blogfile.txt"; }
$fp = fopen($opFile,"r") or die("Error Reading File");
$data = fread($fp, filesize($opFile));
fclose($fp);
$line = explode("\n", $data);
$i=count($line);
for ($n=0 ; $n < $i-1 ; $n++ ) {
$blog = explode("|", $line[$n]);
if (isset($blog[0]))
{
echo "<div class=\"blog-post\">";
echo "<p class=\"blog-title\">".$blog[1]."</p>";
echo "<p class=\"blog-message\">".$blog[2]."</p>";
echo "<p class=\"blog-date\">Posted: " .$blog[0]."</p>";
echo "<div style=\"clear: both;\"></div>";
echo "</div>";
}
}
?>
</body>
</html>
And here is the text file:
Feb 17 2010|Title|Blog post content here|[end]
Feb 17 2010|Title|Blog post content here|[end]
Feb 17 2010|Title|Blog post content here|[end]
Feb 17 2010|Title|Blog post content here|[end]
Any help is greatly appreciated!
Something like this:
<html>
<head>
<title>blog</title>
</head>
<body>
<?php
$POSTS_PER_PAGE = 10;
//Not sure what this is for, but I left it?
$mode = 0;
if ($mode == 0) { $opFile = "blogfile.txt"; }
//Explode the textfile into lines
$lines = file($opFile);
$posts = array();
foreach($lines as $line) {
//Ignore blank lines
if($line != "") {
//Explode each non-empty line
$post = explode("|", $line);
//Store the blog post
array_push($posts, $post)
}
}
//Output the pagination links
echo "<div class=\"blog-pagination\">";
for($i = 1; $i < ceil(count($posts) / $POSTS_PER_PAGE; $i++) {
echo '' + $i + ' ';
}
echo "</div>";
//Assume the user wants the first page if it's not specified
if(!isset($_GET['page'])) {
$_GET['page'] = 1;
}
//Figure out the first and last posts on this page
$first_post = ($_GET['page'] - 1) * $POSTS_PER_PAGE;
$last_post = $_GET['page'] * $POSTS_PER_PAGE - 1;
//Display the requested posts
for($i = $first_post; $i <= $last_post; $i++) {
echo "<div class=\"blog-post\">";
echo "<p class=\"blog-title\">".$blog[1]."</p>";
echo "<p class=\"blog-message\">".$blog[2]."</p>";
echo "<p class=\"blog-date\">Posted: " .$blog[0]."</p>";
echo "<div style=\"clear: both;\"></div>";
echo "</div>";
}
?>
(This is completely untested, but hopefully you can take it from here!)
This worked in my tests:
define('MAX_PER_PAGE',10);
// sanity checks for per-page and page index
$numPosts = ctype_digit((string)$_GET['perpage']) ? $_GET['perpage'] : 5;
$ostart = $start = max(1, ctype_digit((string)$_GET['page']) ? $_GET['page'] : 1) - 1;
$mode = 0;
if ($mode == 0) {
$file = "blogfile.txt";
}
// read the file into an array, strip newlines and ignore empty lines
file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES | FILE_TEXT);
// sort array (see custom function at bottom)
usort($line, 'blogsort');
$lines = count($line);
// get total number of pages
$numPages = ceil($lines / $numPosts);
// additional sanity checks (also sets $ostart if it was invalid; used later)
$numPosts = min(MAX_PER_PAGE, max(1, $numPosts));
if ($start * $numPosts > $lines ) {
$ostart = $start = max(0, $lines - $numPosts);
}
else {
$start *= $numPosts;
}
// Only grab the part of the array we need
$sliced = array_slice($line, $start, $numPosts);
// loop through posts, but break early if we run out
for ($n = 0; $n < $numPosts && isset($sliced[$n]); $n++ ) {
$blog = explode("|", $sliced[$n]);
if (isset($blog[0])) {
echo "<div class=\"blog-post\">\n",
"<p class=\"blog-title\">{$blog[1]}</p>\n",
"<p class=\"blog-message\">{$blog[2]}</p>\n",
"<p class=\"blog-date\">Posted: {$blog[0]}</p>\n",
"<div style=\"clear: both;\"></div>\n",
"</div>\n\n";
}
}
// back link
if ($ostart > 0) {
echo "← Older";
}
else {
echo "None Older";
}
echo " || ";
// forward link
if ($ostart + 1 < $numPages) {
$next = $ostart + 2;
echo "Newer →";
}
else {
echo "None Newer";
}
function blogsort($a, $b) {
$dateA = strtotime(substr($a, 0, strpos($a, '|')));
$dateB = strtotime(substr($b, 0, strpos($b, '|')));
if ($dateA == $dateB) {
return 0;
}
elseif ($dateA > $dateB) {
return -1;
}
else {
return 1;
}
}

PHP Remote file streaming with Resume Support

Firstly, I am aware of similar question being asked before.
The subject pretty much explains the question but still,
the file is hosted on another server, the user will download file via my script, streamed to him...
But the problem is user can't resume it once paused...any solutions?
You can try implementing your own download script using Accept-Ranges and Content-Range here is a prof of concept :
set_time_limit(0);
$download = new ResumeDownload("word.dir.txt", 50000); //delay about in microsecs
$download->process();
Using Internet Download Manager
Start
Paused
Paused State
Resume
Finished
Class Used
class ResumeDownload {
private $file;
private $name;
private $boundary;
private $delay = 0;
private $size = 0;
function __construct($file, $delay = 0) {
if (! is_file($file)) {
header("HTTP/1.1 400 Invalid Request");
die("<h3>File Not Found</h3>");
}
$this->size = filesize($file);
$this->file = fopen($file, "r");
$this->boundary = md5($file);
$this->delay = $delay;
$this->name = basename($file);
}
public function process() {
$ranges = NULL;
$t = 0;
if ($_SERVER['REQUEST_METHOD'] == 'GET' && isset($_SERVER['HTTP_RANGE']) && $range = stristr(trim($_SERVER['HTTP_RANGE']), 'bytes=')) {
$range = substr($range, 6);
$ranges = explode(',', $range);
$t = count($ranges);
}
header("Accept-Ranges: bytes");
header("Content-Type: application/octet-stream");
header("Content-Transfer-Encoding: binary");
header(sprintf('Content-Disposition: attachment; filename="%s"', $this->name));
if ($t > 0) {
header("HTTP/1.1 206 Partial content");
$t === 1 ? $this->pushSingle($range) : $this->pushMulti($ranges);
} else {
header("Content-Length: " . $this->size);
$this->readFile();
}
flush();
}
private function pushSingle($range) {
$start = $end = 0;
$this->getRange($range, $start, $end);
header("Content-Length: " . ($end - $start + 1));
header(sprintf("Content-Range: bytes %d-%d/%d", $start, $end, $this->size));
fseek($this->file, $start);
$this->readBuffer($end - $start + 1);
$this->readFile();
}
private function pushMulti($ranges) {
$length = $start = $end = 0;
$output = "";
$tl = "Content-type: application/octet-stream\r\n";
$formatRange = "Content-range: bytes %d-%d/%d\r\n\r\n";
foreach ( $ranges as $range ) {
$this->getRange($range, $start, $end);
$length += strlen("\r\n--$this->boundary\r\n");
$length += strlen($tl);
$length += strlen(sprintf($formatRange, $start, $end, $this->size));
$length += $end - $start + 1;
}
$length += strlen("\r\n--$this->boundary--\r\n");
header("Content-Length: $length");
header("Content-Type: multipart/x-byteranges; boundary=$this->boundary");
foreach ( $ranges as $range ) {
$this->getRange($range, $start, $end);
echo "\r\n--$this->boundary\r\n";
echo $tl;
echo sprintf($formatRange, $start, $end, $this->size);
fseek($this->file, $start);
$this->readBuffer($end - $start + 1);
}
echo "\r\n--$this->boundary--\r\n";
}
private function getRange($range, &$start, &$end) {
list($start, $end) = explode('-', $range);
$fileSize = $this->size;
if ($start == '') {
$tmp = $end;
$end = $fileSize - 1;
$start = $fileSize - $tmp;
if ($start < 0)
$start = 0;
} else {
if ($end == '' || $end > $fileSize - 1)
$end = $fileSize - 1;
}
if ($start > $end) {
header("Status: 416 Requested range not satisfiable");
header("Content-Range: */" . $fileSize);
exit();
}
return array(
$start,
$end
);
}
private function readFile() {
while ( ! feof($this->file) ) {
echo fgets($this->file);
flush();
usleep($this->delay);
}
}
private function readBuffer($bytes, $size = 1024) {
$bytesLeft = $bytes;
while ( $bytesLeft > 0 && ! feof($this->file) ) {
$bytesLeft > $size ? $bytesRead = $size : $bytesRead = $bytesLeft;
$bytesLeft -= $bytesRead;
echo fread($this->file, $bytesRead);
flush();
usleep($this->delay);
}
}
}
File Used
If you're using PHP to serve the file, you have to implement all resuming logic yourself.
You'll have to send Accept-Ranges and respond appropriately to Ranges.
That's a chunk of work. It might be easier to use mod_proxy.
What's the purpose of this? hiding urls only or just allowing members to download?
The way you described it, it's a bit tricky ...
The remote server your script will download from should support resuming downloads.
Your php script should check for 'Accept-Range' header & pass it through to the remote server (using sockets is your best option I guess) so your script is actually acting as a proxy.

Categories