Building an array from a file with multiple data points - php

I am storing all of my items into lines in a text file like so, with the ID at the start
1/item name/content/etc
2/item name/content/etc
3/item name/content/etc
4/item name/content/etc
This method of storage is for a simple script we are using on a game that is editable by the different users so security really isn't a big deal, or else we would just use mysqli and the like.
I need to be able to print out variable information for a single entry, found by the id at the beginning, and also print say, 3 and 2, in numerical order. So far I am loading them into an array like so
$content = file('script/items.txt');
$items = array($content);
it throws the whole file into an array like this
array(1) {
[0]=> array(4) {
[0]=> string(23) "1/item name/content/etc"
[1]=> string(23) "2/item name/content/etc"
[2]=> string(23) "3/item name/content/etc"
[3]=> string(23) "4/item name/content/etc"
}
}
is there an easier way to pick one, or even multiple using just their id so i don't have to store the whole file into the array (it could get big in the future) or is the best way to store them all, loop through the entire array one line at a time, explode them for each /, and them compare is it's in the required items? Thanks.

You need to use file() flags FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES
Do like below:-
$content = file('script/items.txt',FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
print_r($content);
Note:-
Now loop over the array to get records one-by-one.
Best approach to save data to database table with columns like id,item_name,contents and extra

You can try the below code to read file line by line.
$handle = fopen("script/items.txt", "r");
if ($handle) {
while (!feof($handle)) {
$line = fgets($handle)
// do your stuffs.
}
fclose($handle);
} else {
// error opening the file.
}
It will not load complete file in memory so you can use this for bigger files as well.
feof($handle) It will save you in case you have a line which contains a boolean false.

You could also use array_reduce to store the ID as key into the array.
$content = file('script/items.txt',FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
$content = array_reduce($content, function ($result, $line) {
list($id, $data) = explode('/', $line, 2) ;
$result[$id] = $data;
return $result;
}, array());
print_r($content);
Outputs:
Array
(
[1] => item name/content/etc
[2] => item name/content/etc
[3] => item name/content/etc
[4] => item name/content/etc
)
So you can get elements using without using a loop:
$content[$wanted_id]

Related

warning: fputcsv() expects parameter 2 to be array, boolean. when writing to new csv file. php

I have a CSV file called employee_data.csv. It contains employee records formatted like so:
JANE WILLIAMS,6/8/1998,55846874E,4323
PETER JONES,15/01/1982,56897547Q,1234
JAMES O'BRIEN,09/05/2001,25689514W,3432
I want to delete a selected row within the csv file. To achieve this, I will simply copy the 2 rows within the csv file that I do not want to delete to a new_employee_data.csv file and delete the old one.
<?php
$dataSrc = "persistence/employee_data.csv";
$dataDest = "persistence/new_employee_data.csv";
$dataFile = fopen($dataSrc, "r") or die("Unable to open file!");
$outFile = fopen($dataDest, "w") or die("Unable to open file!");
$i=0; //index for the array
while(!feof($dataFile)) {
$csv = fgetcsv($dataFile); //read a line from the CSV file
//$csv = [ANE WILLIAMS,6/8/1998,55846874E,4323];
//add check to remove row
print_r($csv);
if($csv[2] == '55846874E') continue; //skip to next itteration
fputcsv($outFile, $csv);
}
fclose($dataFile);
fclose($outFile);
?>
The code above takes the contents of $dataFile and writes it to $outFile line by line, if the 3rd column = '55846874E' it will skip writing that line. The csv array contains the rows within the employee_data.csv file.
The elements in the $csv array are as follows.
Array ( [0] => JANE WILLIAMS [1] => 6/8/1998 [2] => 55846874E [3] => 4321 )
Array ( [0] => PETER JONES [1] => 15/01/1982 [2] => 56897547Q [3] => 1234 )
Array ( [0] => JAMES O'BRIEN [1] => 09/05/2001 [2] => 25689514W [3] => 8475 )
It removes the first row of the file - JANE WILLIAMS,6/8/1998,55846874E,4323
Now in the new_employee_data.csv is the two undeleted records.
"PETER JONES",15/01/1982,56897547Q,1234
"JAMES O'BRIEN",09/05/2001,25689514W,8475
This is exactly what I want it to do however I received this warning when I run it in the browser:
fputcsv() expects parameter 2 to be array, boolean given in line 25
It's having a problem with fputcsv($outFile, $csv); and I've no idea why, any suggestions of how to fix this?
I would change the while loop So instead of
while(!feof($dataFile)) {
$csv = fgetcsv($dataFile);
like this
while(false !== ($csv = fgetcsv($dataFile))){
You can see an example of this usage on the PHP website here
What probably happens is there is a extra return at the end of the file, so the feof doesn't catch it, and then you get boolean false for the fgetcsv. For example like this (where \n is a new line):
JANE WILLIAMS,6/8/1998,55846874E,4323\n
PETER JONES,15/01/1982,56897547Q,1234\n
JAMES O'BRIEN,09/05/2001,25689514W,3432\n
\n
\eof
So we can combine these (so you wont need the line under the while loop) and just get the data from the same place we do the loop condition, this way when it returns false it will just drop the loop. It's important to be careful with the number of = in this as a single one is assignment and the !== is strict type comparison. So we can break this down a bit and in English what this says.
pull a row and process it with fgetcsv setting $csv to it's value, parentheses get priority
if $csv is boolean false then the loop condition is false an it ends. So basically if false(boolean) not equal to the result of fgetcsv($dataFile) then it's true otherwise it's false.
It would work basically the same like this
while($csv = fgetcsv($dataFile)){
I tend to like the long hand version, because it's easier to see that we are assigning and not comparing. For example you could glance at the above version and then think it should be == instead of = so the first version just makes that a bit more obvious. Putting the false on the left hand side is basically done for the same reason (and because it's essentially a constant, so putting it on the left avoids mistakes like below false = $csv wont work).
Misplacing an = in a condition can actually be one of the harder bugs to figure out, because it's completely legal. So that is kind of a "pro tip" to put function calls and constants on the left when doing comparison.
hope that helps!

PHP - strtok(), associative array relationship

I'm pretty familiar with the Strtok() function in PHP, and I have had no problem getting the function to work properly for strings in the past. However, I currently have to read a .csv text file (which I've done successfully) where each line is made of 6 fields like so: last name, first name, address, city, district, postal code\r\n <--carriage return and linefeed at the end
I have to use Strok() to split these by the delimiters and token the words as fields (i.e. last, first, address, etc.). I plan to use an associative array using the last name as the primary key so that I can plug the data into an HTML Table, which is created and working. My issue right now is splitting the file correctly, as it has about 200 lines made of those 6 fields, and storing the strings as fields properly for an array, so the data structure is where I'm having some issues. Here's what I have so far:
$inputFile = fopen("input.csv","r");
$delimiters = ",";
$token = strtok($inputFile, $delimiters);
$n=1;
while ($token){
echo "Token $n: $token <br>";
$token = strtok($delimiters);
$n++;
}
Obviously, the table is created below it but since I haven't done the data structure quite yet, I don't have the fields for it. I think my token loop may be incorrect for this issue, but I pulled some from an earlier example in my book and an exercise I did where my token process worked but the file structure was different. Thanks for any direction or help on this.
There are CSV functions in PHP, like fgetcsv, so it really is the wrong approach to reinvent the wheel.
Note that in your code you don't actually read the content of the file, as you only get a file pointer.
If you really need to do this with strtok, and your CSV is simple, in the sense that it does not have quoted strings, which could have embedded delimiter characters, you could use:
file_get_contents() to read the file content in one string. Of course, file() would make it easier for you, as it would already split lines. But I assume that if CSV functions are not allowable for you, then this will neither.
strtok for getting the fields, but at the end of the loop, not at the start, since the initial call with the double arguments already retrieves the first value before the loop.
Code:
$input = file_get_contents("input.csv");
$delimiters = ",\n\r";
$token = strtok($input, $delimiters);
$result = [];
$row = [];
while ($token){
echo "Token $token <br>";
$row[] = $token;
if (count($row) == 6) { // write record
$result[] = $row;
$row = [];
}
$token = str_replace('\r', '', strtok($delimiters));
}
print_r($result);
Note that this does not create an associative array. If you need that, then use this code:
$columns = ['last', 'first', 'address1', 'address2', 'address3', 'zip'];
and then in your loop, replace $row[] = $token by:
$row[$columns[count($row)]] = $token;
You can see that version run on eval.in. The output for the data you provided in comments is:
Array (
[0] => Array (
[last] => SELBY
[first] => AARON
[address1] => 1519 Santiago de los Caballeros Loop
[address2] => Mwene-Ditu
[address3] => East Kasai
[zip] => 22025
)
[1] => Array (
[last] => GOOCH
[first] => ADAM
[address1] => 230 Urawa Drive
[address2] => Adoni
[address3] => Andhra Pradesh
[zip] => 2738
)
)
Again, this is not advisable. You should use fgetcsv. That also deals better with strings that could have commas, double quotes or even newlines in them.
Well, I was going to skip this question because fgetcsv(), but I was bored:
$lines = file($inputFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
$delimiters = ",";
foreach($lines as $line) {
$values = array(strtok($line, $delimiters));
while($token = strtok($delimiters)){
$values[] = $token;
}
$result[] = $values;
}
Read the file lines into an array
Loop to get each line and put the first token of the line into a values array
Loop the line and get all tokens and add to values array
Add values array to result array
I added an array_combine() because you said something about an associative array. You can use something like this if needed:
$result[] = array_combine(array('last name',
'first name',
'address',
'city',
'district',
'postal code'), $values);
If you wanted last name to be the key for each result line, which is not advisable as keys are unique and I don't think you can guarantee last names being unique:
$result[$values[0]] = $values;
//or to remove it from the array but use as the key
$result[array_unshift($values)] = $values;

How to correctly parse .ini file with PHP

I am parsing .ini file which looks like this (structure is same just much longer)
It is always 3 lines for one vehicle. Each lines have left site and right site. While left site is always same right site is changing.
00code42=52
00name42=Q7 03/06-05/12 (4L) [V] [S] [3D] [IRE] [52]
00type42=Car
00code43=5F
00name43=Q7 od 06/12 (4L) [V] [S] [3D] [5F]
00type43=Car
What I am doing with it is:
$ini = parse_ini_file('files/models.ini', false, INI_SCANNER_RAW);
foreach($ini as $code => $name)
{
//some code here
}
Each value for each car is somehow important for me and I can get to each it but really specifily and I need your help to find correct logic.
What I need to get:
mCode (from first car it is 00)
code (from first car it is 52)
vehicle (from first car it is Q7 03/06-05/12 (4L))
values from [] (for first car it is V, S, 3D, IRE , 52
vehicle type ( for first car it is "car")
How I get code from right site:
$mcode = substr($code, 0, 2); //$code comes from foreach
echo "MCode:".$mcode;
How I get vehicle type:
echo $name; // $name from foreach
How I parse values like vehicle and values from brackets:
$arr = preg_split('/\h*[][]/', $name, -1, PREG_SPLIT_NO_EMPTY); // $name comes from foreach
array(6) { [0]=> string(19) "Q7 03/06-05/12 (4L)" [1]=> string(1) "V" [2]=> string(1) "S" [3]=> string(2) "3D" [4]=> string(3) "IRE" [5]=> string(2) "52" }
So basicly I can get to each value I need just not sure how to write logic for work with it.
In general I can skip the first line of each car because all values from there is in another lines as well
I need just 2th and 3th line but how can I skip lines like this? (was thinking to do something like :
if($number % 3 == 0) but I dont know how number of lines.
After I get all data I cant just echo it somewhere but I also need to store it in DB so how can I do this if
I will really appriciate your help to find me correct way how to get this data in right cycle and then call function to insert them all to DB.
EDIT:
I was thinking about something like:
http://pastebin.com/C97cx6s0
But this is just structure which not working
If your data is consistent, use array_chunk, array_keys, and array_values
foreach(array_chunk($ini, 3, true) as $data)
{
// $data is an array of just the 3 that are related
$mcode = substr(array_keys($data)[0], 0, 2);
$nameLine = array_values($data)[1];
$typeLine = array_values($data)[2];
//.. parse the name and type lines here.
//.. add to DB
}

php - push array into an array -(pushing both key and the array)

I am trying to add an array to an existing array. I am able to add the array using the array_push . The only problem is that when trying to add array that contains an array keys, it adds an extra array within the existing array.
It might be best if I show to you
foreach ($fields as $f)
{
if ($f == 'Thumbnail')
{
$thumnail = array('Thumbnail' => Assets::getProductThumbnail($row['id'] );
array_push($newrow, $thumnail);
}
else
{
$newrow[$f] = $row[$f];
}
}
The fields array above is part of an array that has been dynamically fed from an SQl query it is then fed into a new array called $newrow. However, to this $newrow array, I need to add the thumbnail array fields .
Below is the output ( using var_dump) from the above code. The only problem with the code is that I don't want to create a seperate array within the arrays. I just need it to be added to the array.
array(4) { ["Product ID"]=> string(7) "1007520"
["SKU"]=> string(5) "G1505"
["Name"]=> string(22) "150mm Oval Scale Ruler"
array(1) { ["Thumbnail"]=> string(77) "thumbnails/products/5036228.jpg" } }
I would really appreciate any advice.
All you really want is:
$newrow['Thumbnail'] = Assets::getProductThumbnail($row['id']);
You can use array_merge function
$newrow = array_merge($newrow, $thumnail);
Alternatively, you can also assign it directly to $newrow:
if ($f == 'Thumbnail')
$newrow[$f] = Assets::getProductThumbnail($row['id']);
else
...
Or if you want your code to be shorter:
foreach($fields as $f)
$newrow[$f] = ($f == 'Thumbnail')? Assets::getProductThumbnail($row['id']) : $row[$f];
But if you're getting paid by number of lines in your code, don't do this, stay on your code :) j/k

Array Sorting Question for News System

I'm currently stuck trying to figure out how to sort my array files. I have a simple news posting system that stores the content in seperate .dat files and then stores them in an array. I numbered the files so that my array can sort them from lowest number to greatest; however, I have run into a small problem. To begin here is some more information on my system so that you can understand it better.
The function that gathers my files is:
function getNewsList() {
$fileList = array();
// Open the actual directory
if($handle = opendir(ABSPATH . ADMIN . "data")) {
// Read all file from the actual directory
while($file = readdir($handle)) {
if(!is_dir($file)) {
$fileList[] = $file;
}
}
}
// Return the array.
return $fileList;
}
On a seperate file is the programming that processes the news post. I didn't post that code for simplicity's sake but I will explain how the files are named. The files are numbered and the part of the post's title is used... for the numbering I get a count of the array and add "1" as an offset. I get the title of the post, encode it to make it file-name-friendly and limit the amount of text so by the end of it all I end up with:
// Make the variable that names the file that will contain
// the post.
$filename = "00{$newnumrows}_{$snipEncode}";
When running print_r on the above function I get:
Array (
[0] => 0010_Mira_mi_Soledad.dat
[1] => 0011_WOah.dat
[2] => 0012_Sinep.dat
[3] => 0013_Living_in_Warfa.dat
[4] => 0014_Hello.dat
[5] => 001_AS.dat
[6] => 002_ASASA.dat
[7] => 003_SSASAS.dat
...
[13] => 009_ASADADASADAFDAF.dat
)
And this is how my content is displayed. For some reason according to the array sorting 0010 comes before 001...? Is there a way I can get my array to sort 001 before 0010?
You can use natcasesort(array) function of php which will sort an array using a "natural order" algorithm and you will get the desired output
HTH.
:Malay
Take the filename and extract the prefix number as integer number:
// $filename has the format: "00{$newnumrows}_{$snipEncode}"
function generateSortKey($filename)
{
$separatorPos = stripos($filename, '_');
$prefix = trim(substr($filename, 0, $separatorPos));
return intval($prefix);
}
Than create an associative array from the list of files, the keys will be used as sortable value later:
function toSortableArray($files)
{
$result = array();
foreach ($files as $filename)
{
$key = generateSortKey($filename);
$value = $filename;
$result[$key] = $value;
}
return $result;
}
and at last use krsort():
$list = getNewsList();
$sortableList = toSortableArray($list);
krsort($sortableList); // after that $sortableList is
// sorted by key in descending order now
FIX: ksort() => krsort()
The issue is with underscore. Always numerical characters get sorted before underscore.
See whether you get the desired result using sort($your_array, SORT_NUMERIC).
For more info, refer PHP Manual for sort
You may also use natcasesort() (as Malay suggested) or natsort(). But both maintain index association.

Categories