$text
proveniente
de un fichero CSV
(Comma Separated Values). Esto es
el fichero contiene líneas con el formato:
"earth",1,"moon",9.374
Esta línea representa cinco campos.
Es razonable querer guardar esta información en un array,
digamos @field
, de manera que $field[0] == 'earth'
,
$field[1] == '1'
, etc.
Esto no sólo implica descomponer la cadena en campos sino
también quitar las comillas de los campos entrecomillados.
La primera solución que se nos ocurre es hacer uso de la
función split
:
@fields = split(/,/,$text);
Pero esta solución deja las comillas dobles en los campos
entrecomillados. Peor aún, los campos entrecomillados pueden contener
comas, en cuyo caso la división proporcionada por split
sería
errónea.
1 #!/usr/bin/perl -w 2 use Text::ParseWords; 3 4 sub parse_csv { 5 my $text = shift; 6 my @fields = (); # initialize @fields to be empty 7 8 while ($text =~ 9 m/"(([^"\\]|\\.)*)",? # quoted fields 10 | 11 ([^,]+),? # $3 = non quoted fields 12 | 13 , # allows empty fields 14 /gx 15 ) 16 { 17 push(@fields, defined($1)? $1:$3); # add the just matched field 18 } 19 push(@fields, undef) if $text =~ m/,$/; #account for an empty last field 20 return @fields; 21 } 22 23 $test = '"earth",1,"a1, a2","moon",9.374'; 24 print "string = \'$test\'\n"; 25 print "Using parse_csv\n:"; 26 @fields = parse_csv($test); 27 foreach $i (@fields) { 28 print "$i\n"; 29 } 30 31 print "Using Text::ParseWords\n:"; 32 # @words = "ewords($delim, $keep, @lines); 33 #The $keep argument is a boolean flag. If true, then the 34 #tokens are split on the specified delimiter, but all other 35 #characters (quotes, backslashes, etc.) are kept in the 36 #tokens. If $keep is false then the &*quotewords() 37 #functions remove all quotes and backslashes that are not 38 #themselves backslash-escaped or inside of single quotes 39 #(i.e., "ewords() tries to interpret these characters 40 #just like the Bourne shell). 41 42 @fields = quotewords(',',0,$test); 43 foreach $i (@fields) { 44 print "$i\n"; 45 }
Las subrutinas en Perl reciben sus argumentos en el array
@_
. Si la lista de argumentos contiene listas, estas
son ``aplanadas'' en una única lista. Si, como es el caso, la subrutina
ha sido declarada antes de la llamada, los argumentos pueden
escribirse sin paréntesis que les rodeen:
@fields = parse_csv $test;
Otro modo de llamar una subrutina es usando el prefijo &
,
pero sin proporcionar lista de argumentos.
@fields = &parse_csv;En este caso se le pasa a la rutina el valor actual del array
@_
.
Los operadores push
(usado en la línea 17) y pop
trabajan
sobre el final del array. De manera análoga los operadores
shift
y unshift
lo hacen sobre el comienzo.
El operador ternario ?
trabaja de manera análoga como lo hace en C.
El código del push
podría sustituirse por este otro:
push(@fields, $+);Puesto que la variable
$+
contiene la cadena que ha casado
con el último paréntesis que haya casado en el ultimo ``matching''.
La segunda parte del código muestra que existe un
módulo en Perl, el módulo Text::Parsewords
que proporciona la rutina quotewords
que hace la misma función que nuestra subrutina.
Sigue un ejemplo de ejecución:
> csv.pl string = '"earth",1,"a1, a2","moon",9.374' Using parse_csv :earth 1 a1, a2 moon 9.374 Using Text::ParseWords :earth 1 a1, a2 moon 9.374