Suppose that I have 2 files:
File1.txt
10;30;15;40;12;14;15
23;32;10;50;12;54;60
File2.txt
2;4;5;6;7;8;9
3;6;7;8;9;0;7
I want to subtration between these 2 files. Ex 10 - 2........
PHP code:
$file1 = 'File1.txt';
$file2 = 'File2.txt';
if(file_exists($file1)){
$files = fopen($file1,'r');
while(!feof($files)){
$data = explode(";",fgets($files));
if($title ==""){
$title = $data[0];
}
if(!empty($data[3])){
$cate = $data[3];
$filepmta = fopen($file2,'r');
while(!feof($filepmta)){
$hourData = explode(";",fgets($filepmta));
if(!empty($hourData[3])){
if($title ==""){
$title = $hourData[0];
}
if(!empty($btnHour)){
echo $percentRed = ((int)$data[2] - (int)$hourData[2]);
//it loads page so long time so I don know what's error.
}
}
}
It loads the page is so long time.I don know how to fix this,Anyone know help me please,thanks.
I would recommend just simply opening both files and storing the contents of the file into memory. IE Create 2 simple loops (not nested) which iterates through both files respectively and saves the content to an array.
Then make one more loop after the first two. Iterate through the third loop based on the array size of the first two. (I'm assuming you can assume they are both the same size, but it will be a good idea to add checks to make sure loop 1 and loop 2 produce the same size/length arrays.
Within the 3rd loop iterate through both previously two generated arrays with the same index.
Pseudo Code // even tho I said I'd do pseudo code, I ended up doing it all. :-/
$file_values1 = $file_values2 = $answers = array();
while(!feof($file1)) $file_values1[] = explode(';', fgets($file)); // loop 1
while(!feof($file2)) $file_values2[] = explode(';', fgets($file)); // loop 2
foreach($file_values1 as $key1 => $sub_array) { // loop 3
foreach($sub_array as $key2 => $value) // loop 4; hehe, oh wellz
{
$answers[] = $file_values1[$key1][$key2] = $file_values2[$key1][$key2]
}
}
Basically, the nested loop 4, meh. There ARE faster answers. The 3rd loop can be improved upon, it's O(N^2). I'll leave that for you to optimize. No free lunches. :-)
fgets() doesn't move the file pointer after the last line, so you're staying on the last line, feof() still returns false and you're trapped in an infinite loop.
The common way of doing what you want is:
while (($line = fgets($handle)) !== false) {
echo $line;
}
Have a look at the code example in fgets() documentation, it's quite exhaustive.
You're making this way more complicated than it has to be.
Open your files and read them both in with file(), which will give you an array of lines in each file.
Iterate over both line arrays simultaneously with a MultipleIterator.
Convert each lines' contents to arrays with str_getcsv() and an array_map() trick.
Iterate over each line values with another MultipleIterator.
Compute the subtraction and print it.
That's all it takes! I've implemented the above algorithm below.
$file1 = 'File1.txt';
$file2 = 'File2.txt';
$file1 = file( $file1);
$file2 = file( $file2);
$iter = new MultipleIterator;
$iter->attachIterator( new ArrayIterator( $file1));
$iter->attachIterator( new ArrayIterator( $file2));
foreach( $iter as $element) {
list( $line1, $line2) = array_map( 'str_getcsv', $element, array( ';', ';'));
$val_iter = new MultipleIterator;
$val_iter->attachIterator( new ArrayIterator( $line1));
$val_iter->attachIterator( new ArrayIterator( $line2));
foreach( $val_iter as $value) {
list( $el1, $el2) = $value;
echo ($el1 - $el2); echo ";";
}
echo "\n";
}
You can see from this demo, which statically defines the files as arrays, that this produces:
8;26;10;34;5;6;6;
20;26;3;42;3;54;53;
Related
I have two text files of data the first file has 30 lines of data and matches with 30 lines in the second text file, but in addition the first text file has two additional lines that are added as the operator uploads file to the directory I want to find the non matching lines and out put them to be used in the same script as a mailout.
I am trying to use this code, which outputs the contents of the two files to screen.
<?php
if ($file1 = fopen(".data1.txt", "r")) {
while(!feof($file1)) { $textperline = fgets($file1);
echo $textperline;
echo "<br>";}
if ($file2 = fopen(".data.txt", "r")) {
while(!feof($file2)) {$textperline1 = fgets($file2);
echo $textperline1;
echo "<br>";}
fclose($file1);
fclose($file2);
}}
?>
But it outputs the whole list of data, can anyone help listingout only NON matching lines?
attached output of the two files from my code
I want to output only lines that are in file2 but not in file1
My suggestion would be to read each file into an array (one line = one element) and then use array_diff to compare them. Unless you have millions of lines, this approach is the easiest.
To reuse your code, this is how you can read the 2 files into two arrays
$list1 = [];
$list2 = [];
if ($file1 = fopen(".data1.txt", "r")) {
while (!feof($file1)) {
$list1[] = trim(fgets($file1));
}
fclose($file1);
}
if ($file2 = fopen(".data.txt", "r")) {
while (!feof($file2)) {
$list2[] = trim(fgets($file2));
}
fclose($file2);
}
If the files are small and you can read them in one go, you can also use a simplified syntax.
$list1 = explode(PHP_EOL, file_get_contents(".data1.txt"));
$list2 = explode(PHP_EOL, file_get_contents(".data.txt"));
Then, no matter which method you chose, you can compare them as follows
$comparison = array_diff($list2, $list1);
foreach ($comparison as $line) {
echo $line."<br />";
}
This will only output the lines of the second array that are not present in the first one.
Make sure that the one with the additional lines is the first argument of array_diff
ASSUMPTION
Both files are not huge and you can read the whole content into the memory at once. According to this, you can put following code to the top:
$file1 = "./data1.txt";
$file2 = "./data2.txt";
$linesOfFile1 = file($file1);
$linesOfFile2 = file($file2);
$newLinesInFile2 = [];
There are a couple cases, which you did not mention in your question.
CASE 1
New lines are only appended to the secode file file2. The solution for this case is the easiest one:
$numberOfRowsFile1 = count($linesOfFile1);
$numberOfRowsFile2 = count($linesOfFile1);
if($numberOfRowsFile2 > $numberOfRowsFile1)
{
$newLinesInFile2 = array_slice($linesOfFile2, $numberOfRowsFile1);
}
CASE 2
The lines with the same content may have different position in each file. Duplicate lines within the same file are ignored.
Furthermore the case sensitivity may play a role. That's why the content of each line should be hashed to make a simpler comparison. For both case sensitive and insensitive comparison the following function is needed:
function buildHashedMap($array, &$hashedMap, $caseSensitive = true)
{
foreach($array as $line)
{
$line = !$caseSensitive ? strtolower($line) : $line;
$hash = md5($line);
$hashedMap[$hash] = $line;
}
}
Case sensitive comparison
$hashedLinesFile1 = [];
buildHashedMap($linesOfFile1, $hashedLinesFile1);
$hashedLinesFile2 = [];
buildHashedMap($linesOfFile2, $hashedLinesFile2);
$newLinesInFile2 = array_diff_key($hashedLinesFile2, $hashedLinesFile1);
Case INSENSITIVE comparison
$caseSensitive = false;
$hashedLinesFile1 = [];
buildHashedMap($linesOfFile1, $hashedLinesFile1, $caseSensitive);
$hashedLinesFile2 = [];
buildHashedMap($linesOfFile2, $hashedLinesFile2, $caseSensitive);
$newLinesInFile2 = array_diff_key($hashedLinesFile2, $hashedLinesFile1);
i am fairly new to PHP and tried several hours to get something going, sadly without a result. I hope you can point me into the right direction.
So what i got is a CSV file containing Articles. They are separated into diff columns and always the same structure, for example :
ArtNo, ArtName, ColorCode, Color, Size
When an article has different color codes in the CSV, the article is simply repeated with the same information except for the color code, see an example:
ABC237;Fingal Edition;48U;Nautical Blue;S - 5XL;
ABC237;Fingal Edition;540;Navy;S - 5XL;
My problem is, i want to display all the articles in a table, include an article image etc.. so far i got that working which is not a problem, but instead of showing the article twice for every different color code i want to create only one line per ArtNo (First CSV Line) but still read the second duplicate line to add the article color to the first one, like :
ABC237; Fingal Edition ;540;Nautical Blue, Navy;S - 5XL;
Is this even possible or am I going into a complete wrong direction here? My code looks like this
<?php
$csv = readCSV('filename.csv');
foreach ($csv as $c) {
$artNo = $c[0]; $artName = $c[1]; $colorCode = $c[2]; $color = $c[3]; $sizes = $c[4]; $catalogue = $c[5]; $GEP = $c[6]; $UVP = $c[7]; $flyerPrice = $c[8]; $artDesc = $c[9]; $size1 = $c[10]; $size2 = $c[11]; $size3 = $c[12]; $size4 = $c[13]; $size5 = $c[14]; $size6 = $c[15]; $size7 = $c[16]; $size8 = $c[17]; $picture = $c[0] . "-" . $c[2] . "-d.jpg";
// Echo HTML Stuff
}
?>
Read CSV Function
<?php
function readCSV($csvFile){
$file_handle = fopen($csvFile, 'r');
while (!feof($file_handle) )
{
$line_of_text[] = fgetcsv($file_handle, 0, ";");
}
fclose($file_handle);
return $line_of_text;
}
?>
I tried to get along with array_unique etc but couldn't find a proper solution.
Read all the data into an array, using the article number as the key....
while (!feof($file_handle) ) {
$values = fgetcsv($file_handle, 0, ";");
$artno = array_shift($values);
if (!isset($data[$artno])) $data[$artno]=array();
$data[$artno][]=$values;
}
And then output it:
foreach ($data as $artno=>$v) {
$first=each($v);
print $artno . "; " . each($first);
foreach ($v as $i) {
$discard=array_shift($i);
print implode(";", $i);
}
print "\n";
}
(code not tested, YMMV)
You need to know exactly how many items belong to each ArtNo group. This means a loop to group, and another loop to display.
When grouping, I steal the ArtNo from the row of data and use it as the grouping key. The remaining data in the row will be an indexed subarray of that group/ArtNo.
I am going to show you some printf() and sprintf() syntax to keep things clean. printf() will display the first parameter's content and using any subsequent values to replace the placeholders in the string. In this case, the 2nd parameter is a conditional expression. On the first iteration of the group, ($i = 0), we want to show the ArtNo as the first cell of the row and declare the number of rows that it should span. sprinf() is just like printf() except it produces a value (silently). Upon any subsequent iterations of the group, $i will be greater than zero and therefore an empty string is passed as the value.
Next, I'm going to use implode() which is beautifully flexible when you don't know exactly how many columns your table will have (or if the number of columns may change during the lifetime of your project).
Tested Code:
$csv = <<<CSV
ABC237;Fingal Edition;48U;Nautical Blue;S - 5XL
ABC236;Fingal Edition;540;Navy;S - 5XL
ABC237;Fingal Edition;49U;Sea Foam;L - XL
ABC237;Fingal Edition;540;Navy;S - 5XL
CSV;
$lines = explode(PHP_EOL, $csv);
foreach ($lines as $line) {
$row = str_getcsv($line, ';');
$grouped[array_shift($row)][] = $row;
}
echo '<table>';
foreach ($grouped as $artNo => $group) {
foreach ($group as $i => $values) {
printf(
'<tr>%s<td>%s</td></tr>',
(!$i ? sprintf('<td rowspan="%s">%s</td>', count($group), $artNo) : ''),
implode('</td><td>', $values)
);
}
}
echo '</table>';
Output:
I'm iterating over a directory and merging the JSON-Files in it. But it doesn't quite work as i want it to. The array I get after iterating over 3 files is just the last file. Somewhere along the way it seems to just overwrite the previous files. I'm not sure where though. Also I would like to remove rows with certain entries, but I'd be happy if the merging would work at least.
<?php
$dir = new DirectoryIterator("path1");
$destination = "path2";
$json = file_get_contents($destination);
$result = json_decode($json,true);
foreach ($dir as $fileinfo) {
if (!$fileinfo->isDot()) {
$path = $dir -> getPathname();
$data = file_get_contents($path);
echo $data; //works as intended. Prints 3 different Arrays after eachother
$current = json_decode(file_get_contents($path),true);
$result = array_merge($result,$current);
}
}
$final = json_encode($result);
file_put_contents($destination,$final);
?>
Thanks in advance for any help
The function array_merge has this overwriting behaviour, as specified in the manual:
If the input arrays have the same string keys, then the later value for that key will overwrite the previous one.
This effect can be illustrated with this little example:
$a1 = array("a" => 1, "b" => 2);
$a2 = array("a" => 100, "b" => 200);
$result = array_merge($a1, $a2);
print_r (json_encode($result));
output:
{"a":100,"b":200}
So, the values of the first array are lost.
There are several solutions, but it depends on which result you would like to get. If for instance you would like to get this:
{"a":[1, 100],"b":[2, 200]}
Then use the function array_merge_recursive instead of array_merge.
If you prefer to get this:
[{"a":1,"b":2},{"a":100,"b":200}]
Then use this code:
$result[] = $a1;
$result[] = $a2;
In your original code, that last solution would look like this:
$result[] = json_decode($json,true);
foreach ($dir as $fileinfo) {
// ...
$result[] = $current;
// ...
}
I have a 12 XML files from which I am extracting ONE CSV file, from which - I am extracting column 1 and appending values to a tt.txt file .
NOW, I need to extract the values from this .txt file... everytime data is written to it ...
But the problem is , when I use
$contents = fread ($fd,filesize ($filename));
fclose ($fd);
$delimiter = ',' ;
$splitcontents = explode($delimiter, $contents);
IT reads ONLY from the first value of the file , every time a tt.txt file is appended !
I hope u understand the problem .. What I need is , I want $contents to have only the new data that was appended... instead it reads from the start of the file everytime...
Is there a way to achieve this, or does php fail ?/
This prob is extraction from TXT file- > performing computations- > writing INTO a new txt file . The problem being that I can't read from a middle value to a new value.. PHP always reads from the start of a file.
I think you need to store the last file position.
Call filesize to get current length, read the file, later, check if filesize is different (or maybe you know this some other way, and use fseek to move the cursor in the file, then read from there.
IE:
$previousLength = 0;
// your loop when you're calling your new read function
$length = filesize($filename);
fseek($fd,$previousLength);
$contents = fread($fd,$length - $previousLength);
$previousLength = $length;
It is only reading the first field because PHP does not automatically assume that a newline character (\n) means a new record; you have to handle this, yourself.
Using what you already have, I would do the following:
$contents = fread($fd, filesize($filename));
close($fd);
/* Now, split up $contents by newline, turning this into an array, where each element
* is, in effect, a new line in the CSV file. */
$contents = explode("\n", $contents);
/* Now, explode each element in the array, into itself. */
foreach ($contents as &$c) {
$c = explode(",", $c);
}
In the future, if you want to go line-by-line, as you run the risk of hogging too many resources by reading the entire file in, use fgets().
I'm not great at arrays but it sounds to me like you need an associative array (I'm doing a similar thing with the following code.
$lines = explode("\n", $contents);
foreach ($lines as $line) {
$parts = explode(',', $line);
if (count($parts) > 0) {
$posts = array();
$posts[] = array('name' => $parts[3],'email' => $parts[4],'phone' => $parts[5],'link' => $parts[6],'month' => $parts[0],'day' => $parts[1],'year' => $parts[2]); }
foreach ($posts as $post):
$post = array_filter(array_map('trim', $post));
I'm trying to read data from a.csv file to ouput it on a webpage as text.
It's the first time I'm doing this and I've run into a nasty little problem.
My .csv file(which gets openened by Excel by default), has multiple rows and I read the entire thing as one long string.
like this:
$contents = file_get_contents("files/data.csv");
In this example file I made, there are 2 lines.
Paul Blueberryroad
85 us Flashlight,Bag November 20,
2008, 4:39 pm
Hellen Blueberryroad
85 us lens13mm,Flashlight,Bag,ExtraBatteries November
20, 2008, 16:41:32
But the string read by PHP is this:
Paul;Blueberryroad 85;us;Flashlight,Bag;November 20, 2008, 4:39 pmHellen;Blueberryroad 85;us;lens13mm,Flashlight,Bag,ExtraBatteries;November 20, 2008, 16:41:32
I'm splitting this with:
list($name[], $street[], $country[], $accessories[], $orderdate[]) = split(";",$contents);
What I want is for $name[] to contain "Paul" and "Hellen" as its contents. And the other arrays to receive the values of their respective columns.
Instead I get only Paul and the content of $orderdate[] is
November 20, 2008, 4:39 pmHellen
So all the rows are concatenated. Can someone show me how i can achieve what I need?
EDIT: solution found, just one werid thing remaining:
I've solved it now by using this piece of code:
$fo = fopen("files/users.csv", "rb+");
while(!feof($fo)) {
$contents[] = fgetcsv($fo,0,';');
}
fclose($fo);
For some reason, allthough my CSV file only has 2 rows, it returns 2 arrays and 1 boolean. The first 2 are my data arrays and the boolean is 0.
You are better off using fgetcsv() which is aware of CSV file structure and has designated options for handling CSV files. Alternatively, you can use str_getcsv() on the contents of the file instead.
The file() function reads a file in an array, every line is an entry of the array.
So you can do something like:
$rows = array();
$name = array();
$street = array();
$country = array();
$rows = file("file.csv");
foreach($rows as $r) {
$data = explode(";", $r);
$name[] = $data[0];
$street[] = $data[1];
$country[] = $data[2];
}
I've solved it now by using this piece of code:
$fo = fopen("files/users.csv", "rb+");
while(!feof($fo)) {
$contents[] = fgetcsv($fo,0,';');
}
fclose($fo);
For some reason, allthough my CSV file only has 2 rows, it returns 2 arrays and 1 boolean. The first 2 are my data arrays and the boolean is 0.
The remark about fgetcsv is correct.
I will still answer your question, for educational purpose. First thing, I don't understand the difference between your data (with comas) and the "string read by PHP" (it substitutes some spaces with semi-colon, but not all?).
PS.: I looked at the source code of your message, it looks like an odd mix of TSV (tabs) and CSV (coma).
Beside, if you want to go this way, you need to split first the file in lines, then the lines in fields.
The best way is of course fgetcsv() as pointed out.
$f = fopen ('test.csv', 'r');
while (false !== $data = fgetcsv($f, 0, ';'))
$arr[] = $data;
fclose($f);
But if you have the contents in a variable and want to split it, and str_getcsv is unavailable you can use this:
function str_split_csv($text, $seperator = ';') {
$regex = '#' . preg_quote($seperator) . '|\v#';
preg_match('|^.*$|m', $text, $firstline);
$chunks = substr_count($firstline[0], $seperator) + 1;
$split = array_chunk(preg_split($regex, $text), $chunks);
$c = count($split) - 1;
if (isset($split[$c]) && ((count($split[$c]) < $chunks) || (($chunks == 1) && ($split[$c][0] == ''))))
unset($split[$c]);
return $split;
}