How to remove duplicates from a PHP string separated by "X"? - php

I have a csv file that looks like this:
13:BOOT2700 X;27
13:BOOT2700 X;27
13:BOOT2700 X;27
13:BOOT2700 X;27
13:BOXER1136 X;11.36
13:BOXER1364 X;13.64
13:BOXER1591 X;15.91
13:BOXER909 X;9.09
...
I would like to remove the duplicates of data[0] and remove the spaces and the "X" at the end of the string. For the second part it works correctly but I can't delete the duplicates. I tried this code but they remain. It shows me each time the first values while they are identical.
In the end I would like this:
13:BOOT2700;27
13:BOXER1136;11.36
13:BOXER1364;13.64
13:BOXER1591;15.91
13:BOXER909;9.09
How can I do it?
Thanks for your help
<?php
$file = "BI6_20211214_0905_15000.txt";
if (($handle = fopen($file, "r")) !== false) {
while (($data = fgetcsv($handle, 9000000, ";")) !== false) {
$uniqueStr = implode('X', array_unique(explode('X', $data[0]))); //doesn't work
$clean_name = str_replace(' ', '', $data[0]);
$clean_name2 = str_replace('X', '', $clean_name);
echo $clean_name2; //13:BOOT2700
echo ("<br>");
}
}
fclose($handle);
echo "good !";
?>

Here's the entire code simplified and with comments that can help OP and others understand how you can process that.
I have 2 files:
input.txt
13:BOOT2700 X;27
13:BOOT2700 X;28
13:BOOT2700 X;29
13:BOOT2700 X;29
13:BOXER1136 X;11.36
13:BOXER1364 X;13.64
13:BOXER1591 X;15.91
13:BOXER909 X;9.09
When you run the code below, its result will be
===> Processing input.txt
Result:
13:BOOT2700;27
13:BOXER1136;11.36
13:BOXER1364;13.64
13:BOXER1591;15.91
13:BOXER909;9.09
input2.txt
13:BOOT111 X;27
13:BOOT2700 X;29
13:BOOT2700 X;29
13:BOXER1136 X;11.36
13:BOXER1364 X;13.64
13:BOXER1591 X;15.91
13:BOXER909 X;9.09
Its output will be
===> Processing input2.txt
Result:
13:BOOT111;27
13:BOOT2700;29
13:BOXER1136;11.36
13:BOXER1364;13.64
13:BOXER1591;15.91
13:BOXER909;9.09
Code
<?php
# Remove byte order mark (BOM)
function remove_utf8_bom($text) {
$bom = pack('H*','EFBBBF');
$text = preg_replace("/^$bom/", '', $text);
return $text;
}
# get list of all files
$dir = 'your-path/';
$allFiles = scandir($dir);
# process each file
foreach($allFiles as $file) {
if (in_array($file, array(".",".."))) {
continue;
}
if (strpos($file, 'BI6_') === false) {
continue;
}
echo "===> Processing $file\n";
$file = $dir.$file;
$filename = basename( $file );
# stores unique items like 13:BOOT2700, 13:BOXER1136 etc.
$processedItems = array();
# stores lines in the format we need
$finalResult = array();
$handle = fopen($file, 'r');
if ($handle === false) {
echo "Problem opening $file. Skipping.\n";
continue;
}
# read each line
while(!feof($handle)) {
$line = fgets($handle);
$line = remove_utf8_bom($line);
# skip empty lines
if (strlen(trim($line)) === 0) {
continue;
}
# split by X;, trim the first part
$lineSplit = explode('X;', $line);
$lineSplit[0] = trim($lineSplit[0]);
# check if the first part (such as 13:BOOT2700) is processed already
# if so, don't do anything else
if (in_array($lineSplit[0], $processedItems) === true) {
continue;
}
else {
# store the first part in processed items and create the newly
# formatted line; store that in final result
$processedItems[] = $lineSplit[0];
$finalResult[] = $lineSplit[0] . ';' . $lineSplit[1];
}
}
fclose($handle);
# show the final result
echo "Result:\n";
foreach ($finalResult as $x) {
echo $x;
}
}
echo "Done";
?>

