// CSV.es.h (C) 2008 adolfo@di-mare.com #ifdef Spanish_dox /// Documentación en español. #define Spanish_dox "Documentación en español" /// \def Spanish_dox ///< Marca bloques de documentación en español. #endif /* \file CSV.es.h \brief Documentación en español para \c CSV.h. \author Adolfo Di Mare \date 2008 */ #ifdef Spanish_dox /// \def CSV_h ///< Evita la inclusi¢n m£ltiple. #endif /** \file CSV.h \brief \c getNextCSV() y \c setQuotedCSV(): Librería para procesar archivos CSV. CSV: (Comma Separated Value). El formato CSV ha sido definido por IETF with RFC-4180. - Si el archivo está grabado de acuerdo al format especificado en RFC-4180 sus campos CSV serán extraídos correctamente por estas rutinas. - Trabaja con \c char; no ha sido probada con \c wchar_t. - Cuando el contenido del archivo no cumple con RFC-4180 estas rutinas no fallan pues retornan un valor "reazonable2. En estos casos el programador debe ser cuidadoso, y debe tratar de arreglar los valores CSV retornados aplicándoles \c trim() o \c trimCSV(). - Se ha hecho un esfuerzo por cumpliro con RFC-4180: \see http://tools.ietf.org/html/rfc4180 \par Se incluyen 2 rutines principales para procesar archivos CSV: - Para obtener valores use la función \c getNextCSV(). - Para almacenar alores use la función \c void setQuotedCSV() y luego almacene la hilera resultante. La clase \c CSV_line es un empaque C++ para estas rutinas, pero requiere que no haya caracteres de fin de línea ( Line Feed \c "\n" ) en los renglones del archivo CSV. \author Adolfo Di Mare \date 2008 */ #ifdef __cplusplus // compatibility C <==> C++ #include #include #include // CSV: Comma Separated Values [IETF RFC-4180]. // namespace csv { /** \fn void setQuotedCSV( std::string& res , const std::string& value ); \brief Prepara \c value para ser grabado en un archivo CSV. - Almacena un valor nuevo en la hilera \c res. - Rodea el resultado de comillas dobles cuando \c value tiene espacios en blanco (whitespace). - Rodea el resultado de comillas dobles cuando \c value tiene comillas dobles. - Rodea el resultado de comillas dobles cuando \c value tiene comas \c ",". - Sustitutye cada comilla doble \c '"' dentro de \c value por 2 comillas dobles \c [""]. - Trabaja con \c char; no ha sido probada con \c wchar_t. \dontinclude test_CSV.cpp \skipline test::setQuotedCSV() \until }} \see test_CSV::setQuotedCSV() */ /** \fn bool getNextCSV( std::string& csv, std::istream& CIN ); \brief Obtiene del flujo de entrada \c CIN el siguiente valor CSV. - \c CIN debiera estar abierto en modo \c std::ios::binary pues los caracteres son extraídos uno por uno, usando \c CIN.get(ch). - El valor obtenido de \c CIN queda almacenado en \c csv. - Trabaja bien con \c char, no ha sido probado para \c wchar_t. - Elimina de \c csv los caracteres finales (CR+LF or LF) ==> \c "\r\n" o \c "\n". - En lo posible se ha tratado de cumplir con el RFC-4180. \return true cuando el campo CSV termina en \c "\n" (LF -> LineFeed). \see http://tools.ietf.org/html/rfc4180 \see http://www.horstmann.com/cpp/pitfalls.html \dontinclude CSV_line.cpp \skipline test::getNextCSV() \until }} \see test_CSV::getNextCSV() */ /** \fn void trim( std::string & str ); \brief Le elimina a \c "str" los blancos del principio y del final. - También elimina los caracteres " \f\n\r\t\v". - Usa \c isspace(ch) para determinar si una letra es o no es algún tipo de espacio whitespace. \dontinclude test_CSV.cpp \skipline test::trim() \until }} \see test_CSV::test_trim() */ /** \fn void trimCSV( std::string & str ); \brief Convierte un campo CSV incorrecto en su probable valor correcto. - Le elimina a \c "str" los blancos y comillas del principio y del final usando \c trim(). - Si después de eliminar los espacios en blanco \c "str" comienza y termina con comillas, sustituye cada pareja de comillas dobles \c [""] por una sóla comilla simple \c ["]. - No verifica que todas las comillas dobles estén correctamente emparejadas. En algunas ocasiones un archivo.csv tiene campos entre comillas que están precedidos por espacios en blanco. Esos campos CSV no cumplen con los requisitos definidos en la especificación RFC-4180 por lo que son extraídos por \c getNextCSV() tal cual vienen, sin eliminar los espacios y sin sustituir las parejas de comillas dobles por comillas simples, como se muestra en el siguiente ejemplo en que los paréntesis cuadrados se usan en lugar de la comilla \c ["] para hacerlo más legible: \code ["zero", "if "" 1" , , " 3xt" \r\n] [....0.,........ 1..,2,.........3...] csv field getNextCSV() trimCSV() +------------------+----------------+----------+ | ["zero"] | [zero] | [zero] | | [, "if "" 1" ] | [ "if "" 1" ] | [if " 1] | | [, ] | [ ] | [] | | [, " 3xt" \r\n] | [ " 3xt" ] | [ 3xt] | +------------------+----------------+----------+ \endcode Por sentido común el programador esperaría que todas estas hileras fueran reconocidas, pero de acuerdo al RFC-4180 únicamente la primera es correcta. Al usar \c trimCSV() en el valor obtenido al procesar esta línea, se obtiene a fin de cuentas el valor esperado. - Sin embargo, los campos que contienen cambios de línea \c "\r" o fines de línea \c "\n" seguramente son procesados de una manera diferente a lo esperado por \c getNextCSV(), aún antes de ser recibidos como argumentos de \c trimCSV(), por lo que es mejor no confiar en que esta rutina es una solución completa cuando se usan archivos.csv que no están escritos con apego estricto a la especificación RFC-4180. \dontinclude test_CSV.cpp \skipline test::trimCSV() \until }} \see test_CSV::test_trimCSV() */ /** \fn void chop( std::string & str , char ch=0 ); \brief Elimina \c ch si es el último caracter de \c str. - El caracter eliminado siempre es \c ch. \dontinclude test_CSV.cpp \skipline test::chop() \until }} \see test_CSV::test_chop() */ // }; // namespace csv /// Definido por la biblioteca estándar C++ namespace std { } // truco para que Doxygen lo documente #endif // __cplusplus #include // NULL, etc. [C language] #ifdef __cplusplus extern "C" { #endif /* Obtiene del flujo de caracteres \c getChar() el siguiente valor CSV. - En cada invocación a \c getChar() un solo caracter es obtenido. - Si no hay más caracteres, \c getChar() debe retornar \c -1. */ /** Retrieves from the input character stream \c getChar() then next CSV value. - On each invocation to \c getChar() a single character is retrieved. - When nor more caracteres are available \c getChar() must retorn \c -1. - Stores the CSV value read into memory buffer \c res. - Buffer \c res can hold up to \c res_size char's. - If the value read is bigger than what \c res can hold only as many char's as will fit into \c res are retrieved; in this case, the full value will not be read. - If the full CSV value can be stored into \c res returns \c 1, otherwise signals failure returning \c 0. - The number of characters retrieved from \c getChar() always is \c strlen(res). */ int getNextCSV( char* res , size_t res_size , int (getChar)( void* context) ); /** Prepares \c line for output into a CSV file. - Stores the resulting string in memory buffer \c res. - Buffer \c res can hold up to \c res_size char's. - \c NULL is returned whenever more than \c res_size char's are required. - Otherwise, returns \c res. - Surrounds the result in double-quotes when \c line has whitespace. - Surrounds the result in double-quotes when \c line has double-quotes. - Surrounds the result in double-quotes when \c line has commas ','. - Substitutes any double-quotes '"' within \c line with 2 double-quotes [""]. */ char* putCSV( char* res , size_t res_size , const char * line ); /** Retorna la mayor subhilera de \c "str" que no tiene blancos al principio o al final. - Modifica la hilera \c "str" y retorna un puntero dentro de ella. - Si \c "str" tiene blancos al final, los elimina insertándole un \c char(0). - También elimina los caracteres " \f\n\r\t\v". - Usa \c isspace(ch) para determinar si una letra es o no es algún tipo de espacio whitespace. \dontinclude test_CSV.cpp \skipline test::trim_C() \until }} \see test_CSV::test_trim_C() */ char * trim( char * str ); /** Elimina \c ch si es el último caracter de \c str. - El caracter eliminado siempre es \c ch. \dontinclude test_CSV.cpp \skipline test::chop_C() \until }} \see test_CSV::test_chop_C() */ void chop( char * str , char ch ); #ifdef __cplusplus } #endif // EOF: CSV.es.h