Problem with .csv file and missed comma(s) - php

Hi all I have a problem with CSV opening through PHP code. My PHP code is:
<?php
header("Content-Type: text/html; charset=windows-1251");
echo "<html>
<head>
<title></title>
</head>
<body>
";
$file = "import.csv";
if(file_exists($file)) {
if (($fopen = fopen($file, "r")) !== FALSE) {
echo "<table>\n";
while (($data = fgetcsv($fopen, 1024, ",")) !== FALSE) {
$max = count($data);
$num++;
echo "<tr>\n<td>".$num."</td>\n";
for ($i=0;$i<$max;$i++) {
echo "<td>".$data[$i]."</td>\n";
}
echo "</tr>\n";
}
echo "</table>";
fclose($fopen);
}
}
else {
echo "File doesn't exists!";
}
echo "
</body>
</html>";
?>
The problem isn't in PHP code, the problem is in .csv file. PHP code must work even if there is missing comma, when it show the information the normal way.
The .csv file:
First name,Family,Sex,Date of birth,City,Phone number
One, Ofamily, Male, 1975, LA,13-25-16
Two, Tfamily, Male, 1955, LV, 555-14345
Three, Thfamily, Male, 1958, NY, 15689
Four, Ffamily, Female, 1974, SF, 5897912
Five, Fifamily, Male, 1991, LA, 123456789
Six, Sfamily, Male, 1967, 9876542
Seven, Sefamily, Female,, SF,

<?php
header("Content-Type: text/html; charset=windows-1251");
echo "<html>
<head>
<title></title>
</head>
<body>
";
$file = "import.csv";
if(file_exists($file)) {
if (($fopen = fopen($file, "r")) !== FALSE) {
echo "<table>\n";
while (($data = fgetcsv($fopen, 1024, ",")) !== FALSE) {
$num++;
echo "<tr>\n<td>".$num."</td>\n";
foreach($data as $k => $v) {
switch ($k) {
case 0 : // first name
case 1 : // family
case 2 : // sex
case 4 : // city
if (is_numeric($v)) {
array_splice($data,$k,0,'');
}
break;
case 3 : // date of birth
case 5 : // phone number
if (!is_numeric($v)) {
array_splice($data,$k,0,'');
}
break;
}
}
foreach($data as $v) {
echo "<td>".$v."</td>\n";
}
echo "</tr>\n";
}
echo "</table>";
fclose($fopen);
}
}
else {
echo "File doesn't exists!";
}
echo "
</body>
</html>";
?>

If you don't have control of the incoming CSV, you're not going to be able to use fgetcsv. How is it supposed to know if there's a missing ,?
Unfortunately, you're going to have to write your own function to handle this. I would start by reading each line into an array. Then looping through the line and explodeing them by a comma. Then, you'll have to check each value and try to determine if somethings missing from the resulting array.
Let's look at the problematic line in your example.
Six, Sfamily, Male, 1967, 9876542
Here's what we know about it:
It contains one less value then all the rest, so we should run some data consistency checks on it.
We know the first and second values are going to be strings that do not equal "Male" or "Female".
We know the third value should always equal either "Male" or "Female".
We know the fourth value is going to be a year and should always be a numerical value.
We know the fifth value is missing and it should be a city code and will always be two letters in length.
Based on that information, you should be able to write some checks to determine if one of those values doesn't equal what you'd expect, and then fix it.

The problem is most likely to do with lines like:
Six, Sfamily, Male, 1967, 9876542
Where there is no city information. In this case, you'll never get "9876542" to show up in the phone column unless you apply some logic to determine that this isn't the city, and to skip to the next column. What you should do though, so that you have 6 columns in each row, is that instead of resetting $max every time, you should just set it once after reading the header. Then display that number of columns from each row you read.

Related

PHP - Count Distinct Value in a CSV file

