I have a php script that is generating a tab delimited .txt file that works great but I need to output a field that contains a . (full stop/ period) within the $model field i.e itemname.1 it affects the formatting of the file.
$csv_output .= $model . "\t";
$csv_output .= '' . "\t";
$csv_output .= '' . "\t";
$csv_output .= '' . "\t";
$csv_output .= $totalstock . "\t";
$csv_output .= $leadtime . "\t";
$csv_output .= "\n";
$csv_handler = fopen('../outputfile.txt','w');
fwrite ($csv_handler,$csv_output);
fclose ($csv_handler);
I have tried enclosing in double quotes and various other variations but the output is inserting newlines
example output
itemname.1
20 1
any ideas how i can output the fields with the . in them without it affecting the tabs/lines.
Could you show us your code that encloses the variable in quotes please?
The not-very-well-informed solution appears to be use replace() to test if the . is indeed causing the new line, or if it's "something else". You might just be able to replace new line straight up even.
It Was a school boy error.
The issue was the data imported into db had carriage returns on each line entry, although the data appeared correct in the db and in excel the problem only manifested in the output file.
Thanks for your help guys made me go back to source and identify the issue.
Related
I'm having an issue getting the line folding to work the way it's specified. I'm obviously misunderstanding something about the documentation, so I was hoping I could get some help. The validator at https://icalendar.org/validator.html is saying
Lines not delimited by CRLF sequence near line # 1
Reference: RFC 5545 3.1. Content Lines
This is my function to generate the .ics files for download:
public function getCalendarFile($event) {
header("Content-Type: text/Calendar; charset=utf-8");
header("Content-Disposition: attachment; filename="ExampleFile.ics");
$icsFile = "BEGIN:VCALENDAR\r\n";
$icsFile .= "VERSION:2.0\r\n";
$icsFile .= "PRODID:Example Event" . $event->name . "\r\n";
$icsFile .= "METHOD:PUBLISH\r\n";
$icsFile .= "BEGIN:VEVENT\r\n";
$icsFile .= "UID:". $event->name . gmdate("Ymd\THis\Z", strtotime(Carbon::now())) . "\r\n";
$icsFile .= "DTSTAMP:" . gmdate("Ymd\THis\Z", strtotime(Carbon::now())) . "\r\n";
$icsFile .= "DTSTART:" . gmdate("Ymd\THis\Z", strtotime($event->begin)) . "\r\n";
$icsFile .= "DTEND:" . gmdate("Ymd\THis\Z", strtotime($event->end)) . "\r\n";
$icsFile .= "LOCATION:" . strip_tags($event->location) . "\r\n";
$icsFile .= "SUMMARY:" . $event->name . "\r\n";
$icsFile .= "DESCRIPTION:" . $this->foldCalendarDescription(strip_tags($event->description)) . "\r\n";
$icsFile .= "END:VEVENT\r\n";
$icsFile .= "END:VCALENDAR\r\n";
echo $icsFile;
}
public function foldCalendarDescription($description) {
return wordwrap($description, 75, "\r\n\t", TRUE);
}
I'm not sure if it has something to do with strip_tags possibly? The event description is stored as a wysiwyg html input. But the issue says with line # 1 and line # 1 looks fine to me.
Here's a wrapper for ICAL strings that works for me:
function format_ical_string( $s ) {
$r = wordwrap(
preg_replace(
array( '/,/', '/;/', '/[\r\n]/' ),
array( '\,', '\;', '\n' ),
$s
), 73, "\n", TRUE
);
// Indent all lines but first:
$r = preg_replace( '/\n/', "\n ", $r );
return $r;
}
As I wrote in the comment beneath kmoser's answer, people seem to be really hard pressed to get exactly 75 bytes on a line and create very convoluted code to do that. But why not just sometimes less than 75 bytes? A simple wordwrap will give you that because it just looks for whitespace to break and is not multi-byte aware. You'll possibly have a few more linebreaks in the iCal object code than strictly neccessary, but does that matter? This is why I upvoted kmoser's answer. It's a nice and simple solution.
I've tried to create an even simpler and faster version of kmoser's answer:
function format_ical_string( $str ) {
$str = str_replace(
[ "\r\n", '\\', ',', ';', "\n" ], // replacement order is important
[ "\n", '\\\\', '\,', '\;', '\n' ], $str );
return wordwrap( $str, 73, "\n ", false );
}
The replacements (just a str_replace, because nothing fancy is needed here) are:
replace CRLF's with just LF's
replace literal backslashes with escaped backslashes
escape comma's
escape semicolons
replace newlines with a literal \n (per rfc5545)
Then wordwrap with a LF + space (per rfc5545).
After this, strictly you should replace all LF's with CRLF's (rfc5545 line ending). I tend to format the entire iCal object with just LF's, and only at the very end replace all LF's with CRLF's to make the result compliant. This saves me the hassle of repeatedly inserting these Windows line endings (who uses those these days?) during the composition of my iCal objects.
It is unclear from rfc5545 if the property name itself is counted in the 75 bytes line limit, as in https://www.rfc-editor.org/rfc/rfc5545#section-3.1:
Lines of text SHOULD NOT be longer than 75 octets, excluding the line break.
But just to be sure, I feed the property name to the function, as in:
$line = format_ical_string( 'SUMMARY:Really long text here' );
IMPORTANT: my function above will fail on single words (!) that are longer than 75 bytes. I'm not sure how common that is. If you have an 18-character word completely made of 4-byte characters, you'd be at the limit. This seems very unlikely to me.
I've got the following code which outputs two different strings sent from a php contact form.
$email_message .= "Module: ".clean_string($modcode_1_from)."\s".clean_string($modcode_2_from)."\r\n";
is meant to display module code 1 and module code 2.
Currently looks likes this
1001\s2002
However i want it to look like:
1001 2002
So i added \s to add white space between the strings but it does not do anything for me.
use non-breaking space or you can use \t to add a tab char. And the best one is just a space:
$email_message .= "Module: " . clean_string($modcode_1_from). " ". clean_string($modcode_2_from) . "\r\n";
$email_message .= "Module: " . clean_string($modcode_1_from) . "\t" . clean_string($modcode_2_from) . "\r\n";
$email_message .= "Module: " . clean_string($modcode_1_from) . " " . clean_string($modcode_2_from) . "\r\n";
You can use this:
$email_message .= "Module: ".clean_string($modcode_1_from)." ".clean_string($modcode_2_from)."\r\n";
What I did: I removed the \s and put a space in its place.
IMPORTANT NOTE: DO NOT USE " ", it will echo/add between 1001 and 2002.
Resulting in:
1001 2002
Therefore, replace \s with an actual space using your spacebar.
Footnote (other options):
If you wish to later use your data in Excel for example, you could use a comma ,
(CSV, comma seperated value) as the seperating character or a tab for a tab-seperated value \t i.e. "\t", or a semi-colon ; i.e. "; ".
You may need/want to add a space after the comma; i.e. ", " for use as a CSV.
Example output of using a comma: 1001, 1002.
Can you try changing the \s to
' '
and see if this works for you?
Try this,
$email_message .= "Module: ".clean_string($modcode_1_from)." ".clean_string($modcode_2_from)."\r\n";
I know there are some similar threads but my code doesn't look anything like those questions and am not sure how to adopt. I think my approach can probably work, but I am just not getting the desired result.
I am trying to export a MySQL db to a csv file. One field has commas in it so putting the string in double quotes allows me to reimport the csv file back into my program. Without the quotes, the comma makes it seem like another field, so then all subsequent data is off in my reimport.
The first line of code outputs perfect, the description field may have commas or other punctuation. I want to put double quotes around it and have it output to my csv file.
<td><?php echo $rows['model']; $csv_output .= $rows['model'] . ", ";?></td>
<td><?php echo $rows['description']; $csv_output .= "\"" . $rows['description'] . "\"" . ", ";?></td>
For testing I have substituted "\"" with "5" and a 5 appears before and after my string and in the csv file.
I am new so if it is at all possible to do it this way, it would be better than changing my approach.
I tried the below line and added an echo to the screen and all my data, quotes included displays.
<td><?php echo $rows['description']; $csv_output .= '"' . $rows['description'] . '"' . ", ";?></td>
So one complete line looks like this.
1, 1, , , "Retiremnet Costs JE 100209 - F", 388155, 0, 1, , , 2006-06-30, A, 1, 0, 0, 113687.67, , ,
The quotes are there.
But when I press a button to post to the next page where the file will be written, the data disappears and leaves me with this.
1, 1, , ,
So it seems to me that there is something perhaps wrong with the file that writes the data so I'll include that.
<?php
$out = '';
if (isset($_POST['csv_hdr'])) {
$out .= $_POST['csv_hdr'];
$out .= "\n";
}
if (isset($_POST['csv_output'])) {
$out .= $_POST['csv_output'];
}
$filename = $file."_".date("Y-m-d_H-i",time());
//Generate the CSV file header
header("Content-type: application/vnd.ms-excel");
header("Content-disposition: csv" . date("Y-m-d") . ".csv");
header("Content-disposition: filename=".$filename.".csv");
//Print the contents of out to the generated file.
print $out;
//Exit the script
exit;
?>
I figured out the problem but still don't have a solution. If I put in
$out .= "4, 1, , , \"Cisco 1000Base-SX SFP, LC\", 708476, 0, 3, , , 2011-03-15, A, 1, 20016525, 7088398, 300.00, , , ";
The file writes perfectly. I changed all of my csv_output statements so that when I echo it reads exactly the same as what I pasted over the POST variable on the POST page, but when I do that, the csv_output variable is completely empty.
Thanks
The XML output from this loop was failing to validate but the validator was giving me different errors each time. Each time it had to do with the opening < of an element closure being missing. A different one each time...
Every time I refresh and re-validate the output there is at least one of these and it has never yet been in the same member record.
Initially I was adding tags everywhere which is why you will see many of them wrapping things where they should not be needed.
The XML is built by this loop:
if ($members) {
$xml = '<api><response status="ok"><users>';
foreach ($members as $m) {
$join_date = date("Y-m-d H:i:s", $m->join_date);
list($md) = $mdObj->retrieve("member_id = '$m->member_id'");
$join_date = ($m->join_date > 0) ? date("Y-m-d H:i:s", $m->join_date) : '0000-00-00 00:00:00';
$address = preg_replace('/\R/', '', $md->m_field_id_3);
$xml .= "<user id=\"$m->member_id\"><admin>0</admin><name><![CDATA[$m->username]]></name><company>$md->m_field_id_9</company><company_id>$md->m_field_id_28</company_id><address><![CDATA[$address]]></address><city>$md->m_field_id_5</city><region>$md->m_field_id_6</region><postal_code>$md->m_field_id_7</postal_code><email><![CDATA[$m->email]]></email><phone>$md->m_field_id_10</phone><first>$md->m_field_id_1</first><last>$md->m_field_id_1 $md->m_field_id_2</last><url></url><description><![CDATA[]]></description><status>active</status><date>$join_date</date><modified>0000-00-00 00:00:00</modified></user>";
}
$xml .= '</users></response></api>';
return $xml;
}
Has anyone seen this before? Have any advice?
Here's a little PHP info:
PHP Version 5.2.17
Linux foo.foo.com 2.6.18-274.17.1.el5 #1 SMP Wed Jan 4 22:45:44 EST 2012 x86_64
Build Date Feb 8 2012 14:19:50
I suspect the database entries you're including into you XML might contain unescaped characters which have special meaning, e.g. &, <, >, " and ' which need to be encoded.
I would also break up that long string into
$xml .= "<user id=\"" . $m->member_id . "\"><admin>0</admin><name><![CDATA[";
$xml .= $m->username . "]]></name><company>" . $md->m_field_id_9 . "</company>";
$xml .= "<company_id>" . $md->m_field_id_28 . "</company_id><address><![CDATA[";
$xml .= $address . "]]></address><city>" . $md->m_field_id_5 . "</city><region>";
$xml .= $md->m_field_id_6 . "</region><postal_code>" . $md->m_field_id_7;
$xml .= "</postal_code><email><![CDATA[" . $m->email . "]]></email><phone>";
$xml .= $md->m_field_id_10 . "</phone><first>" . $md->m_field_id_1 . "</first>";
$xml .= "<last>" . $md->m_field_id_1 . $md->m_field_id_2 . "</last><url></url>";
$xml .= "<description><![CDATA[]]></description><status>active</status><date>";
$xml .= $join_date . "</date><modified>0000-00-00 00:00:00</modified></user>";
and then use str_replace() to specifically encode the above-mentioned characters.
What could be happening is that you data contains invisible whitespace, most notably DEL characters .. I suppose that would cause this precise behaviour.
To check, loop over each character in the string and print the character code to check if a string contains any hidden whitespace.
This appears to be a bug in Chrome's view source routine on a large XML file. XML obtained from the same source via IE and FireFox was valid across repeated tests.
Additionally Chrome's normal view did not display these aberrations and did not report errors in the XML in normal view.
I can't seem to figure out why the following code doesn't produce a new line in my text file - neither does using \n etc either - any ideas what could be wrong?
$data = $name . ' | ' . $_POST['comment'] . PHP_EOL;
//write to file
$f = file_put_contents('posts.txt', $data, FILE_APPEND);
Are you viewing the text file in an internet browser by any chance?
If you are, the browser will get rid of the newline characters (unless you're using PRE tags).
Try double quotes: $data = $name . ' | ' . $_POST['comment'] . "\n";
Or: $data = "$name | {$_POST['comment']}\n";
Have you tried \r or \n\r ? Just an idea.