

$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