I have a csv file with a very large number of item (5000 lines) in this format
storeId,bookId,nb
124,48361,0
124,48363,6
125,48362,8
125,48363,2
126,28933,4
142,55433,6
142,55434,10
171,55871,7
171,55872,6
I need to count the number of stores in the file, so for exemple with the line above the result should be 5. But I need to doo it with 5000 lines so I can't just loop.
How can I achieve that?
I also need too return the max quantity, so 10
I began by converting the file into an array:
if (file_exists($file)) {
$csv = array_map('str_getcsv', file($file));
#Stores
$storeIds = array_column($csv, 0);
$eachStoreNb = array_count_values($storeIds);
$storeCount = count($eachStoreNb);
}
print_r($storeCount);
Is there a better way to do it? Faster ? Maybe without using the array
Faster here would come in the context of micro-optimization, however you can see an improvement in memory usage.
You could just read the file line by line instead of collecting all store IDs in an array and then doing an array_count_values() saving you an extra loop and unnecessary linear storage of all duplicate values.
Store IDs would just be made as a key for an associative array.
For max NB, you can just keep a max variable keeping the track of max value using max() function. Rest is self-explanatory.
Snippet:
<?php
$file = 'test.csv';
if (file_exists($file)) {
$fp = fopen($file ,'r');
$max_nb = 0;
$store_set = [];
fgetcsv($fp); // ignoring headers
while(!feof($fp)){
$row = fgetcsv($fp);
$store_set[$row[0]] = true;
$max_nb = max($max_nb,end($row));
}
fclose($fp);
echo "Num Stores : ",count($store_set),"<br/>";
echo "Max NB : ",$max_nb;
}else{
echo "No such CSV file found.";
}
Note: For profiling, I suggest you to try both scripts using xdebug
What if you looped through the file line by line?
I mean ...
$datas = [];
$handle = fopen("filename.csv", "r");
$flagFirstLine = true;
while(!feof($handle)){
//dont read first line
if($flagFirstLine) continue;
$flagFirstLine = false;
$csvLine = fgetcsv($handle);
$storeID = $csvLine[0];
$datas[] = $storeID;
}
echo "all row: " . count($datas);
echo "\nnum store: " . count(array_unique($datas));
What 'nice_dev' says, but a little more compact.
$fp = fopen('<your_file>', 'r');
fseek($fp, strpos($content, "\n") + 1); // skip first line
$stores = [];
while($row = fgetcsv($fp)) {
$stores[$row[0]] = max([($stores[$row[0]] ?? 0), $row[2]]);
}
Working example.
An answer with awk would be:
awk -F, 'BEGIN {getline}
{ a[$1]++; m=$3>m?$3:m }
END{ for (i in a){ print i, a[i] };
print "Number of stores",length(a), "max:",m}' testfile
getline to skip the first line
increment the element with the value of the first column $1 in array a with one, and keep the max value in m
loop over the array a and print all counts (optional)
print the total 'Number of stores', and the max value.
output:
124 52
125 52
126 26
142 52
171 52
Number of stores 5 max: 10
Solution in AWK, to compare the difference. This includes the count of each store as well. AWK should be able to process millions in less than 1 second. I use the same to filter duplicates from a file.
BEGIN{ # Set some variables initially
FS="," # field separator for INPUT
mymax=0 # init variable mymax
}
NR>1 { # skip the header line, this matches line 2 onwards
mycount[$1]++ # increase associative array at that position
if ($3>mymax){ # compare with max
mymax=$3
}
}
END{ # finally print results
for (i in mycount){
if (length(i)>0){
print "value " i " has " mycount[i]
}
}
print "Maximum value is " mymax
}

PHP read text file and display formatted results

My goal is to use PHP to read data from a text file and display a page based on said data.
The text file would look something like this...
joe smith|ceo|chief executive officer|10|14|126
Jane Doe|cfo|chief financial officer|8|12|94
What I need to be able to do is read the text file, extract each of the values, separated by the "|" token, and format the output in a table-like display. When reading the text file, the first column (name) equates to a picture, located in a subfolder. Therefore, the output should look something like this...
Can anyone out there assist with this request?
Thanks in advance.
You need something like that:
I would save the Data in this example in a file called test.csv
But I think you get some problems with the first row. A Space in the src of the img-Tag....
I would replace the space from the Name with str_replace() in the PHP
and the space in the name of the Pictures with Underscrore "_"
<?php
echo '<table>';
$row = 1;
if (($handle = fopen("test.csv", "r")) !== FALSE) {
while (($data = fgetcsv($handle, 1000, "|")) !==false) {
$num = count($data);
//print_r($data);
echo "<tr> ";
$row++;
for ($c=0; $c <= $num; $c++) {
if ($c==0){
echo '<td><img src="YOUR/SUBFOLDER/"'.$data[$c]
."/>".$data[$c].'</td>';
}else{
echo '<td>'.$data[$c] .'</td>';
}
}
echo "</tr> \n";
}
fclose($handle);
echo '</table>';
}
?>
Hope It Helps :-).
Sorry for my bad english

