Attached is a patch that implements fputcsv()
as a complement to fgetcsv()
.
There are two things that still need improvement:
-
It adds "\n" as a newline onto the end of each line. I think it would
be better to add a platform-specific line ending. -
It is not mbstring-aware.
Any suggestions for fixing these things (or other issues) would be
appreciated.
Thanks,
David
How about using PHP_EOL?
At 12:33 PM 4/11/2004 -0400, David Sklar wrote:
Attached is a patch that implements
fputcsv()
as a complement tofgetcsv()
.There are two things that still need improvement:
It adds "\n" as a newline onto the end of each line. I think it would be
better to add a platform-specific line ending.It is not mbstring-aware.
Any suggestions for fixing these things (or other issues) would be
appreciated.Thanks,
DavidIndex: ext/standard/basic_functions.c
RCS file: /repository/php-src/ext/standard/basic_functions.c,v
retrieving revision 1.662
diff -u -B -b -r1.662 basic_functions.c
--- ext/standard/basic_functions.c 3 Apr 2004 09:51:57 -0000 1.662
+++ ext/standard/basic_functions.c 11 Apr 2004 16:31:33 -0000
@@ -596,6 +596,7 @@
PHP_FE(stream_copy_to_stream,
NULL)
PHP_FE(stream_get_contents,
NULL)
PHP_FE(fgetcsv,
NULL)
PHP_FE(fputcsv, NULL) PHP_FE(flock, third_arg_force_ref) PHP_FE(get_meta_tags, NULL) PHP_FE(stream_set_write_buffer, NULL)
Index: ext/standard/file.h
RCS file: /repository/php-src/ext/standard/file.h,v
retrieving revision 1.88
diff -u -B -b -r1.88 file.h
--- ext/standard/file.h 8 Jan 2004 17:32:51 -0000 1.88
+++ ext/standard/file.h 11 Apr 2004 16:31:33 -0000
@@ -39,6 +39,7 @@
PHP_FUNCTION(fscanf);
PHPAPI PHP_FUNCTION(fgetss);
PHP_FUNCTION(fgetcsv);
+PHP_FUNCTION(fputcsv);
PHPAPI PHP_FUNCTION(fwrite);
PHPAPI PHP_FUNCTION(fflush);
PHPAPI PHP_FUNCTION(rewind);
Index: ext/standard/file.cRCS file: /repository/php-src/ext/standard/file.c,v
retrieving revision 1.380
diff -u -B -b -r1.380 file.c
--- ext/standard/file.c 25 Feb 2004 20:16:26 -0000 1.380
+++ ext/standard/file.c 11 Apr 2004 16:31:35 -0000
@@ -35,6 +35,7 @@
#include "php_open_temporary_file.h"
#include "ext/standard/basic_functions.h"
#include "php_ini.h"
+#include "php_smart_str.h"#include <stdio.h>
#include <stdlib.h>
@@ -1704,6 +1705,112 @@
return ptr;
}+/* {{{ proto int fputcsv(resource fp, array fields [, string delimiter [,
string enclosure]])
- Format line as CSV and write to file pointer */
+PHP_FUNCTION(fputcsv)
+{char *delimiter = ","; /* allow this to be set as parameter */
char *enclosure = "\""; /* allow this to be set as parameter */
- php_stream *stream;
- int ret;
- zval *fp = NULL, *fields = NULL, **field = NULL;
- char *delimiter_str = NULL;
- int delimiter_str_len = 0;
- char *enclosure_str = NULL;
- int enclosure_str_len = 0;
- char *buffer = NULL;
- int csvline_len = 0;
- HashPosition pos;
- int count = 0, i = 0;
- char enc_double[3];
- smart_str csvline = {0};
- if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|ass",
&fp, &fields, &delimiter_str,
&delimiter_str_len,
&enclosure_str, &enclosure_str_len) ==
FAILURE) {
return;
- }
- if (delimiter_str != NULL) {
/* Make sure that there is at least one character in string */
if (delimiter_str_len < 1) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "delimiter must
be a character");
RETURN_FALSE;
}
/* use first character from string */
delimiter[0] = delimiter_str[0];
- }
- if (enclosure_str != NULL) {
if (enclosure_str_len < 1) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "enclosure must
be a character");
RETURN_FALSE;
}
/* use first character from string */
enclosure[0] = enclosure_str[0];
- }
- PHP_STREAM_TO_ZVAL(stream, &fp);
- enc_double[0] = enc_double[1] = enclosure[0];
- enc_double[2] = '\0';
- count = zend_hash_num_elements(Z_ARRVAL_P(fields));
- zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(fields), &pos);
- while (zend_hash_get_current_data_ex(Z_ARRVAL_P(fields), (void **)
&field, &pos) == SUCCESS) {if ((*field)->type != IS_STRING) {
SEPARATE_ZVAL(field);
convert_to_string(*field);
}
/* enclose a field that contains a delimiter, an enclosure
character, or a newline */
if (php_memnstr(Z_STRVAL_PP(field), delimiter, 1,
Z_STRVAL_PP(field) + Z_STRLEN_PP(field)) ||
php_memnstr(Z_STRVAL_PP(field), enclosure, 1,
Z_STRVAL_PP(field) + Z_STRLEN_PP(field)) ||
php_memnstr(Z_STRVAL_PP(field), "\n", 1, Z_STRVAL_PP(field) +
Z_STRLEN_PP(field)) ||
php_memnstr(Z_STRVAL_PP(field), "\r", 1, Z_STRVAL_PP(field) +
Z_STRLEN_PP(field))) {
smart_str_appendl(&csvline, enclosure, 1);
{
zval enclosed_field;
php_char_to_str_ex(Z_STRVAL_PP(field),
Z_STRLEN_PP(field), enclosure[0],
enc_double, 2, &enclosed_field, 0, NULL);
smart_str_appendl(&csvline, Z_STRVAL(enclosed_field),
Z_STRLEN(enclosed_field));
zval_dtor(&enclosed_field);
}
smart_str_appendl(&csvline, enclosure, 1);
} else {
smart_str_appendl(&csvline, Z_STRVAL_PP(field),
Z_STRLEN_PP(field));
}
if (++i != count) {
smart_str_appendl(&csvline, delimiter, 1);
}
zend_hash_move_forward_ex(Z_ARRVAL_P(fields), &pos);
- }
- /* XXX: this should be platform-specific */
- smart_str_appendc(&csvline, '\n');
- smart_str_0(&csvline);
if (PG(magic_quotes_runtime)) {
buffer = estrndup(csvline.c, csvline.len);
php_stripslashes(buffer, &csvline_len TSRMLS_CC);
} else {
csvline_len = csvline.len;
- }
ret = php_stream_write(stream, buffer ? buffer : csvline.c,
csvline_len);
if (buffer) {
efree(buffer);
}
- smart_str_free(&csvline);
RETURN_LONG(ret);
+}
/* {{{ proto array fgetcsv(resource fp [,int length [, string delimiter
[, string enclosure]]])
Get line from file pointer and parse for CSV fields */
PHP_FUNCTION(fgetcsv)
Hi David,
I've made a slight revision to your proposed patch with adds the following
changes:
- Raise notice when delimeter or enclosure are >1 character long.
- Fixed a crash with custom delimeters & enclosures.
- Made fields with spaces and tabs be enclosed.
- Simplify the code & made it abide by PHP's CS.
As far as the line terminators, I think your initial usage of '\n' is the best
approach. Most applications capable of reading csv files will support \n
without further input from the user regardless of the system's EOL. Although,
\r\n is equally well supported, the only one which is not terribly well
supported is \r especially on some Win32 applications. Since the idea would
be to generate a universally usable csv file, \n seems (to me) like the best
choice.
Ilia
Attached is a patch that implements
fputcsv()
as a complement tofgetcsv()
.There are two things that still need improvement:
It adds "\n" as a newline onto the end of each line. I think it would
be better to add a platform-specific line ending.It is not mbstring-aware.
Any suggestions for fixing these things (or other issues) would be
appreciated.Thanks,
David
Attached is a patch that implements
fputcsv()
as a complement to
fgetcsv()
.There are two things that still need improvement:
It adds "\n" as a newline onto the end of each line. I think it
would be better to add a platform-specific line ending.It is not mbstring-aware.
As for writing stuff, we don't have to care about in which encoding
strings
are encoded as long as it is 8-bit compatible. UTF-16 / UTF-32 / UCS-2 /
UCS-4 are not 8-bit compatible and I've never seen a case where one of
these is utilised for CSV files, so I think your code is actually
"multibyte safe" :)
Moriyoshi
Any suggestions for fixing these things (or other issues) would be
appreciated.Thanks,
David