preg_replace all newlines within quotes [duplicate] - php

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)));
}
}

Related

Parsing csv where the last "header" is followed by a space

I'm trying to parse csv using str_getcsv in the following manner however I'm running into two problems (below).
$url = 'http://my.events.calendar/api/csv?mycalendarquery';
$response = wp_remote_get($url);
$response_body = $response['body']; // this is a really long string
$parsed_string = str_getcsv($response_body, ',', '"');
$full_events_array = array_chunk($parsed_string, 11);
Problem 1 - I can't use explode because one of the fields in the csv is "Description" which contains many/lengthy prose descriptions of events. Naturally these include commas, new lines, returns, etc...
Problem 2 - This is the one I have a question about. The categories (headers?) for the csv file are "Subject", "Date", (more things here...) "Description", "Calendar Address". However the last one doesn't have a comma after it. So, the entries for "Subject" and "Calendar" address are being combined like this -
array(11) {
[0] =>
string(32) "Calendar Address
"Application Due"" // this pattern happens for every event but with a url instead.
// Yes, I know this looks wrong in the code block, but this is exactly how the data is coming in (complete with the double quotes).
How do I parse this so that Calendar Address and Application Due are separated?
For reference, I've also tried str_getcsv($response_body, '"', ','); str_getcsv($response_body, ',', '"', '\n'); and several other combinations. Thanks for the help!
This script will read the CSV file into 251 rows of 12 elements each.
<?php // demo/temp_44414553.php
/**
* https://stackoverflow.com/questions/44414553/parsing-csv-where-the-last-header-is-followed-by-a-space?noredirect=1#comment75830321_44414553
*/
error_reporting(E_ALL);
// UNABLE TO ACCESS: http://events.uconn.edu/exports-2017-05-31-2017-07-01.csv
// COPY MADE HERE FROM THE DOWNLOAD LINK
$url = 'storage/exports-2017-05-31-2017-07-01.csv';
$csv = file_get_contents($url);
echo $csv;
$fpr = fopen($url, 'r');
if (!$fpr) trigger_error("Unable to open $url", E_USER_ERROR);
$kount = 0;
while (!feof($fpr))
{
$row = fgetcsv($fpr);
$kount++;
if (!$row) break;
echo PHP_EOL . count($row);
}
echo PHP_EOL . "There are $kount rows in the CSV at $url";

remove line where multiple characters are present

I am reading file with file_get_contents.
Some lines can have multiple "=" chars and I want to remove these lines.
I tried
str_replace("=", "", $content);
but this replaces all occurences of "=" but not removes these lines.
Any idea please?
UPDATE: my content from file looks like this:
something
apple is =greee= maybe red
sugar is white
sky is =blue
Without seeing an example of your file/strings, it's a bit tricky to advise, but the basic principle I would work to would be something like this:
$FileName = "PathToFile";
$FileData = file_get_contents($FileName);
$FileDataLines = explode("\r\n", $FileData); // explode lines by ("\n", "\r\n", etc)
$FindChar = "="; // the character you want to find
foreach($FileDataLines as $FileDataLine){
$NoOfChar = substr_count($FileDataLine, $FindChar); // finds the number of occurrences of character in string
if($NoOfChar <= 1){ // if the character appears less than two times
$Results[] = $FileDataLine; // add to the results
}
}
# print the results
print_r($Results);
# build a new file
$NewFileName = "YourNewFile";
$NewFileData = implode("\r\n", $Results);
file_put_contents($NewFileName, $NewFileData);
Hope it helps

PHP extract data from text file and write to another file

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.

str_getcsv example

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);
}

editing values stored in each subarray of an array

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)

Categories