Select specifc value from csv matrix using PHP

I have been going round in circles with this and I'm hoping someone can help.
I have a .csv file containing data, example:
Weight, 10, 20, 30, 40
product1, 1, 2, 3, 4
product2, 2, 4, 6, 8
product3, 3, 6, 9, 12
What I need to do, using PHP, is select the value from the matrix where the row is "product2" and the number in that row corresponds with the nearest rounded match to a Weight of 27. The result should therefore be 6.
I've tried adding the csv rows to an array using fgetcsv and looping through to get the line where the first array item = product2, but I can't cross reference it with the Weight values from line 1.
I appreciate this csv is effectively upside down but this is the easiest way to manage it.
I can post some code I've tried so if it helps but, to be honest, it's all a bit of a mess so I was hoping someone could point me in the right direction with some working code.
Any help is appreciated! Thanks.
<?php
$first_line;
$other_lines_separated;
$file=file('CSV_File_location'); //Put file contents in an array with each item being a line
foreach($file as $f){
//Get each line then break that line an into a array
$values=explode(',',$f);
//If this is the first line we want it separate
if($f== $file[0]){
$first_line=$values;
}else{
$other_lines_separated[]=$values;
}
}
//We now have an array with the first line and an array holding the other arrays with values separated
//Go through each of the other arrays and do what you need
for($i=0;$i<count($other_lines_separated); $i++){
$other_line= $other_lines_separated[$i];
for($y=0;$y<count($other_line); $y++){
// in here we'll have access to any value we need
if($other_line[$y] == "product1"){
//We're on the product1 line
}
if($other_line[$y] == "product2"){
//We're on the product2 line
}
if($other_line[$y] == "product3"){
//We're on the product3 line
}
echo "currently at value ".$other_line[$y]."</br>";
}
}
?>
You should define what the nearest rounded match is. For 24 it should look the value for 20? I think as I understand what for you use it for 24 it should also be 30 otherwise you should define if round should be defined for a half of weights (first column) or not.
There is sample code that works as I think it should, so for 24 it will also choose 30
PHP file:
<?php
$name = 'product2';
$weight = 24;
$lines = explode("\n", file_get_contents("test.csv"));
$found= '';
$foundCost = false;
foreach ($lines as $line) {
if (strpos($line,$name.',') === 0) {
$found = $line;
break;
}
}
if ($found != '') {
$found = explode(',', $found);
$weights = explode(',',$lines[0]);
$index = false;
for ($i=1; $i<count($weights); ++$i) {
if ($weights[$i] >= $weight) {
$index = $i;
break;
}
}
if ($index !== false) {
$foundCost = $found[$index];
}
}
if ($foundCost !== false) {
echo "found ".$foundCost;
}
CSV file (I assumed there are no space after , as in ordinary CS file):
Weight,10,20,30,40
product1,1,2,3,4
product2,2,4,6,8
product3,3,6,9,12

Stucked with array and explode - spaces

