I want to parse a comma separated value string into an array. I want to try the str_getcsv() php function but I can't find any good examples on how to use it.
For example I have an input where users submit tags for programming languages (php, js, jquery, etc), like the "tags" input in stackoverflow when you submit a question.
How would I turn an input with example value="php, js, jquery" into an array using str_getcsv?
Its true that the spec at http://us3.php.net/manual/en/function.str-getcsv.php doesn't include a standard example, but the user-submitted notes do a decent job of covering it. If this is a form input:
$val = $_POST['value'];
$data = str_getcsv($val);
$data is now an array with the values. Try it and see if you have any other issues.
I think you should maybe look at explode for this task.
$values = "php, js, jquery";
$items = explode(",", $values);
// Would give you an array:
echo $items[0]; // would be php
echo $items[1]; // would be js
echo $items[2]; // would be jquery
This would probably more efficient than str_getcsv();
Note that you would need to use trim() to remove possible whitespace befores and after item values.
UPDATE:
I hadn't seen str_getcsv before, but read this quote on the manpage that would make it seem a worthwhile candidate:
Why not use explode() instead of str_getcsv() to parse rows?
Because explode() would not treat possible enclosured parts of
string or escaped characters correctly.
For simplicity and readability, I typically find myself using just explode(), only adding in str_getcsv() if the following two conditions are met: 1) the primary delimiter is also used within the data itself; 2) the token that I'm trying to use as the main delimiter is enclosed by another distinct character.
For example, a basic parser for a CSV file:
$filename = $argv[1];
if (empty($filename)) { echo "Input file required\n"; exit; }
$AccountsArray = explode("\n", file_get_contents($filename));
As long as each of the elements of $AccountsArray doesn't embed a "," within the data itself, this will work perfectly and is straightforward and easy to follow:
foreach ($AccountsArray as $entry) {
$acctArr = explode(",", $entry);
}
However, often the data will contain the delimiter, at which point an enclosing token (a " in this example) has to be present. If so, then I switch to str_getcsv() like so:
foreach ($AccountsArray as $entry) {
$acctArr = str_getcsv($entry, ",", "\"");
}
//get the csv
$csvFile = file_get_contents('test.csv');
//separate each line
$csv = explode("\n",$csvFile);
foreach ($csv as $csvLine) {
//separet each fields
$linkToInsert = explode(",",$csvLine);
//echo what you need
//$linkToInsert[0] would be the first field, $linkToInsert[1] second field, etc
echo '• ' . $linkToInsert[1] . '<br>';
}
The code is quite simple, using the Str_getcsv function, we will go
through each line of the CSV file "images.csv" that is located in the
same directory as our script.
NOTE: Functions used are compatible with versions of PHP >= 5.3.0
//First, reading the CSV file
$csvFile = file('file.csv');
foreach ($csvFile as $line) {
$url = str_getcsv($line);
$ch = curl_init($url[0]);
$name = basename($url[0]);
if (!file_exists('directory/' . $name)) {
$fp = fopen('directory/' . $name, 'wb');
}
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_exec($ch);
curl_close($ch);
fclose($fp);
}
Related
Is there a native function or solid class/library for writing an array as a line in a CSV file without enclosures? fputcsv will default to " if nothing is passed in for the enclosure param. Google is failing me (returning results for a whole bunch of pages about fputcsv), and PEAR's libraries do more or less the same things as fputcsv.
Something that works exactly like fputcsv, but will allow the fields to remain unquoted.
currently: "field 1","field 2",field3hasNoSpaces
desired: field 1,field 2,field3hasNoSpaces
The warnings about foregoing enclosures are valid, but you've said they don't apply to your use-case.
I'm wondering why you can't just use something like this?
<?php
$fields = array(
"field 1","field 2","field3hasNoSpaces"
);
fputs(STDOUT, implode(',', $fields)."\n");
works with chr() function:
fputcsv($f,$array,',',chr(0));
fputcsv($file, $data, ';', chr(127));
Well car(0) didn't work out as the NULL value will most likely choke most csv parsers.
I ended using fputcsv() to build the initial file, then went through and removed all quotes. Elegant? Maybe not, but it got the job done :).
<?php
$filename = "sample.csv";
$handle = fopen($filename, 'w+');
fputcsv($handle, ['column 1','column 2']);
$data = ['sample','data'];
fputs($handle, implode(',', $data)."\n");
// or
fwrite($handle, implode(',', $data)."\n");
fclose($handle);
$headers = array(
'Content-Type' => 'text/csv',
);
Doesn't this work?
fputcsv($fp, split(',', $line),',',' ');
This is what I use to put standard CSV into an array...
function csv_explode($delim=',', $str, $enclose='"', $preserve=false){
$resArr = array();
$n = 0;
$expEncArr = explode($enclose, $str);
foreach($expEncArr as $EncItem){
if($n++%2){
array_push($resArr, array_pop($resArr) . ($preserve?$enclose:'') . $EncItem.($preserve?$enclose:''));
}else{
$expDelArr = explode($delim, $EncItem);
array_push($resArr, array_pop($resArr) . array_shift($expDelArr));
$resArr = array_merge($resArr, $expDelArr);
}
}
return $resArr;
}
You can then output whatever you want in a foreach loop.
The downside with a CSV file with no enclosures means an errant comma in user input will munge the row. So you'll need to remove commas before writing a CSV row.
The tricky part with handling CSV is parsing enclosures, which makes the PHP & PEAR CSV functions valuable. Essentially you're looking for a file that is comma-delimited for columns and newline-delimited for rows. Here's a simple starting point:
<?php
$col_separator= ',';
$row_separator= "\n";
$a= array(
array('my', 'values', 'are', 'awes,breakit,ome'),
array('these', 'values', 'also', "rock\nAND\nROLL")
);
function encodeRow(array $a) {
global $col_separator;
global $row_separator;
// Can't have the separators in the column data!
$a2= array();
foreach ($a as $v) {
$a2[]= str_replace(array($col_separator, $row_separator), '', $v);
}
return implode($col_separator, $a2);
}
$output= array();
foreach ($a as $row) {
$output[]= encodeRow($row);
}
echo(implode($row_separator, $output));
?>
I use tricky way to remove double quote, but only in Linux
....
fputcsv($fp, $product_data,"\t");
....
shell_exec('sed -i \'s/"//g\' /path/to/your-file.txt ');
Whats wrong with good old fwrite()?
function encloseString($field){
return ($field) ? '"' . $field . '"' : null;
}
$delimiter = ';';
$data = ["data_field_1", "data_field_2", "data_field_3"];
$fp = fopen("some-file.csv", 'w');
for($i = 0;$i<100000;$i++) {
fwrite($fp, implode($delimiter, array_map('encloseString', $data) . "\n");
}
fclose($fp);
(Obviously you need to make sure the data in $data is escaped first)
Chose the solution depends on your application. For some case own code for csv is needed. In case 1 (See result chr0), case 2 (chr127) and case 3 (chr127) the data will be modified(bad thing). Instead use something like this, thanks #oops:
<?php
$fields = array("field 1","field 2","field3hasNoSpaces");
fputs(STDOUT, implode(',', $fields)."\n");
case 1. fputcsv($f, $array, $delimiter, car(0)) See result chr0
case 2. fputcsv($f, $array, $delimiter, car(127))
chr127
case 3. fputcsv($f, $array, $delimiter, ' ')
onespace
case 4. Own code (s.above inspired by
oops or
Ansyori or
zeros-and-ones ) produces
better results.
This is an old question but as I was struggling with this as well, I thought it would be good to let anyone who is looking for something like this know than you can just pass empty string in the enclosure param of fputcsv and it will not use any enclosure at all.
i.e.
fputcsv($file, $array, ',', '');
chr(0) also worked for me:
fputcsv($fp, $aLine, $sDelimiter, chr(0));
Figured it out. By passing in the ascii code for Null to the car() function it seems to work just fine.
fputcsv($f, $array, $delimiter, car(0))
Thanks for the answers everyone!!!
This question already has answers here:
How to replace new lines by regular expressions
(2 answers)
Closed 9 years ago.
I am trying to replace any newline characters that are with a quoted string e.g.
$help = '"Hi this is a string and I really want to replace
any newlines that are within that string" "There are multiple strings all within one string that all need
to have their newlines replaces"';
I have tried all sorts. The problem is I can't get rid of the line endings themselves. Otherwise the fgetcsv function returns a single array. It needs to be line endings / newlines within the quotes.
$str = str_replace(PHP_EOL, '', $str);
Okay here's my code. Download the csv file.
<?php
$username = 'username';
$password = 'password';
$loginURL = 'http://www.example.com/login';
$contentURL = 'http://www.example.com/feedback.csv';
// Initialize the curl
$ch = curl_init();
// Pass the curl some options
curl_setopt($ch, CURLOPT_URL, $loginURL);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, 'inp-email=' . $username . '&inp-pass=' . $password);
curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookie.txt');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
// Execute the curl to login
$store = curl_exec($ch);
// Change the URL to the CSV and execute
curl_setopt($ch, CURLOPT_URL, $contentURL);
$content = curl_exec($ch);
// Time to sanitise, first I want to remove any newlines from customers comments
$content = '\"' .implode('"', explode(PHP_EOL, $content)) . '\"';
// Return the file contents
file_put_contents('feedback.csv', $content)
And then the file that grabs the CSV file and prints it out at the moment...
<?php
// Function to loop through CSV and build up array
function readCSV($csvFile){
$file_handle = fopen($csvFile, 'r');
while (!feof($file_handle) ) {
$csvlines[] = fgetcsv($file_handle, 0, "\t");
}
fclose($file_handle);
return $csvlines;
}
// Set path to CSV file
$csvFile = 'feedback.csv';
// Read the CSV file and build array using readCSV function
$csv = readCSV($csvFile);
echo '<pre>';
foreach($csv as $line){
if(count($line) != 16){
print_r($line);
}
}
echo '</pre>';
So to reiterate I am trying to go from this:
$str = '"this string has no new lines" "but this one does have new
lines to strip out"';
to:
$str = '"this string has no new lines" "but this one does have new lines to strip out"';
Here's one possible approach to solve the problem given in the original question (demo): one can remove all the newlines within the double quoted strings (but only those!) by...
preg_replace('#\\n(?=[^"]*"[^"]*(?:"[^"]*"[^"]*)*$)#' , ' ', $help);
The core idea is very simple: for each end of line symbol, we make sure that it's followed by (DQM = ")...
any number of non-DQM symbols, then...
exactly one DQM, then...
any number of non-DQM, then...
any number of single DQM - any number of non-DQM - single DQM - any number of non-DQM combos, then...
the end of string.
For properly formed string, this will result in collecting endlines lying in between double quotation marks, as asked.
There's a caveat to this approach, though. Obviously we won't be able to correct the line if it has an odd number of DQMs (even more, it will work incorrectly in this case). That's easy to check, just count the DQMs in the string. BTW, desired behaviour is a bit unclearly for such strings:
"should "we
replace" endline here
?
In theory, it still can be fixed a bit, by using look-behind instead of look-ahead, something like this...
preg_replace('#(?<=^(?:[^"]*"[^"]*")*[^"]*"[^"]*)\\n#' , ' ', $help);
... but in practice, one can't (still) use look-behind expressions of variable length in PHP. So you have to resort to parsing this string in this case.
If this consideration is not relevant in your case, though, the approach shown might be helpful, I suppose.
Try this:
$str = implode('', explode(PHP_EOL, $str));
If it's not working, try to hardcode the PHP_EOL constant:
$str = implode('', explode("\r\n", $str));
If it is still not working, try to treat your CSV file here:
foreach($csv as $line){
if(count($line) != 16){
print_r(implode('', explode("\n", $line)));
}
}
This is my first post on the internet for some assistance with coding so please bear with me!
I have been finding open code on the internet for a few years and modding it to do what I want but I seem to have come up against a wall with this one that I am sure is very simple. If you would please be able to help me it would be very much appreciated.
I have the following page:
<?php
$text = $_REQUEST['message'];
$f = file_get_contents("all.txt");
$f = explode(", ", $f);
function modFile($pos, $tothis, $inthis)
{
foreach($inthis as $pos => $a){
}
$newarr = implode("\r\n", $inthis);
$fh = fopen("example.txt", "w");
fwrite($fh, $newarr);
fclose($fh);
}
modFile(4, '', $f);
I have a file (all.txt) with the following:
11111111111, 22222222222, 33333333333, 44444444444
That I wish to display like this:
11111111111
22222222222
33333333333
44444444444
and to add a space then some text after each number where the text is the same on each line:
11111111111 text here
22222222222 text here
33333333333 text here
44444444444 text here
I have an html form that passes the custom text to be appended to each line.
I need to keep the file all.txt intact then save the newly formatted file with a different name.
I have tried putting variables into the implode where I currently have the "\r\n" but this does not work.
Any help very much appreciated.
Thanks in advance!
A few notes about your code: You are passing $pos to the function but it will get overwritten in the foreach. Also the foreach is empty, so what's it good for? And I don't see you use $text anywhere either.
To achieve your desired output, try this instead:
file_put_contents(
'/path/to/new.txt',
preg_replace(
'/[^\d+]+/',
' some text' . PHP_EOL,
file_get_contents('all.txt')
)
);
The pattern [^\d+]+ will match any string that is not a consecutive number and replace it with "some text " and a new line.
A somewhat more complicated version achieving the same would be:
file_put_contents(
'/path/to/new.txt',
implode(PHP_EOL, array_map(
function ($number) {
$message = filter_var(
$_POST['message'], FILTER_SANITIZE_SPECIAL_CHARS
);
return sprintf('%s %s', trim($number), $message);
},
array_filter(str_getcsv(file_get_contents('/path/to/all.txt')))
)
));
This will (from the inside out):
Load the content of all.txt and parse it as CSV string into an array. Each array element corresponds to a number.
Each of these numbers is appended with the message content from the POST superglobal (you dont want to use REQUEST).
The resulting array is then concatenated back into a single string where the concatenating character is a newline.
The resulting string is written to the new file.
In case the above is too hard to follow, here is a version using temp vars and no lambda:
$allTxtContent = file_get_contents('/path/to/all.txt');
$numbers = array_filter(str_getcsv($allTxtContent));
$message = filter_var($_POST['message'], FILTER_SANITIZE_SPECIAL_CHARS);
$numbersWithMessage = array();
foreach ($numbers as $number) {
$numbersWithMessage[] = sprintf('%s %s', trim($number), $message);
};
$newString = implode(PHP_EOL, $numbersWithMessage);
file_put_contents('/path/to/new.txt', $newString);
It does the same thing.
Your foreach() closing brace is on the wrong place. You've missed the exact part of running the execution of the new file creation. Here:
$text = $_REQUEST['message'];
$f = file_get_contents("all.txt");
$f = explode(", ", $f);
function modFile($pos, $tothis, $inthis, $text){
$fh = fopen("example.txt", "w");
foreach($inthis as $pos => $a){
$newarr = $a." ".$text."\r\n";
fwrite($fh, $newarr);
}
fclose($fh);
}
modFile(4, "", $f, $text);
This is for formatting your new file as you desire, however, you're not passing the new $text['message'] you want to append to your new file. You could either modify your mod_file() method or pass it within the foreach() loop while it runs.
EDIT* Just updated the whole code, should be now what you aimed for. If it does, please mark the answer as accepted.
I was just about to ask the same questions as the question aksed here.... Forcing fputcsv to Use Enclosure For *all* Fields
The question was
When I use fputcsv to write out a line
to an open file handle, PHP will add
an enclosing character to any column
that it believes needs it, but will
leave other columns without the
enclosures.
For example, you might end up with a
line like this
11,"Bob ",Jenkins,"200 main st. USA
",etc
Short of appending a bogus space to
the end of every field, is there any
way to force fputcsv to always enclose
columns with the enclosure (defaults
to a ") character?
The answer was:
No, fputcsv() only encloses the field
under the following conditions
/* enclose a field that contains a delimiter, an enclosure character, or a newline */
if (FPUTCSV_FLD_CHK(delimiter) ||
FPUTCSV_FLD_CHK(enclosure) ||
FPUTCSV_FLD_CHK(escape_char) ||
FPUTCSV_FLD_CHK('\n') ||
FPUTCSV_FLD_CHK('\r') ||
FPUTCSV_FLD_CHK('\t') ||
FPUTCSV_FLD_CHK(' ')
)
There is no "always enclose" option.
I need to create a CSV file will every field enclosed... What would be the best solution?
Thanks in advance...
Roll your own function - its not hard:
function dumbcsv($file_handle, $data_array, $enclosure, $field_sep, $record_sep)
{
dumbescape(false, $enclosure);
$data_array=array_map('dumbescape',$data_array);
return fputs($file_handle,
$enclosure
. implode($enclosure . $field_sep . $enclosure, $data_array)
. $enclosure . $record_sep);
}
function dumbescape($in, $enclosure=false)
{
static $enc;
if ($enclosure===false) {
return str_replace($enc, '\\' . $enc, $in);
}
$enc=$enclosure;
}
(above is using unix style escaping)
C.
A workaround: Supposing you have your data in a 2-dimensional array, you can append a string that will force quoting and you are sure is not contained in your data ("## ##" here) and then remove it:
$fp = fopen($filename, 'w');
foreach ($data as $line => $row) {
foreach ($row as $key => $value) {
$row[$key] = $value."## ##";
}
fputcsv($fp, $row);
}
fclose($fp);
$contents = file_get_contents($filename);
$contents = str_replace("## ##", "", $contents);
file_put_contents($filename, $contents);
I have encountered the same problem, and I have solved it as follows.
I have inserted single quotes to each array value.
This way when you open the csv file with excel, the scientific notation E + 15 will no longer be displayed.
Here I share you as I did.
This worked for me, I hope you do too.
Regards
// this function add a single quote for each member of array
function insertquote($value) {
return "'$value'";
}
# here i send each value of array
# to a insertquote function, and returns an array with new values,
# with the single quote.
foreach ($list as $ferow) {
fputcsv($fp, array_map(insertquote, $ferrow), ';');
}
I am using the following code which lets me navigate to a particular array line, and subarray line and change its value.
What i need to do however, is change the first column of all rows to BLANK or NULL, or clear them out.
How can i change the code below to accomplish this?
<?php
$row = $_GET['row'];
$nfv = $_GET['value'];
$col = $_GET['col'];
$data = file_get_contents("temp.php");
$csvpre = explode("###", $data);
$i = 0;
$j = 0;
if (isset($csvpre[$row]))
{
$target_row = $csvpre[$row];
$info = explode("%%", $target_row);
if (isset($info[$col]))
{
$info[$col] = $nfv;
}
$csvpre[$row] = implode("%%", $info);
}
$save = implode("###", $csvpre);
$fh = fopen("temp.php", 'w') or die("can't open file");
fwrite($fh, $save);
fclose($fh);
?>
Use foreach or array_map to perform the same action on all elements of an array.
In this case, something roughly along these lines?
foreach($rows as &$row) {
$row[0] = NULL;
}
I don't have a ready answer for you but I would recommend checking out CakePHP's Set class. It does things like this very well and (in some methods) supports XPath. Hopefully you can find the code you need there.
Depending on the size of that file, this could be much more efficient than looping through:
$data = file_get_contents("temp.php"); //data = blah%%blah%%blah%%blah%%###blah%%blah%%blah
$data = preg_replace( "/^(.+?)(?=%%)/", "\\1", $data ); //Replace first column to blank
$data = preg_replace( "/(###)(.+?)(?=%%))/", "\\1", $data ); //Replace all other columns to blank
After that, write it back to the file as you did above.
This would need to be adjusted to allow for escape characters if your columns allow %% to appear consecutively within them, but other than that, this should work.
If you expect this csv file to get REALLY large, you should start thinking of looping through the file line by line rather than reading it completely into memory using file_get_contents. I would point you to fgets_csv, but I don't believe it is possible to get each csv line by any delimiter other than newline (unless you are willing to replace your ### separator with \r\n). If you end up going this way, the answer totally changes :P
For more information on Regex (specifically positive lookaheads) see Regex Tutorial - Lookahead and Lookbehind Zero-Width Assertions (also a great site for regex in general)