Unexpected Behavior when Parsing Text File - php

I am learning PHP, & right now I'm stuck.
I am reading a .txt file in the script. The file contents are like this:
joe:secret
root:admin
I can read the file easily using the file() function, which returns an array. I store the data in a a variable as:
$data = file('location/file.txt');
Next I use foreach loop, and then I explode each line, and store its contents in a variable again. Then I put checks on the variable, but this is where I get behavior which I don't understand.
foreach ($data as $d) {
$row = explode(":", $d);
if ($row[0] == "joe" && $row[1] == "secret") {
echo "match found ";
}
}
The above code does give expected output. Why is that? If I do this,
echo $row[0]; echo $row[1];
then I do receive correct output. So I don't get why my check fails?

This one was tricky; I don't blame you for not catching this :)
So first, let's investigate with the php -a interactive shell:
php > $data = file('test.txt');
php > var_dump($data);
array(3) {
[0]=>
string(11) "joe:secret
"
[1]=>
string(11) "root:admin
"
[2]=>
string(1) "
"
}
See how the closing quote is on a separate line? That's because the \n's at the end of each line are kept inside each array element in $data. So $row[1] doesn't equal "secret"; it equals "secret\n". To fix this, all we need is rtrim():
foreach ($data as $line) {
$line = rtrim($line);
$row = explode(":", $line);
if ($row[0] == "joe" && $row[1] == "secret") {
echo "Match found for joe!";
}
}
Now, it works as expected:
php > $data = file('test.txt');
php > foreach ($data as $line) {
php { $line = rtrim($line);
php { $row = explode(":", $line);
php {
php { if ($row[0] == "joe" && $row[1] == "secret") {
php { echo "Match found for joe!";
php { }
php { }
Match found for joe!
php >
Edit: We could also use file_get_contents() instead of file(), so we just get the file contents as a string, and convert it into an array ourselves:
$data = file_get_contents('test.txt');
foreach (explode("\n", $data) as $line) {
$row = explode(":", $line);
if ($row[0] == "joe" && $row[1] == "secret") {
echo "Match found for joe!";
}
}

Related

Converting csv to array causing "Tokenization is skipped for long lines for performance reasons."

After uploading a CSV, I am trying to convert the first column (a list of domains) to an array and add the array items to a txt file.
So the CSV shows:
test.com
test2.net
test3.org
And the txt file should show them like this:
test.com, test2.net, test3.org
Everything works, except the first row (test.com) doesn't seem to be converting as text properly. All of the other rows work fine. When I look at the txt file, the content is there and doesn't seem to show an issue, but in my IDE it shows an error before the first item: Tokenization is skipped for long lines for performance reasons.
Screenshot of my txt file in VS Code:
I need to check each domain in the txt file before adding new domains to prevent duplicates, and it's not recognizing test.com as being there already.
So I ran a test to compare the array to a manually written array with the same values, and sure enough test.com and test.com do not equal each other. The other domains do.
// Converting a CSV UTF-8 (Comma delimited) to array
function csv_to_array( $filepath ) {
// Get the rows
$rows = array_map('str_getcsv', file( $filepath ));
// Store the items here
$csv = [];
// Grab the items
foreach($rows as $row) {
$csv[] = $row[0];
}
// Return the array
return $csv;
}
Running my test:
$csv_url = 'test.csv';
$csv_domains = csv_to_array( $csv_url );
print_r($csv_domains);
$csv_domains_string = implode(', ', $csv_domains);
print_r('<br>'.$csv_domains_string);
echo '<br><hr><br>';
$compare_domains = ['test.com', 'test2.net', 'test3.org'];
print_r($compare_domains);
$compare_domains_string = implode(', ', $compare_domains);
print_r('<br>'.$compare_domains_string);
echo '<br><hr><br>';
if ($csv_domains[0] === $compare_domains[0]) {
echo '<br>true: ';
echo '$csv_domains[0] ('.$csv_domains[0].') = $compare_domains[0] ('.$compare_domains[0].')';
} else {
echo '<br>false: ';
echo '$csv_domains[0] ('.$csv_domains[0].') != $compare_domains[0] ('.$compare_domains[0].')';
}
echo '<br><hr><br>';
if ($csv_domains[1] === $compare_domains[1]) {
echo '<br>true: ';
echo '$csv_domains[1] ('.$csv_domains[1].') = $compare_domains[1] ('.$compare_domains[1].')';
} else {
echo '<br>false: ';
echo '$csv_domains[1] ('.$csv_domains[1].') != $compare_domains[1] ('.$compare_domains[1].')';
}
echo '<br><hr><br>';
if ($csv_domains[2] === $compare_domains[2]) {
echo '<br>true: ';
echo '$csv_domains[2] ('.$csv_domains[2].') = $compare_domains[0] ('.$compare_domains[2].')';
} else {
echo '<br>false: ';
echo '$csv_domains[2] ('.$csv_domains[2].') != $compare_domains[0] ('.$compare_domains[2].')';
}
Result:
So how do I fix this?
EDIT: var_dump returns two different values:
var_dump($csv_domains[0]); // string(11) "test.com"
var_dump($compare_domains[0]); // string(8) "test.com"
Thanks to #ChrisHaas, I was able to fix it by changing the for loop and removing the BOM from the first cell:
$count = 0;
foreach($rows as $row) {
if ($count == 0) {
$new_row = str_replace("\xEF\xBB\xBF",'', $row[0]);
} else {
$new_row = $row[0];
}
$csv[] = $new_row;
$count++;
}

Removing by ID from a .json file with PHP

I am having a hard time removing a record in my .json file by id.
So my original JSON looks like this:
[["{\"id\":1474753066818,\"name\":\"dd\",\"brand\":\"dd\",\"price\":\"12\"}"],["{\"id\":1474753069035,\"name\":\"dd3\",\"brand\":\"dd\",\"price\":\"12\"}"]]
And this is my php:
<?php
$string = file_get_contents("products.json");
//var_dump($string);
$input = json_decode($string, true);
$output = array();
//here starts the problem
foreach($input as $element) {
if($_GET['data'] != $element[0]["id"]){ //add in array if id doesn't match
$output[] = $element;
}
}
$final = json_encode($output);
$f = #fopen("products.json", "r+");
if ($f !== false) {
ftruncate($f, 0);
fclose($f);
}
file_put_contents("products.json", $final);
>
Basically my problem starts in the foreach where I am iterating through something that looks like this :
array(2) { [0]=> array(1) { [0]=> string(58) "{"id":1474753066818,"name":"dd","brand":"dd","price":"12"}" } [1]=> array(1) { [0]=> string(59) "{"id":1474753069035,"name":"dd3","brand":"dd","price":"12"}" } }
Obviously here I am not able to access the id like I am trying to do in the foreach, because the whole thing is a string.
I have no idea how can I turn this string to an array, compare the id's and then encode it back to my original json format shown at the begining of this post.
Please help!
Your products.json file is a little strange, see below:
[
[
"{\"id\":1474753066818,\"name\":\"dd\",\"brand\":\"dd\",\"price\":\"12\"}"
],
[
"{\"id\":1474753069035,\"name\":\"dd3\",\"brand\":\"dd\",\"price\":\"12\"}"
]
]
Your products.json contains an array of arrays with 1 element whose value is a json encoded string. It looks like you will have to call json_decode again on the string contents to get what you're after.
foreach($input as $element) {
$product = json_decode($element[0]);
if($_GET['data'] != $product["id"]){
//add in array if id doesn't match
$output[] = $product ;
}
}
It's also important to note that the rest of your code:
$final = json_encode($output);
$f = #fopen("products.json", "r+");
if ($f !== false) {
ftruncate($f, 0);
fclose($f);
}
file_put_contents("products.json", $final);
Will not ending up saving the products.json in the same way that you are currently reading it. With the code I provided above it will probably end up looking like this:
[
{"id":1474753066818,"name":"dd","brand":"dd","price":"12"},
{"id":1474753069035,"name":"dd3","brand":"dd","price":"12"}
]

How to use php array+string in html?

I am using PL/pgsql RETURNS TABLE to get the below output using pg_fetch_all in PHP
array(4) {
[0]=> array(1)
{ ["not_actual_values"]=> string(88) "("var1","var2",var3,var4,date1,int,int,int,var5,int,int,int,int)" }
[1]=> array(1)
{ ["not_actual_values"]=> string(89) "("var1","var2",var3,var4,date1,int,int,int,var5,int,int,int,int)" }
[2]=> array(1)
{ ["not_actual_values"]=> string(88) "("var1","var2",var3,var4,date1,int,int,int,var5,int,int,int,int)" }
[3]=> array(1)
{ ["not_actual_values"]=> string(89) "("var1","var2",var3,var4,date1,int,int,int,var5,int,int,int,int)" }
}
I am unable to use the above output in HTML. I tried using php explode but it didnt work I got zero array. Also, What confuses me I am getting quotes in first two variable and not in others.
Update
I used below function but I got zero array
function pgArrayToPhp($text) {
if(is_null($text)) {
return array();
} else if(is_string($text) && $text != '{}') {
$text = substr($text, 1, -1);// Removes starting "{" and ending "}"
if(substr($text, 0, 1) == '"') {
$text = substr($text, 1);
}
if(substr($text, -1, 1) == '"') {
$text = substr($text, 0, -1);
}
// If double quotes are present, we know we're working with a string.
if(strstr($text, '"')) { // Assuming string array.
$values = explode('","', $text);
} else { // Assuming Integer array.
$values = explode(',', $text);
}
$fixed_values = array();
foreach($values as $value) {
$value = str_replace('\\"', '"', $value);
$fixed_values[] = $value;
}
return $fixed_values;
} else {
return array();
}
}
How can i do this ?
Your inner values are arrays, not strings, you need to take that into account:
function pgArraytoPhp($array) {
foreach ($array as $row) { // $row is an inner array here!
actuallyDoParsing($row[0]); // Parse row's first element,
// which will be the string you want.
}
}
Depending on what you need, you'll return the accumulated result of all of those actuallyDoParsing() calls.
This is the answer that I have come up with. Though it is not neat but it seems to be working as expected now
$fetch=pg_fetch_all($query); //fetching data from postgresql
$count=count($fetch);
for ($j=0;$j<$count;$j++) {
$a = $fetch[$j]['not_actual_values'];
$b = array(explode("," , $a));
$count2=count($b[0]);
foreach ($b as $f) { echo '<tr>';
for ($i=0;$i<$count2;$i++){
echo '<td><input type="text" readonly value='.$f[$i].'>';
} } }
But now the problem is raw data contains noise as in "(" so that is spoiling the output.
Thank you all for your efforts.

Parsing a weird malformed ini

I have a really broken/weird INI file that looks like this.
RowID=11668
Name=SCNA DaCe
PPA
Relation=Family
RowID=31999
Name=PCA
RowID=11593
Name=CRMLEVEL
Relation=Family
If possible, end up as
array("11668" => array("name"=> "SCNA DaCe", "relation"=>"Family", "ppa"=>true));
Linebreaks are separated based on RowID instead of a proper [section] and I have no idea how I can read this, any tips on where to start?
There is inconsistent casing, certain things don't have values (like PPA by itself on a line), not all key=vals are defined for each case.
For something like this, an array of objects comes in handy.
http://www.laprbass.com/RAY_temp_dylan.php
<?php // RAY_temp_dylan.php
error_reporting(E_ALL);
echo '<pre>';
$str = <<<END
RowID=11668
Name=SCNA DaCe
PPA
Relation=Family
RowID=31999
Name=PCA
RowID=11593
Name=CRMLEVEL
Relation=Family
END;
// SIMULATE READING WITH file()
$arr = explode(PHP_EOL, $str);
// COLLECT THE NORMALIZED DATA HERE
$out = array();
$obj = new stdClass;
// USE AN ITERATOR ON EACH ROW
foreach ($arr as $row)
{
// SKIP BLANK LINES
$row = trim($row);
if (empty($row)) continue;
// FOR EACH ROWID CREATE A NEW OBJECT
if (FALSE !== strpos($row, 'RowID'))
{
// SAVE THE OLD OBJECT
$out[] = $obj;
$obj = new stdClass;
$obj->RowID = end(explode('=', $row));
}
// FOR REMAINING ELEMENTS THAT ARE KEY-VALUE PAIRS
if (FALSE !== strpos($row, '='))
{
$key = current(explode('=', $row));
$val = end(explode('=', $row));
$obj->$key = $val;
}
// FOR REMAINING ELEMENTS THAT ARE NOT KEY-VALUE PAIRS
else
{
$obj->$row = TRUE;
}
}
// SAVE LAST ELEMENT AT EOF
$out[] = $obj;
// DISCARD THE ZERO "STARTER" ELEMENT
unset($out[0]);
var_dump($out);
You would have to roll your own custom solution to parse this INI file as PHP's built in parse_ini_file is based on PHP's own php.ini format constraints. So it has to be valid PHP.
For your purposes if the requirements are as simple as key/value pairs on each line with the exception of the one-off stranded value as a default boolean true then you could do something like the following.
function my_parse_ini($ini_file_name) {
$ini = file($ini_file_name, FILE_IGNORE_NEW_LINES);
$return = array();
$row = null;
foreach ($ini as $key => $value) {
if ($value == '') {
$row = null;
continue;
}
#list($k, $v) = explode('=',$value);
if ($v === null) {
$v = true;
}
if ($row === null) {
$row = $v;
$return[$row] = array();
continue;
}
$return[$row][$k] = $v;
}
return $return;
}
/* usage */
var_dump(my_parse_ini('test.ini'));
This would output the following for your sample ini file....
array(2) {
[11668]=>
array(4) {
["Name"]=>
string(3) "PCA"
["PPA"]=>
bool(true)
["Relation"]=>
string(6) "Family"
["RowID"]=>
string(5) "31999"
}
[11593]=>
array(2) {
["Name"]=>
string(8) "CRMLEVEL"
["Relation"]=>
string(6) "Family"
}
}

Reading text file and comparing line with the exact same line returns false

My current code:
$file = fopen("countries.txt","r");
$array = array();
while(!feof($file)) {
$array[] = fgets($file);
}
fclose($file);
Here is my foreach loop:
$str = "test";
foreach ($array as $key => $val) {
if ($val == $str) {
echo $val;
} else {
echo "not found";
}
}
I am wondering why it is only printing $val if it is the last value of the array.
For example, it works if the txt file looks like this
test1
test2
test3
test
but doesn't work if it looks like this
test1
test2
test
test3
The problem is, that you have a new line character at the end of each line, so:
test\n !== test
//^^ See here
That's why it doesn't work as you expect it to.
How to solve it now? Can I introduce to you the function: file(). You can read a file into an array and set the flag to ignore these new lines at the end of each line.
So putting all this information together you will get this code:
$array = file("countries.txt", FILE_IGNORE_NEW_LINES);
$str = "test";
foreach ($array as $key => $val) {
if ($val == $str) {
echo $val;
} else {
echo "not found";
}
}
When you compare strings - you should always use '==='.

Categories