This question already has answers here:
Replace substrings with an incremented counter value
(5 answers)
Closed 2 years ago.
I have a text file (data.txt) with the following lines
# one
# two
a-two
b-two
c-two
# three
a-three
b-three
# four
I would like to replace # with incremental number per line so to be like this
1 one
2 two
a-two
b-two
c-two
3 three
a-three
b-three
4 four
I have try to do this
<?php
$path_to_file = 'data.txt';
$what_to_replace = '#';
$i = 0; // initial count
$file_contents = file_get_contents($path_to_file);
$file_contents = str_replace($what_to_replace, $i++,$file_contents);
file_put_contents($path_to_file,$file_contents);
?>
it did changed in all lines with # but not incrementally
Here's a simple way via preg_replace_callback():
$i = 1;
$file_contents = preg_replace_callback('/^#/m', function($match) use (&$i) {
return $i++;
}, $file_contents);
The m (multiline) flag is important - it will force our pattern to match # at the start of any line, not just the first.
I think you have to iterate over all the lines and replace the hash one by one. Of course, you can use some preg_replace as well to only replace the # at the line beginning: https://www.php.net/manual/en/function.preg-replace.php
$result = '';
$handle = fopen("data.txt", "r");
$i = 1;
if ($handle) {
while (($line = fgets($handle)) !== false) {
$content .= str_replace('#', $i++,$line) . "\n";
}
fclose($handle);
} else {
// error opening the file.
}
file_put_contents('data.txt', $result);
$path_to_file = 'data.txt';
$what_to_replace = '#';
$i = 1; // initial count
$file_contents = file_get_contents($path_to_file);
$lines = explode(PHP_EOL, $file_contents);
$new_lines = [];
foreach ($lines as $line) {
if (strpos($line, '#') !== false) {
$new_lines[] = str_replace('#', $i, $line);
$i++;
} else {
$new_lines[] = $line;
}
}
file_put_contents($path_to_file, implode(PHP_EOL, $new_lines));
We can split the text line-by-line, then process each line individually, looking for a # at the start of the line & modifying the output in those cases.
The preg_replace_callback approach is better than this one, IMO.
$text = file_get_contents(...);
$lines = explode("\n", $text); // split by unix-style line break
$out = []; // For storing the modified lines
$i=0;
foreach ($lines as $line){
if (substr($line,0,1)=='#'){
//replace the first character & increment the counter if line starts with #
$i++;
$line = $i.substr($line,1);
}
//append the SOMETIMES modified line to the output
$out[] = $line;
}
// convert array of lines to string
$fixedText = implode("\n", $out);
See https://stackoverflow.com/a/28725803/802469 for a more robust version of the explode("\n", $text) line.
Personally, I like the $out = []; ...; implode(...) approach. I find it easier to work with & to visualize in my mind. Using an $out = ""; $out .= $line."\n"; approach would work just as well. I suspect any performance increase from going with string would be negligible, but might lower your carbon footprint a small amount.
If you have serious performance/resource concerns (extremely large files) then the fopen()/while fgets() approach would be better, and fopen/fwrite() could be used to append each output line to a file without using up memory for an output string.
Related
I get trouble on replacing the word into special characters
First, I read the txt file and store each line into $line
and put the special character that I want to change into $table array.
How do I change $line with special character $table array one by one based on the position for example, the txt include three words:
pads
password
qwerty
so the program should show
p#ds
p#d$
p#ssword
p#$sword
p#$$word
p#$$w0rd
qwerty
Now my work just change all special characters into a new word.
but how to change it using foreach / for loop one by one based on the position
My code as follows
<?php
$file = fopen("text.txt", "r");
while(!feof($file)) {
$line = fgets($file);
$line = rtrim ($line);
$table = array(
'a'=>'#', 'o'=>'0', 's'=>'$',
);
$length = strlen($line);
for ($i=0 ; $i<$length ; $i++){
$line = strtr ($line, $table);
echo $line."<br>";
};
}
fclose($file);
?>
This should do the job (haven't tested it):
$char_array = str_split($line);
$replaced = FALSE;
foreach($char_array as $char) {
if(array_key_exists($char, $table)) {
$line = str_replace($char, $table[$char], $line, 1);
echo $line."<br>";
$replaced = TRUE;
}
}
if(!$replaced)
echo $line."<br>";
By setting the count argument of str_replace to 1, you make sure only the current character is replaced and not all of them.
Instead of strtr(), use preg_replace() like this:
for ($i=0 ; $i<$length ; $i++){
if (array_key_exists($line[$i], $table)) {
$line = preg_replace('/' . $line[$i] . '/', $table[$line[$i]], $line, 1);
echo $line."<br>";
}
};
I want to read the text file line by line using php while copying only these values 1.23 , 0.5 , -1.8903, 186 , -0095
I will use strtok() to extract the part of each line I want.
I need help writing the code to iterate through each line.
While you read the file you can explode the file into an array of lines, then split by space and get the first element, like :
$text = "00123123 asdjasdjjdaswd
-32423 asdasda sdsagfgasdf
42 adklsdfkasdfsdf";
$lines = explode(PHP_EOL, $text); # EOL = End of line
foreach($lines as $line){
$array = explode(" ", $line);
echo $value = $array[0];
echo "\n";
}
/* Outputs:
00123123
-32423
42
*/
Alternatively you could write a regex that will get you the contents before the first whitespace per line.
I have used given code:-
$file="test.txt";
$linecount = 0;
$handle = fopen($file, "r");
while(!feof($handle)){
$line = fgets($handle);
$array = explode(" ", $line);
echo $value = $array[0];
echo "\n";
$linecount++;
}
fclose($handle);
I have a text file and in it i want to only read the lines that start with an r then a space.
I have tried strpos but it does not seem to work.
$r="r ";
$file = file_get_contents('cached-consensus', true);
$n = explode("\n", $file);
foreach($n as $line){
$pos= strpos($line,$r);
echo $pos;}
Use this:
$file = file_get_contents('cached-consensus', true);
$n = explode("\n", $file);
foreach($n as $line){
if(0 === strpos($line, "r ")){
// do whatever you want with the line
}
}
This checks that $line starts with 'r '. Currently you're just echoing the position of the string (this should be 0).
How to count specific lines in a text file depending on a particular variable in that line.
For example i need to count the lines of a text file only containing for instance $item1 or $item2 etc.
Sounds like you need something like what grep -c do in the shell, try something like this:
$item1 = 'match me';
$item2 = 'match me too';
// Thanks to #Baba for the suggestion:
$match_count = count(
preg_grep(
'/'.preg_quote($item1).'|'.preg_quote($item2).'/i',
file('somefile_input.txt', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES)
)
);
// does the same without creating a second array with the matches
$match_count = array_reduce(
file('somefile_input.txt', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES),
function($match_count, $line) use ($item1, $item2) {
return
preg_match('/'.preg_quote($item1).'|'.preg_quote($item2).'/i', $line) ?
$match_count + 1 : $match_count;
}
);
The above code sample uses the file() function to read the file into an array (splitted by lines), array_reduce() to iterate that array and preg_match() inside the iteration to see if a line matched (the /i at the end makes it case-insensitive).
You could use a foreach as well too.
This code reads file.php and counts only lines containing '$item1' or '$item2'. The check itself could be finetuned, since you have to add a new stristr() for every word you want to check.
<?php
$file = 'file.php';
$fp = fopen($file, 'r');
$size = filesize($file);
$content = fread($fp, $size);
$lines = preg_split('/\n/', $content);
$count = 0;
foreach($lines as $line) {
if(stristr($line, '$item1') || stristr($line, '$item2')) {
$count++;
}
}
echo $count;
Read your file line by line and use strpos to determine if a line contains a specific string/item.
$handle = fopen ("filename", "r");
$counter = 0;
while (!feof($handle))
{
$line = fgets($handle);
// or $item2, $item3, etc.
$pos = strpos($line, $item);
if ($pos !== false)
{
$counter++
}
}
fclose ($handle);
I am curious if you have a string how would you detect the delimiter?
We know php can split a string up with explode() which requires a delimiter parameter.
But what about a method to detect the delimiter before sending it to explode function?
Right now I am just outputting the string to the user and they enter the delimiter. That's fine -- but I am looking for the application to pattern recognize for me.
Should I look to regular expressions for this type of pattern recognition in a string?
EDIT: I have failed to initially specify that there is a likely expected set of delimiters. Any delimiter that is probably used in a CSV. So technically anyone could use any character to delimit a CSV file but it is more probable to use one of the following characters: comma, semicolon, vertical bar and a space.
EDIT 2: Here is the workable solution I came up with for a "determined delimiter".
$get_images = "86236058.jpg 86236134.jpg 86236134.jpg";
//Detection of delimiter of image filenames.
$probable_delimiters = array(",", " ", "|", ";");
$delimiter_count_array = array();
foreach ($probable_delimiters as $probable_delimiter) {
$probable_delimiter_count = substr_count($get_images, $probable_delimiter);
$delimiter_count_array[$probable_delimiter] = $probable_delimiter_count;
}
$max_value = max($delimiter_count_array);
$determined_delimiter_array = array_keys($delimiter_count_array, max($delimiter_count_array));
while( $element = each( $determined_delimiter_array ) ){
$determined_delimiter_count = $element['key'];
$determined_delimiter = $element['value'];
}
$images = explode("{$determined_delimiter}", $get_images);
Determine which delimiters you consider probable (like ,, ; and |) and for each search how often they occur in the string (substr_count). Then choose the one with most occurrences as the delimiter and explode.
Even though that might not be fail-safe it should work in most cases ;)
I would say this works 99.99% of the cases :)
The basic idea is, that number of valid delimiters should be the same line by line.
This script calculates delimiter count discrepancies between all lines.
Less discrepancy means more likely valid delimiter.
Putting it all together this function read rows and return it back as an array:
function readCSV($fileName)
{
//detect these delimeters
$delA = array(";", ",", "|", "\t");
$linesA = array();
$resultA = array();
$maxLines = 20; //maximum lines to parse for detection, this can be higher for more precision
$lines = count(file($fileName));
if ($lines < $maxLines) {//if lines are less than the given maximum
$maxLines = $lines;
}
//load lines
foreach ($delA as $key => $del) {
$rowNum = 0;
if (($handle = fopen($fileName, "r")) !== false) {
$linesA[$key] = array();
while ((($data = fgetcsv($handle, 1000, $del)) !== false) && ($rowNum < $maxLines)) {
$linesA[$key][] = count($data);
$rowNum++;
}
fclose($handle);
}
}
//count rows delimiter number discrepancy from each other
foreach ($delA as $key => $del) {
echo 'try for key=' . $key . ' delimeter=' . $del;
$discr = 0;
foreach ($linesA[$key] as $actNum) {
if ($actNum == 1) {
$resultA[$key] = 65535; //there is only one column with this delimeter in this line, so this is not our delimiter, set this discrepancy to high
break;
}
foreach ($linesA[$key] as $actNum2) {
$discr += abs($actNum - $actNum2);
}
//if its the real delimeter this result should the nearest to 0
//because in the ideal (errorless) case all lines have same column number
$resultA[$key] = $discr;
}
}
var_dump($resultA);
//select the discrepancy nearest to 0, this would be our delimiter
$delRes = 65535;
foreach ($resultA as $key => $res) {
if ($res < $delRes) {
$delRes = $res;
$delKey = $key;
}
}
$delimeter = $delA[$delKey];
echo '$delimeter=' . $delimeter;
//get rows
$row = 0;
$rowsA = array();
if (($handle = fopen($fileName, "r")) !== false) {
while (($data = fgetcsv($handle, 1000, $delimeter)) !== false) {
$rowsA[$row] = Array();
$num = count($data);
for ($c = 0; $c < $num; $c++) {
$rowsA[$row][] = trim($data[$c]);
}
$row++;
}
fclose($handle);
}
return $rowsA;
}
I have the same problem, I am dealing with a lot of CSV's from various databases, which various people extract to CSV in various ways, sometimes different each time for the same dataset ... Have simply implemented a function like this in my convert base class
protected function detectDelimiter() {
$handle = #fopen($this->CSVFile, "r");
if ($handle) {
$line=fgets($handle, 4096);
fclose($handle);
$test=explode(',', $line);
if (count($test)>1) return ',';
$test=explode(';', $line);
if (count($test)>1) return ';';
//.. and so on
}
//return default delimiter
return $this->delimiter;
}
I made something like this:
$line = fgetcsv($handle, 1000, "|");
if (isset($line[1]))
{
echo "delimiter is: |";
$delimiter="|";
}
else
{
$line1 = fgetcsv($handle, 1000, ";");
if (isset($line1[1]))
{
echo "delimiter is: ;";
$delimiter=";";
}
else
{
echo "delimiter is: ,";
$delimiter=",";
}
}
This simply checks whether there is a second column after a line is read.
I am having the same issue. My system will recieve CSV files from the client but it could use ";", "," or " " as delimiter and I wnat to improve the system so the client dont have to know which is (They never do).
I search and found this library:
https://github.com/parsecsv/parsecsv-for-php
Very good and easy to use.