The file is read into an array with file. With array_map and preg_replace the spaces and the X are removed from each line. array_unique then removes the duplicate entries.
$array = file('input.txt',FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
$array = array_map(function($v){return preg_replace('/ +X;/',';',$v);}, $array);
$array = array_unique($array);
The result is an array.
array (
0 => "13:BOOT2700;27",
4 => "13:BOXER1136;11.36",
5 => "13:BOXER1364;13.64",
6 => "13:BOXER1591;15.91",
7 => "13:BOXER909;9.09",
)
If a file is required as a result, the array can be converted into a string with implode and written to a file with file_put_contents.
$str = implode("\r\n",$array);
file_put_contents('input.csv', $str);

Related

Add text to specific line without removing the exist line in php

I'm trying to add only last name if first name is same
data.txt
Alice Sandy
Alice Nanami
James Watt
Alice Monica
Johann Gauss
to result.txt
Alice Sandy Nanami Monica
James Watt
Johann Gauss
I try with this code
$resultFile = "result.txt";
$search = "Alice";
$lineNumber = false;
if ($handle = fopen($result, "r")) {
$count = 0;
while (($line = fgets($handle, 4096)) !== FALSE and !$lineNumber) {
$count++;
$lineNumber = (strpos($line, $search) !== FALSE) ? $count : $lineNumber;
$isExist = (strpos($line, $search) !== FALSE) ? "yup" : "no";
}
fclose($handle);
}
if($isExist=="yup"){
$lines = file($resultFile);
$lines[$lineNumber] = $lines[$lineNumber].' '.$lastName;
file_put_contents($result, implode('', $lines));
}else{
$fullName = $firstName.' '.$lastName;
$fileOpen = fopen($result, "a");
fwrite($fileOpen,$fullName);
fclose($fileOpen);
$addBreaker = "\n";
$splResult = new SplFileObject($resultFile, 'a');
$splResult->fwrite($addBreaker);
}
But it give error offset (I'm using PHP 7) and the result is untidy
Alice Sandy Nanami
Monica
James Watt
Johan Gauss
Thanks for help
Another apporach instead of replacing lines would be save every line to an array and then iterate over array and save to the new file. You can also use the same file as an $outputFile.
$inputFile = 'names.txt';
$outputFile = 'result.txt';
$names = [];
if ($handle = fopen($inputFile, "r")) {
$count = 0;
while (($line = fgets($handle, 4096)) !== FALSE) {
$count++;
$lineNames = explode(' ', $line);
$names[$lineNames[0]][] = trim($lineNames[1]);
}
fclose($handle);
}
$handle = fopen($outputFile, 'w');
foreach ($names as $firstName => $lastNames) {
fwrite($handle, $firstName . ' ' . implode(' ', $lastNames) . PHP_EOL);
}
Two additional notes:
Don't use string as boolean value.
$isExist = (strpos($line, $search) !== FALSE) ? "yup" : "no";
Use just following condition. It's enough
$isExist = (strpos($line, $search) !== FALSE)
If you read lines from file you copy also new lines char, although you can't see them quite well in the output. You should trim all whitespace characters before inserting/replacing etc. to avoid old structure of file.
Use file() to collect the file contents as an array of lines. My snippet starts from there with $lines.
Iterate the array and makes each line modifiable by reference. (&)
Locate the first occurring needle match that not only exists in the line but matches the whole first word so that you don't get false-positive matching.
Then declare the first match as a reference variable (=&) and continue iterating the array. Any subsequent matches will have the delimiting space and second word appended to the reference variable. Then immediate unset the line to be purged from the document.
When done, re-implode the data and stuff the content into the result file.
This is clean, readable, and only needs one loop.
Code: (Demo)
// $lines = file('result.txt', FILE_IGNORE_NEW_LINES);
$lines = [
'Alice Sandy',
'Alice Nanami',
'James Watt',
'Alice Monica',
'Johann Gauss',
'Cooper Alice',
];
$needle = 'Alice';
foreach($lines as $index => &$line) {
if ($needle === strstr($line, ' ', true)) { // check whole first word
if (!isset($firstOccurrence)) {
$firstOccurrence =& $line;
} else {
$firstOccurrence .= strstr($line, ' ');
unset($lines[$index]);
}
}
}
var_export($lines);
// file_put_contents('result.txt', implode(PHP_EOL, $lines));
Output:
array (
0 => 'Alice Sandy Nanami Monica',
2 => 'James Watt',
4 => 'Johann Gauss',
5 => 'Cooper Alice',
)
P.s if you want to know if any rows were changed you could check if the original array is === the new array after looping, or you could just use a boolean flag variable in the else condition.

How to read specific lines from a text file in PHP

I have a txt file which has a lot of lines and the values in every line are separated with commas.
I want to read the 1st line alone which I did already using fgets :
$head = fgets(fopen($file, 'r'));
$headValues = explode(',', $head);
but now I want to read every other line from line 2 until the end of file and put those values into an array.
I searched for similar solutions but couldn't find any
Just use descriptor
$fd = fopen($file, 'r');
$head = fgets($fd);
$headValues = explode(',', $head);
$data = [];
while(($str = fgets($fd)) !== false) {
$otherValues = explode(',', $str);
$data[] = $otherValues;
}
This uses fgetcsv for the lines you care about and uses array_combine to put the headers and the line data together.
$fh = fopen($file, 'r');
$headValues = fgetcsv($fh);
$data = [];
while (true) {
if ( ($values = fgetcsv($fh)) === false ) {
break;
}
$data[] = array_combine($headValues, $values);
if ( fgets($fh) === false ) {
break;
}
}
fclose($fh);
print_r($data);
It checks at each read in case the EOF has been reached and then breaks out of the read loop.
You could use file(), array_map() and array_shift() :
$lines = file($file) ; // get file as array.
$lines = array_map(function($l){ return explode(',', $l); }, $lines);
$headValues = array_shift($lines); // get first values (removed from $lines)
So, $lines will contains all lines except the first one.

Remove a line from file if it exists

I'm getting used to PHP and trying to remove a line from a file (if it exists) and resave the file.
So if I had the file
user1
user2
user3
user4
I could use
if(existsAndRemove("user3")){
do thing
}
I've tried using code similar to the code below but it sometimes bugs out and will only remove a line if it is last in the file. I have no idea how to fix this.
$data2 = file("./ats.txt");
$out2 = array();
foreach($data2 as $line2) {
if(trim($line2) != $acc) {
$out2[] = $line2;
}
}
$fp2 = fopen("./ats.txt", "w+");
flock($fp2, LOCK_EX);
foreach($out2 as $line2) {
fwrite($fp2, $line2);
}
flock($fp2, LOCK_UN);
fclose($fp2);
}
}
Any help at all would be greatly appreciated, and i would also appreciate if you could explain the code too so I could easier learn from it!!
Thank you.
If the file size is small enough that you're not worried about reading it all into memory, you could do something more functional
// Read entire file in as array of strings
$data = file("./ats.txt");
// Some text we want to remove
$acc = 'user3';
// Filter out any lines that match $acc,
// ignoring any leading or trailing whitespace
//
$filtered_data = array_filter(
$data,
function ($line) use ($acc) {
return trim($line) !== $acc;
}
)
// If something changed, write the file back out
if ($filtered_data !== $data) {
file_put_contents('./ats.txt', implode('', $filtered_data));
}
Something like this might work:
function remove_user($user) {
$file_path = "foo.txt"
$users = preg_split("[\n\r]+", file_get_contents($file_path));
foreach ($users as $i => $existing) {
if ($user == $existing) {
$users = array_splice($users, $i, 1);
file_put_contents($file_path, implode("\n", $users));
break;
}
}
}
Should be much easier since you're already using file():
$data2 = file("./ats.txt", FILE_IGNORE_NEW_LINES);
unset($data2[array_search('user3', $data2)]);
file_put_contents("./ats.txt", implode("\n", $data2));
Or to check if it exists first:
$data2 = file("./ats.txt", FILE_IGNORE_NEW_LINES);
if( ($key = array_search('user3', $data2)) !== false ) {
unset($data2[$key]);
file_put_contents("./ats.txt", implode("\n", $data2));
}

