I have a text file full of proxy servers. Some are commented with a # at the beginning and should not be used. I have tried to do this using the code below, but it is still picking commented lines. Where am I going wrong?
function getProxy()
{
$file = file('proxy.txt',FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
$line = $file[array_rand($file)];
if (strpos($line,'#') !== FALSE) { getProxy(); }
return $line;
}
Ideally I think the code should probably do some form of while loop until it picks a proxy (at random) which does not have a # at the beginning - rather than calling the function each time and reloading the file.
Help!
Actually, the best way to handle the problem is to remove any line that is commented, and only then pick a random result.
Otherwise you might have a very bad luck streak or a file with thousands of commented lines and one or two active, and will wait for hours to have the randomness pick the one line that is valid.
function getProxy()
{
$file = file('proxy.txt', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($file as $key => $line) {
if (strpos(ltrim($line), "#") === 0) {
unset($file[$key]);
}
}
$line = $file[array_rand($file)];
return $line;
}
You can use array_filter() instead, like this:
$file = array_filter($file, function($value){return !(strpos(ltrim($value), "#") === 0);});
true - loop is better here:
function getProxy()
{
$file = file('proxy.txt',FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
do {
$line = $file[array_rand($file)];
} while(strpos($line,'#') !== FALSE);
return $line;
}
you must also add some additional condition - if file have only commented lines it will now create infinite loop, so better idea would be:
function getProxy()
{
$maxChecks=100;
$i=0;
$file = file('proxy.txt',FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
do {
$line = $file[array_rand($file)];
if($i++>$maxChecks) break;
} while(strpos($line,'#') !== FALSE);
if($i>$maxChecks)
return false;
return $line;
}
Related
This might be an easy one but not for me.
I have this function:
function pingAddress($ip) {
// Read from file
$lines = file('F:\share\result.txt');
foreach($lines as $line)
{
// Check if the line contains the string we're looking for, and print if it does
if(strpos($line, $ip) !== false)
echo $line;
}
}
The text file looks like this:
192.168.50.104 UP
192.168.50.105 UP
192.168.50.106 DOWN
192.168.50.107 UP
If I give ip 192.168.50.1 for example it returns all the lines from 192.168.50.1 to 192.168.50.199 and I need it to return only that specific line and if possible only what's after ip: UP or DOWN.
Help would be nice :-)
By using strpos(), you are accepting partial matches, which causes 192.168.50.1 to match all your examples.
Instead, split the line on a space, and check for a full IP match. Then you can return the UP or DOWN part.
function pingAddress($ip) {
// Read from file
$lines = file('F:\share\result.txt');
foreach($lines as $line){
$parts = explode(' ', $line);
if($parts[0] == $ip){
return $parts[1];
}
}
}
echo pingAddress('192.168.50.1'); // nothing returned
echo pingAddress('192.168.50.105'); // UP
Just add a "return;" after "echo $line;" The function stops, after the searched string is found.
function pingAddress($ip) {
// Read from file
$lines = file('F:\share\result.txt');
foreach($lines as $line)
{
// Check if the line contains the string we're looking for, and print if it does
if(strpos($line, $ip) !== false)
echo $line;
return;
}
}
You would need to check that there is a space after the IP address to ensure it is the full address and not part of another IP.
I've also changed this so that it reads the file a line at a time, so if it finds the IP address it stops reading there (and returns the UP or DOWN). This saves reading the entire file before searching...
function pingAddress($ip) {
// Add space for search
$ip .= ' ';
$fp = fopen("a.csv", "r");
while ( $line = fgets($fp) )
{
// Check if the line contains the string we're looking for, and print if it does
if(strpos($line, $ip) !== false)
return trim(substr($line, strlen($ip)));
}
fclose($fp);
return '';
}
This seems like it should be a simple thing to do but I'm having a bit of trouble with fgetc() when returning the last line of a open file handle. what I'm trying to do is return the last line written to the handle, I have the following which works if the handle has only one line:
function getLastLineFromHandle($handle)
{
$seeker = function($handle) use (&$seeker) {
fseek($handle, -1, SEEK_CUR);
return ftell($handle) ?
$seeker($handle) :
$handle;
};
return trim(fgets($seeker($handle)));
}
$handle = fopen('php://temp', 'w+');
fwrite($handle, 'Hello World'.PHP_EOL);
//prints Hello World
print getLastLineFromHandle($handle);
The problem is when I have multiple lines written to the handle, adding an fgetc() to the check condition doesn't seems to work for example:
function getLastLineFromHandle($handle)
{
$seeker = function($handle) use (&$seeker) {
fseek($handle, -1, SEEK_CUR);
return ftell($handle) && fgetc($handle) != PHP_EOL ?
$seeker($handle) :
$handle;
};
return trim(fgets($seeker($handle)));
}
This returns blank if multiple lines are written to the handle and fgetc($handle) seems to return the same character each time?
I'm sure there is something very simple that I've missed, but any pointers would be great as this is driving me crazy!
Thanks.
Found what was missing from example above, turns out it was case of there being an unexpected end of line char at the pointer at start so moving one position in solves the issue, for example:
function getLastLineFromHandle($handle)
{
$seeker = function($handle) use (&$seeker) {
fseek($handle, -2, SEEK_CUR);
return ftell($handle) && fgetc($handle) != PHP_EOL ?
$seeker($handle) :
$handle;
};
return trim(fgets($seeker($handle)));
}
While exploring this I also found another way of doing the same thing, thanks to #TheMarlboroMan's comment about seeking to the end which sparked this:
function getLastLineFromHandle($handle)
{
$seeker = function($handle, $cur = -2, $line = '') use (&$seeker)
{
$char = '';
if (fseek($handle, $cur, SEEK_END) != -1) {
$char = fgetc($handle);
$line = $char.$line;
}
return ftell($handle) > 0 && $char != PHP_EOL?
$seeker($handle, $cur-1,$line) :
$line;
};
return trim($seeker($handle));
}
This hasn't been through a refactor loop, however it passes the same tests as the other method above. This solution seems to be a bit messy to return the line string instead of the file handle as that is what you would expect back.
Setting this as solved but open for comments if anyone has a different way :)
I have a text file that stores lastname, first name, address, state, etc as a string with a | delimiter and each record on a separate line.
I have the part where I need to store each record on a new line and its working fine; however, now I need to be able to go back and update the name or address on a particular line and I can't get it to work.
This how to replace a particular line in a text file using php? helped me here but I am not quite there yet. This overwrites the whole file and I lose the records. Any help is appreciated!
After some edit seems to be working now. I am debugging to see if any errors.
$string= implode('|',$contact);
$reading = fopen('contacts.txt', 'r');
$writing = fopen('contacts.tmp', 'w');
$replaced = false;
while (!feof($reading)) {
$line = fgets($reading);
if(stripos($line, $lname) !== FALSE) {
if(stripos($line, $fname) !== FALSE) {
$line = "$string";
$replaced = true;
}
}
fwrite($writing, "$line");
//fputs($writing, $line);
}
fclose($reading); fclose($writing);
// might as well not overwrite the file if we didn't replace anything
if ($replaced)
{
rename('contacts.tmp', 'contacts.txt');
} else {
unlink('contacts.tmp');
}
It seems that you have a file in csv-format. PHP can handle this with fgetcsv() http://php.net/manual/de/function.fgetcsv.php
if (($handle = fopen("contacts.txt", "r")) !== FALSE) {
$data = fgetcsv($handle, 1000, '|')
/* manipulate $data array here */
}
fclose($handle);
So you get an array that you can manipulate. After this you can save the file with fputcsv http://www.php.net/manual/de/function.fputcsv.php
$fp = fopen('contacts.tmp', 'w');
foreach ($data as $fields) {
fputcsv($fp, $fields);
}
fclose($fp);
Well, after the comment by Asad, there is another simple answer. Just open the file in Append-mode http://de3.php.net/manual/en/function.fopen.php :
$writing = fopen('contacts.tmp', 'a');
I am using fopen to reach my PHP file :
$readFd = #fopen($file, 'r+');
I would like to search this file for the function call parent::process();
And if this exists I would then insert a new function call after this.
I have tried using preg_replace but it does not seem to match parent::process();
For example the result I need is this.
public function process() {
parent::process();
$this->newFunction();
}
Then to write the to the file I am using :
fwrite($readFd, $content);
I guess I must be missing something important with regex.
Hopefully someone can point me in the right direction.
I would use the php function fgets to read every in the file one by one until you reach the line you need. And then your pointer will be after that line where you can write your own line.
EDIT
I was wrong, when you write something to a file at a specific point, everything after that point is lost. So I did a little testing and came up with this:
$handle = fopen($file,"r+");
$lines = array();
while(($line = fgets($handle)) !== false) {
$lines[] = $line;
if(strpos($line, 'parent::process()')) {
$lines[] = '$this->newFunction();';
}
}
fseek($handle, 0); // reset pointer
foreach($lines as $line) {
fwrite($handle, $line);
}
fclose($handle);
I hope this solves your problem.
I came up with the solution however your code seems much shorter so I will try your solution tomorrow.
if(! $readFd = #fopen($file, "r+"))
return FALSE;
$buffer = fread($readFd, 120000);
fclose($readFd);
$onDuplicate = FALSE;
$lines = explode("\n", $buffer);
foreach($lines AS $key => $line) {
if(strpos($line, "newFunction()")) {
$onDuplicate = TRUE;
}
if(strpos($line, "parent::process()")) {
$lines[$key] = "\t\tparent::process();\n\t\t//\$this->newFunction();\n";
}
}
if(! $onDuplicate) {
$readFd = fopen($file, "w");
$buffer = implode("\n", $lines)."\n";
fwrite($readFd, $buffer);
fclose($readFd);
} else {
var_dump('changes are already applied');
}
Thanks for all your help!
I have a txt file that has a change-log.I'm trying to display the new changes only for the current version.
I wrote a function to read the file and check every line if it has the wanted words, if it finds those words it starts to get the content and push it to an array.
I searched to see if there is an example but everyone was talking about how to stop at a specified line, not to start from one.
Here is the code I use:
public function load($theFile, $beginPosition, $doubleCheck) {
// Open file (read-only)
$file = fopen($_SERVER['DOCUMENT_ROOT'] . '/home/' . $theFile, 'r');
// Exit the function if the the file can't be opened
if (!$file) {
return;
}
$changes = Array();
// While not at the End Of File
while (!feof($file)) {
// Read current line only
$line = fgets($file);
// This will check if the current line has the word we look for to start loading
$findBeginning = strpos($line, $beginPosition);
// Double check for the beginning
$beginningCheck = strpos($line, $doubleCheck);
// Once you find the beginning
if ($findBeginning !== false && $beginningCheck !== false) {
// Start storing the data to an array
while (!feof($file)) {
$line = fgets($file);
// Remove space and the first 2 charecters ('-' + one space)
$line = trim(substr($line, 2));
if (!empty($line)) { // Don't add empty lines
array_push($changes, $line);
}
}
}
}
// Close the file to save resourses
fclose($file);
return $changes;
}
It's working currently, but as you can see it's nested loops and that's not good and in case the txt file grows it will take more time!
I'm trying to improve the performance, so does is there any better way to do this ?
much simpler than you think
$found = false;
$changes = array();
foreach(file($fileName) as $line)
if($found)
$changes[] = $line;
else
$found = strpos($line, $whatever) !== false;
That nested loop will not degrade performance, cause it's not really a nested loop in the sense that it is a combinatorially-growing loop over multiple variables. It isn't necessary to write it like that though. Here's another way that avoids it. Try this (pseudocode here):
// skim through the beginning of the file, break upon finding the start
// of the portion I care about.
while (!feof($file)) {
if $line matches beginning marker, break;
}
// now read and process until the endmarker (or eof...)
while (!feof($file)) {
if $line matches endmarker, break;
filter/process/store line here.
}
Also, doublechecking is absolutely not necessary. Why is that there?