I am trying to have PHP read a file to the end, which works perfectly fine, except when the file is empty. Then it throws an error saying:
Uncaught ValueError: fread(): Argument #2 ($length) must be greater than 0`
`strlen()`does not work in this case.
Is there some argument for the length like "read all that is there and if it is empty, just ignore it"? Here is the PHP:
$data_file_read = fopen("data.txt","r");
$alldata = fread($data_file_read,filesize("data.txt"));
fclose($data_file_read);
Get the length of the file into a variable and check that for your length
$filename = "data.txt";
//
// Check file exists first
//
if (file_exists($filename) === true) {
//
// Get file length and check if it is > 0
//
$dataLength = filesize($filename);
if ($dataLength > 0) {
//
// Finally read the data
//
$data_file_read = fopen($filename, "r");
$alldata = fread($data_file_read, $dataLength);
fclose($data_file_read);
}
}
Related
function numOfLines () {
$file = fopen("data/text.txt", "r");
$count = 0;
while (!feof($file)) {
$line = fgets($file);
$count++;
}
return $count;
}
print_r(numOfLines()); = output 13 (for example)
Above code works perfectly fine.
However, if I declare $FILE variable outside the function and pass it as parameter of the function, then it gives this error "feof(): supplied resource is not a valid stream resource "
$FILE = fopen("data/text.txt", "r");
function numOfLines ($file) {
$count = 0;
while (!feof($file)) {
$line = fgets($file, 1024);
$count++;
}
return $count;
}
print_r(numOfLines($FILE)); = feof(): supplied resource is not a valid stream resource
Could anyone explain it a little bit what's actually going on here?
You're declaring $FILE as the file handle, but passing the undefined $file as an argument to the function. PHP is case sensitive.
Using PHP 7.3, I'm trying to achieve "tail -f" functionality: open a file, waiting for some other process to write to it, then read those new lines.
Unfortunately, it seems that fgets() caches the EOF condition. Even when there's new data available (filemtime changes), fgets() returns a blank line.
The important part: I cannot simply close, reopen, then seek, because the file size is tens of gigs in size, well above the 32 bit limit. The file must stay open in order to be able to read new data from the correct position.
I've attached some code to demonstrate the problem. If you append data to the input file, filemtime() detects the change, but fgets() reads nothing new.
fread() does seem to work, picking up the new data but I'd rather not have to come up with a roll-your-own "read a line" solution.
Does anyone know how I might be able to poke fgets() into realising that it's not the EOF?
$fn = $argv[1];
$fp = fopen($fn, "r");
fseek($fp, -1000, SEEK_END);
$filemtime = 0;
while (1) {
if (feof($fp)) {
echo "got EOF\n";
sleep(1);
clearstatcache();
$tmp = filemtime($fn);
if ($tmp != $filemtime) {
echo "time $filemtime -> $tmp\n";
$filemtime = $tmp;
}
}
$l = trim(fgets($fp, 8192));
echo "l=$l\n";
}
Update: I tried excluding the call to feof (thinking that may be where the state becomes cached) but the behaviour doesn't change; once fgets reaches the original file pointer position, any further fgets reads will return false, even if more data is subsequently appended.
Update 2: I ended up rolling my own function that will continue returning new data after the first EOF is reached (in fact, it has no concept of EOF, just data available / data not available). Code not heavily tested, so use at your own risk. Hope this helps someone else.
*** NOTE this code was updated 20th June 2021 to fix an off-by-one error. The comment "includes line separator" was incorrect up to this point.
define('FGETS_TAIL_CHUNK_SIZE', 4096);
define('FGETS_TAIL_SANITY', 65536);
define('FGETS_TAIL_LINE_SEPARATOR', 10);
function fgets_tail($fp) {
// Get complete line from open file which may have additional data written to it.
// Returns string (including line separator) or FALSE if there is no line available (buffer does not have complete line, or is empty because of EOF)
global $fgets_tail_buf;
if (!isset($fgets_tail_buf)) $fgets_tail_buf = "";
if (strlen($fgets_tail_buf) < FGETS_TAIL_CHUNK_SIZE) { // buffer not full, attempt to append data to it
$t = fread($fp, FGETS_TAIL_CHUNK_SIZE);
if ($t != false) $fgets_tail_buf .= $t;
}
$ptr = strpos($fgets_tail_buf, chr(FGETS_TAIL_LINE_SEPARATOR));
if ($ptr !== false) {
$rv = substr($fgets_tail_buf, 0, $ptr + 1); // includes line separator
$fgets_tail_buf = substr($fgets_tail_buf, $ptr + 1); // may reduce buffer to empty
return($rv);
} else {
if (strlen($fgets_tail_buf) < FGETS_TAIL_SANITY) { // line separator not found, try to append some more data
$t = fread($fp, FGETS_TAIL_CHUNK_SIZE);
if ($t != false) $fgets_tail_buf .= $t;
}
}
return(false);
}
The author found the solution himself how to create PHP tail viewer for gians log files 4+ Gb in size.
To mark this question as replied, I summary the solution:
define('FGETS_TAIL_CHUNK_SIZE', 4096);
define('FGETS_TAIL_SANITY', 65536);
define('FGETS_TAIL_LINE_SEPARATOR', 10);
function fgets_tail($fp) {
// Get complete line from open file which may have additional data written to it.
// Returns string (including line separator) or FALSE if there is no line available (buffer does not have complete line, or is empty because of EOF)
global $fgets_tail_buf;
if (!isset($fgets_tail_buf)) $fgets_tail_buf = "";
if (strlen($fgets_tail_buf) < FGETS_TAIL_CHUNK_SIZE) { // buffer not full, attempt to append data to it
$t = fread($fp, FGETS_TAIL_CHUNK_SIZE);
if ($t != false) $fgets_tail_buf .= $t;
}
$ptr = strpos($fgets_tail_buf, chr(FGETS_TAIL_LINE_SEPARATOR));
if ($ptr !== false) {
$rv = substr($fgets_tail_buf, 0, $ptr + 1); // includes line separator
$fgets_tail_buf = substr($fgets_tail_buf, $ptr + 1); // may reduce buffer to empty
return($rv);
} else {
if (strlen($fgets_tail_buf) < FGETS_TAIL_SANITY) { // line separator not found, try to append some more data
$t = fread($fp, FGETS_TAIL_CHUNK_SIZE);
if ($t != false) $fgets_tail_buf .= $t;
}
}
return(false);
}
This is a second request on the same subject. I wasn't clear
I needed the line to be deleted.
I searched here and found part of a script that is suppose search for
a word and delete the line. There seems to be a slight error with what
I'm trying to do.
I have an option list in a pull down. I would like for it to
remove the line selected. The file choice.php that is called
from the pull down page seems to be released when the php below
is called called because there is no access denied, or violation
errors.
These are the errors I'm getting after adding the 3 last lines I
was told I need.
fopen() expects at least 2 parameters, 1 given
implode(): Invalid arguments passed
fwrite() expects parameter 1 to be resource, boolean given
fclose() expects parameter 1 to be resource, boolean given
Thanks in advance
<?php
// Separate choice.php has the following pull down
// Select item to delete from list
// <option value="item1.php">Item 1</option>
// <option value="item2.php">Item 2</option>
// ...... many items.
$workitem = $_POST["itemtodelete"];
$file = file("option.list.php");
foreach( $file as $key=>$line ) {
if( false !== strpos($line, $workitem) ) {
unset ($file[$key]);
}
}
// Removed "\n"
$file = implode("", $file);
// Told to add this.
$fp = fopen ("option.list.php");
fwrite($fp,implode("",$file);
fclose ($fp);
?>
fopen requires a $mode as the second parameter, so that fails and everything that needs $fp.
Just use file_put_contents. It will even implode the array for you:
$workitem = $_POST["itemtodelete"];
$file = file("option.list.php");
foreach( $file as $key=>$line ) {
if( false !== strpos($line, $workitem) ) {
unset ($file[$key]);
}
}
file_put_contents('option.list.php', $file);
Ok. You are missing some closing parenthesis, as well as other things.
$replaceItem = $_POST['itemtodelete']; // You should filter this data
$newContents = "";
$path = PATH_TO_FILE; // This could be hard coded, but not recommended
$filename = "option.list.php";
// Check to see if the file exists
if ( file_exists($path."/".$filename) ) {
// Wrap our IO stuff so we catch any exceptions
try {
// Open the file for reading
$fp = fopen($path."/".$filename, "r");
if ($fp) {
// Loop line-by-line through the file
while($line = fgets($fp, 4096) !== false) {
// Only add the line if it doesn't contain $replaceItem
// This is case insensitive. I.E. 'item' == 'ITEM'
// For case sensitive, use strstr()
if ( stristr($line, $replaceItem) == false ) {
$newContents .= $line;
}
}
}
// Close our file
fclose($fp);
// Replace the contents of the file with the new contents
file_put_contents($path."/".$filename, $newContents);
} catch (Exception $e) {
throw new Exception($e->getMessage());
}
}
Edit: Try this. I modified it somewhat.
Consider this code:
$a = fopen('test.txt', 'a');
$b = fread($a, filesize('test.txt'));
I'd expect $b to be false, since it's obviously trying to read from a file which is only open for writing. However, $b is in that case an empty string. How do I distinguish between a bad attempt and a legit empty file? Same applies to fwrite() btw.
According to the definition of fread() it will almost always return a string result value except in these two cases:
The first argument is not a valid resource,
A length of zero or less is specified as the number of bytes to read.
In your case, you could detect a bad attempt with code like this:
if (($data = fread($f, 512) === '' && !feof($f)) {
// nothing read but not at EOF either
// that's fishy
}
In case of fwrite() you can test how many bytes were written (it's the return value); if less bytes were written, something is wrong.
$content = false;
$filename = 'test.txt';
if( $handle = fopen($filename, 'a') )
{
$meta = stream_get_meta_data($handle);
// Check if mode readable
if( $meta['mode'] == 'r')
$content = fread($handle, filesize($filename));
fclose($handle);
}
if( $content == false )
echo 'bad attempt!';
I tested and output is:
bad attempt!
So I have a PHP program that a line from a text file. Then it uses that line of text it read to point to another text file
$posts = "posts/posts.txt";
$postsLines = file($posts);
$fetchingPost = TRUE;
$postNumber = 0;
$postPointer;
$postPointerString;
$postLines;
$postTag;
$postTitle;
$postContent;
$endCondition = "end";
while ($fetchingPost == TRUE) {
$endOfFile = strcmp($postsLines[$postNumber], $endCondition);
if ($endOfFile == 0) {
$fetchingPost = FALSE;
}
if ($endOfFile <> 0) {
$postPointer[$postNumber] = $postsLines[$postNumber];
$postLines = file($postPointer[$postNumber]);
$postNumber = $postNumber + 1;
}
}
And I get this errors when I run it, I am using the WAMP server
Warning: file(posts/leapMotionSandbox.txt ): failed to open stream: Invalid argument in C:\wamp\www\noahhuppert\Paralax v2\index.php on line 45
Warning: file(posts/topDownShooter.txt ): failed to open stream: Invalid argument in C:\wamp\www\noahhuppert\Paralax v2\index.php on line 45
Please help
The elements of the array returned by file() have a newline at the end of each line. This is not a valid filename character on Windows (it's valid on Unix, although it would be perverse to include newlines in a filename).
From the documentation:
Each line in the resulting array will include the line ending, unless FILE_IGNORE_NEW_LINES is used, so you still need to use rtrim() if you do not want the line ending present.
Your loop can also be simplified greatly. There's no need for the $fetchingPost or $endOfFile variables, just test for the end in the while() condition.
while (($line = rtrim($postsLines[$postNumber]) != $endCondition) {
$postPointer[$postNumber] = $line;
$postLines = file($line);
$postNumber++;
}
Alternatively, you can do:
$postsLines = file($posts, FILE_IGNORE_NEW_LINES);