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;
}
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);
While reading a csv file with PHP a problem occured with a line break within the CSV file. The contents of one cell will be split once a comma is followed by a line break:
$csv = array_map('str_getcsv', file($file));
first,second,"third,
more,text","forth"
next,dataset
This will result in:
1) first | second | third
2) more text | forth
3) next | dataset
While it should result in:
1) first | second | third more text | forth
2) next | dataset
Is this a bug within str_getcsv?
Don't do that, use fgetcsv(). You're having problems because file() doesn't care about the string encapsulation in your file.
$fh = fopen('file.csv', 'r');
while( $line = fgetcsv($fh) ) {
// do a thing
}
fclose($fh);
https://secure.php.net/manual/en/function.fgetcsv.php
And try not to store all the lines into an array before performing your operations if you can help it. Your system's memory usage will thank you.
<?php
$csvString = "ID,Condition,Condition,Condition,Condition,AdSize,Content:Text,Content:Text,Content:Text,Content:ImageUrl,Content:LandingPageUrl,Archive,Default
ID,Locations:Region,Device Properties:Device,Weather:Condition,Dmp:Liveramp,AdSize,title1,description1,price1,imageUrl1,landingPageUrl1,Archive,Default
ROW_001,\"Wa, Ca, Tn\",Mobile,Snow,12345,300x250,Hello Washingtonian,My Custom Description,10,http://domain/Snow.jpg,https://www.example.com,TRUE,
ROW_002,Wa,Mobile,Snow,12345,300x250,Hello Washingtonian,My Custom Description,10,http://domain/New_Snow.jpg,https://www.example.com,,
ROW_003,Wa,Mobile,,,300x250,Hello Washingtonian,My Custom Description,10,http://domain/clear.jpg,https://www.example.com,,
ROW_004,,,,,300x250,Hello,My Custom Description,20,http://domain/clear.jpg,https://www.example.com,,TRUE";
function csvToArray($csvString, $delimiter = ',', $lineBreak = "\n") {
$csvArray = [];
$rows = str_getcsv($csvString, $lineBreak); // Parses the rows. Treats the rows as a CSV with \n as a delimiter
foreach ($rows as $row) {
$csvArray[] = str_getcsv($row, $delimiter); // Parses individual rows. Now treats a row as a regular CSV with ',' as a delimiter
}
return $csvArray;
}
print_r(csvToArray($csvString));
https://gist.github.com/sul4bh/d392315c7049abd86916e077707bf123
<?php
// Open the file
$filename = 'pvemail.txt';
$fp = fopen($filename, 'r');
// Add each line to an array
if ($fp) {
$array = explode("\n", fread($fp, filesize($filename)));
}
//print_r ($array);
for ($c = 0; $c < count($array); $c++){
$cell = explode(",", $array[$c]);
print_r ($cell);
echo '<br/>';
}
?>
I am currently working on this code. I have taken a text file generated from a Google report, and managed to explode it into an array, and then I've taken each element of the array and exploded that into another array. However, the problem I'm now having is I only want to retrieve 3 elements of the second exploded array and there are 20 elements to each array.
What would be the best way to go about this, should I use a for or foreach loop? I only need to print $cell[2], $cell[11] and $cell[12]. I have tried using:
echo ($cell[2] + " " + $cell[12] + " " + $cell[11]
($cell[11] and $cell[12] are in this order because 11 is a last name and 12 is a first name and I want the first name first so I've had to put them backwards) but when I run that piece of code it just outputs line breaks and 0's. I'm really just wondering what would be the most effective method of looping through the arrays, and should I do it within the loop that I have already established?
I was thinking that if I were to put it inside my existing for loop I could use an if/else loop, something like:
if($cell = $cell[2]){
echo ($cell[2])
};
but i'm not convinced this will work. Should I define a variable to store $cell[2], [11] and [12] in, and create my if loop based on that, and then I would only need to echo the variable? Is that likely to be effective? Any help would be appreciated, I've looked around on the forum for posts similar to this but I haven't been able to find anything.
20130912,b875c9b154cf7b8d,el#pv-eu.com,ACTIVE,30720,1054180015,,,20100902,20130910,20130904,L,E,,,,20130911,2010-09-02 09:11:37,2013-09-10 23:51:21,2013-09-04 03:06:09,2013-09-11 00:41:24
20130912,66c63753b8188f17,lf#pv-eu.com,ACTIVE,30720,3699701524,,,20110315,20130911,20130911,F,L,,,,19691231,2011-03-15 02:00:31,2013-09-11 00:50:17,2013-09-11 00:52:16,1969-12-31 16:00:00
20130912,bd5ef40689adf9ac,ah#pv-eu.com,ACTIVE,30720,3476851137,,,20110426,20130911,20130910,H,A,,,,20110720,2011-04-26 01:47:56,2013-09-11 16:58:48,2013-09-10 06:20:26,2011-07-20
This is how the text file itself looks, although there is a lot more data. All I'm trying to pull is the email address and name.
Assuming pvemail.txt is a CSV file, does this solve your problem?
$content = file_get_contents('pvemail.txt');
$lines = explode("\n", $content);
header('Content-type: text/plain');
foreach($lines as $line) {
$values = explode(',', $line);
echo $values[2], ' ', $values[12], ' ', $values[11], "\n";
}
Using the 3 sample lines, the above code outputs this:
el#pv-eu.com E L
lf#pv-eu.com L F
ah#pv-eu.com A H
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;
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));