martes, abril 15, 2008

Convertir de CSV a String Array

La primera vez pensé que era cuestión de

File.ReadAllLines(Filename).Map(\x -> Split(","))

Pero eso no cubre los casos "raros". Y eso es que según la wiki y mas específicamente la RFC 4180 esta el caso de poder incluir separadores dentro de una entrada. Poder incluir saltos de línea e incluir comillas dobles (").

En F# resulta muy fácil. Uno solo hecha mano de FSLex y FSYacc.

La definición del lexer:


    8 rule token = parse

    9 | ',' {SEPARATOR}

   10 | '"' {QUOTE}

   11 | '\n'| '\r' '\n' {NEWLINE (Lexing.lexeme lexbuf)}

   12 | eof {EOF}

   13 | _ {OTHER (Lexing.lexeme lexbuf)}



El Parser:


   17 start:

   18     Line  {$1}

   19 

   20 Line:

   21     Entry  {[$1]}

   22     | Entry SEPARATOR Line  {$1::$3}

   23 

   24 Entry:

   25     | Text                   {$1}

   26     | QUOTE QuotedText QUOTE {$2}

   27 

   28 Text:

   29                         {new System.Text.StringBuilder()}

   30     | Text OTHER        {$1.Append($2)}

   31 

   32 SubField:

   33     | OTHER                {$1}

   34     | SEPARATOR            {","}

   35     | NEWLINE               {$1}

   36 

   37 QuotedText:

   38                             {new System.Text.StringBuilder()}

   39     | QuotedText SubField    {$1.Append($2)}

   40     | QuotedText QUOTE QUOTE {$1.Append(@"""")}



Y listo.

Haciendo esto me tropecé con unas cosas que se me había olvidado que existían.

Reduce-Reduce conflict y Shift-Reduce conflict. Inicialmente estaba haciendo parsing de todo el archivo. Pero que pasa si la primera línea está mala? mejor hacer el trabajo bajo demanda (Record por Record). Eso resuelve Los conflictos
Uno hace un Wrapper de 10 líneas y después puede uno generar una Secuencia (una especie de Generador, o lista por comprensión)


   26 let readCSV (filename:string) encoding =

   27     seq {use text = new StreamReader(filename)

   28         let csv = new CSVParser(text, encoding)

   29         while not csv.EndOfStream do

   30             yield csv.ReadRecord()

   31     }



Quiero verlos haciendo esto en Java!!

2 comentarios:

jpemberthy dijo...

o.o te habías equivocado de blog XD.

diegoeche dijo...

Si que pena :S