I have an application called mystique item where users has to guess the item that's behind the watermark. Water mark is revealing on time to time and everything is working perfect, but i have a "small" problem with guessing words. I have added words in arrays, separated with commas, and i'm exploding that array in my php, but for some reason, it only catches the first word as correct, everything else is being incorrect. Here's what i have done.
$gt = getVal('pics','gtext','online',1);
$won = getVal('pics','winner','online',1);
if($won=='no')
{
$counts = getGen(3);
$counts2 = getGen(4);
if($counts2==0)
{
$counts2 = 9999999999999;
}
$ccount = getCount2("$uid","$pid",date('Y-m-d H:i:s',$t1),date('Y-m-d H:i:s',$t2));
$ccount3 = getCount3("$uid","$pid");
if( $ccount>=$counts || $ccount3>=$counts2)
{
echo '4';
}
else
{
$sp = explode(",",$gt);
if(in_array($val, $sp)) // guess correct
{
echo '1';
}
else// guess wrong
{
echo '2';
}
}
}
gtext is the row where I store the words, my words has spaces in them, for example: new iphone,iphone 5s,apple ipad,etc etc).
And here's the code that checks the words:
$.post('guessit.php',{from:1,val:$('#ug').val(),uid:$('#uid').val(),pid:$('#pid').val(),t1:<?php echo $time1; ?>,t2:<?php echo $time3; ?>},function(d){
if(parseInt(d)==1){
$.post('guessit.php',{from:2,val:$('#ug').val(),uid:$('#uid').val(),pid:$('#pid').val(),t1:<?php echo $time1; ?>,t2:<?php echo $time3; ?>},function(d1){
advanced_example($('#uid').val(),'Congratulations!','You are the winner!!',1);
//setInterval($(location).attr('href','redirecttohome.php'),10000);
});
}else if(parseInt(d)==2){
$.post('guessit.php',{from:3,val:$('#ug').val(),uid:$('#uid').val(),pid:$('#pid').val(),t1:<?php echo $time1; ?>,t2:<?php echo $time3; ?>},function(d1){
advanced_example($('#uid').val(),'Wrong!','Please try again!',1);
//setInterval($(location).attr('href','redirecttohome.php'),10000);
});
}else if(parseInt(d)==3){
advanced_example($('#uid').val(),'Sorry!','Someone else was faster!',1);
//setInterval($(location).attr('href','redirecttohome.php'),8000);
}else if(parseInt(d)==4){
advanced_example($('#uid').val(),'Error!','You already attempted maximum times',1);
//setInterval($(location).attr('href','redirecttohome.php'),8000);
}
guessit.php is containing the first code I've showed you.
If you need anything else in order to help me, please let me know.
#AmalMurali What I need is next: I have in MySQL:
apple ipad,apple iphone4,apple ipod,iphone4,apple
I need them as strings as:
apple ipad
apple iphone4
apple ipod
iphone4
apple
You need to trim the whitespace for the if conditions to work as you want it:
$sp = explode(",",$gt);
$sp = array_map('trim', $sp); //trim all the elements in $sp
If the elements contain a whitespace in the beginning or end, the following condition will evaluate to FALSE, thus triggering the statements in else block:
if(in_array($val, $sp)) {
If the whitespace is removed, in_array should work and the code should work as expected.

Searching if letters in a word are found in another word PHP Strings

Just like the title of this post says, I would to be able to check if every letter of a word is found in another word. So far these are the lines of codes that I was able to come up with:
<?php
$DBword = $_POST['DBword'];
$inputWords = $_POST['inputWords'];
$inputCount = str_word_count($inputWords,1);
echo "<b>THE WORD:</b>"."<br/>".$DBword."<br/><br/>";
echo "<b>WORDS ENTERED:</b><br/>";
foreach($inputCount as $outputWords)
{
echo $outputWords."<br/>";
}
foreach($inputCount as $countWords)
{
for($i=0; $i<strlen($countWords); $i++)
{$count = strpos( "$DBword", $countWords[$i]);}
if($count === false)
{
$score++;
}
}
echo "<b><br/>TOTAL SCORE: </b>";
echo $score;
?>
My point in having the foreach with the $outputWords is to just output the letters entered.
As for the other foreach that has $countWords, I am using it to really check if all letters in the word entered are found in the $DBword. I am using the for loop to check every letter.
So far, I am not getting the output that I want and I just ran out of ideas. Any ideas please?
function contains_letters($word1, $word2) {
for ($i = 0; $i < strlen($word1); $i++)
if (strpos($word2, $word1{$i}) === false)
return false;
return true;
}
//example usage
if (contains_letters($_POST['inputWords'], $_POST['DBword']))
echo "All the letters were found.";
If this check should be case-insensitive (i.e. 'A' counts as a usage of 'a'), change strpos to stripos.
Since you are overwriting $count in the for loop for each letter in $countWords, $count will contain the position of the last letter of $countWord only. Also, I am not sure why you increase score when the letter wasn't found.
In any case, you are making your life more difficult than necessary.
PHP has a function for counting chars in a string:
return count_chars($dbWord, 3) === count_chars($inputWord, 3);
will return true if the same letters are found in both strings.
Example to find all the words having exactly the same letters:
$dbWord = count_chars('foobar', 3);
$inputWords = 'barf boo oof raboof boarfo xyz';
print_r(
array_filter(
str_word_count($inputWords, 1),
function($inputWord) use ($dbWord) {
return count_chars($inputWord, 3) === $dbWord;
}
)
);
will output "raboof" and "boarfo" only.

Categories