How do I get only the second column of a CSV file using PHP?

I have the csv file(test.csv) and have the text as below:
1,maly,maly(),f,df
2,cheata,aaa,df,df
3,cheata,df,df,df
4,maly,fc,cfv,f
5,maly,df,fg,fg
6,chantha,fc,gf,fg
7,chantha,gh,a,g
8,David,fgfd,dfg,g
What I want:
I want to diplay only:maly cheata chantha David.For the name that have two or more the same,take only one.And I have the php code as below:
$c=0;
$data=fopen('test.csv','r');
while($row=fgets($data)){
if($c!=0){
echo $row[3]."<br>\n";
}
$c++;
}
The problem is
It does not display what I want. It displays
h h a a h h a
How do I fix this?
Use fgetcsv instead of fgets.
if (($handle = fopen("test.csv", "r")) !== FALSE) {
while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
if (isset($data[1])) {
echo $data[1] . "<br>\n";
}
}
fclose($handle);
}
It looks like you were intending to call fgetcsv instead of fgets. Also, the name index would be 1 then, instead of 3:
<?php
$file = fopen('test.csv', 'r');
while($row = fgetcsv($file)) {
if (isset($row[1])) {
echo $row[1], "\n";
}
}
fclose($file);
The file method returns all lines in an array. The explode method splits up a line by a particular delimiter.
$lines = file($file_name);
$fields = array();
foreach ($lines as $line) {
$cells = explode(',', $line);
$fields[] = $cells[1];
}
echo "<pre>";
print_r($fields);
echo "</pre>";
You're heading does not match your question, you want the second column, not row.
Firstly, your getting the 4th character of each row you need to do something like this (not tested):
$data=fopen('test.csv','r');
while($row=fgets($data)){
$cols = explode(',' $row);
echo $cols[1]."<br>\n";
}

PHP - How do I open files and read them then write new ones with "x" lines per file?

I posted this question here before but there were no responses. I may have done something wrong so, here it is again with some more details.
The files in the directory are named 1.txt, 2.txt, 3.txt etc.... The snippet below enters that directory, opens all the *,txt files reading them, removes the dupes and creates one file with all the unique contents. (names in this case).
$files = glob($dirname."/*.txt"); //matches all text files
$lines = array();
foreach($files as $file)
{
$lines = array_merge($lines, file($file, FILE_SKIP_EMPTY_LINES | FILE_IGNORE_NEW_LINES));
}
$lines = array_unique($lines);
file_put_contents($dirname."/allofthem.txt", implode("\n", $lines));
}
The above works great for me! Thanks to great help here at stackoverflow.
But, I desire to take it one step further.
Instead of one big duplicate free "allofthem.txt" file, how can I modify the above code to create files with a maximum of 5oo lines each from the new data?
They need to go into a new directory eg $dirname."/done/".$i.".txt" I have tried counting in the loop but my efforts are not working and ended up being a mile long.
I also attempted to push 500 into an array, increment to another array and save that way. No luck. I am just not "getting" it.
Again, this beginner needs some expert assistance. Thanks in advance.
Once you have your array of lines as per your code, you can break it into chunks of 500 lines using array_chunk, and then write each chunk to its own file:
// ... from your code
$lines = array_unique($lines);
$counter = 1;
foreach (array_chunk($lines, 500) as $chunk)
{
file_put_contents($dirname . "/done/" . $counter . ".txt", implode("\n", $chunk));
$counter++;
}
this function will get you somewhere !
function files_identical($fn1, $fn2) {
if(filetype($fn1) !== filetype($fn2))
return FALSE;
if(filesize($fn1) !== filesize($fn2))
return FALSE;
if(!$fp1 = fopen($fn1, 'rb'))
return FALSE;
if(!$fp2 = fopen($fn2, 'rb')) {
fclose($fp1);
return FALSE;
}
$same = TRUE;
while (!feof($fp1) and !feof($fp2))
if(fread($fp1, 4096) !== fread($fp2, 4096)) {
$same = FALSE;
break;
}
if(feof($fp1) !== feof($fp2))
$same = FALSE;
fclose($fp1);
fclose($fp2);
return $same;
}
Src: http://www.php.net/manual/en/function.md5-file.php#94494
$files = glob($dirname."/*.txt"); //matches all text files
$lines = array();
foreach($files as $file)
{
$lines = array_merge($lines, file($file, FILE_SKIP_EMPTY_LINES | FILE_IGNORE_NEW_LINES));
}
$lines = array_unique($lines);
$lines_per_file = 500;
$files = count($lines)/$lines_per_file;
if(count($lines) % $lines_per_file > 0) $files++;
for($i = 0; $i < $files; $i++) {
$write = array_slice($lines, $lines_per_file * $i, $lines_per_file);
file_put_contents($dirname."/done/".$i.".txt", implode("\n", $write));
}

Categories