regex - array - Divida los datos con 3 delimitadores y guárdelos en 2 matrices separadas a la vez
perl substring (2)
Tengo datos con 3 delimitadores (:, y;) Y en los datos: y; aparecer solo una vez
__DATA__
1:X,Y,X,A,B;C,D,E,F
2:A,C,B,D
3:W,R,T,E;E
Paso 1:
Dividir por: y crear un hash
Paso 2:
Dividir por y almacenar cada valor separado por comas en una matriz hasta que encontremos;
Paso 3:
Todo lo que sigue; estaría en otra matriz
De los datos anteriores, estoy tratando de almacenar todos los valores antes; en el conjunto A y todo a la derecha en el conjunto B
Output
A = [X,Y,X,B,A,B,C,D,W,R,T,E] B=[C,D,E,F,E]
A continuación está el código que probé
my (@A,@B);
sub Compare_results
{
my %result_hash = map { chomp; split '':'', $_ } <DATA> ; #split by colon and futher split by , and ; if any (doing it in insert_array)
foreach my $key ( sort { $a <=> $b } (keys %result_hash) )
{
@A = split ",", (/([^;]+)/)[0], $result_hash{$key};
@B = split ",", (/;([^;]+)/)[0], $result_hash{$key};
print Dumper /@A,/@B;
}
}
Pero esto no está produciendo ningún resultado. Las matrices de salida están vacías. ¿Cuál es el enfoque correcto para dividir datos por y? en un momento almacenar en una matriz separada ¿Hay también una manera de dividir datos por tres delimitadores (una división para construir un hash) de una vez
Gracias
El truco es hacer cada paso por separado y en un orden diferente. El orden es tokenize luego analizar . Básicamente, divídelo en pedazos, luego haz algo con esas piezas.
El siguiente truco es tokenizar recursivamente . Eso es, en lugar de intentar tokenizar todo de una vez, dividir tokens grandes en tokens más pequeños, y convertirlos en tokens más pequeños, y así sucesivamente hasta que toques fondo. Primero la línea, luego los CSV.
Mirándolo de esta manera, la primera capa de la gramática se ve más o menos así (se ignora el espacio en blanco).
LINE = LINENUM : CSV ; CSV
Tenga en cuenta que en este momento no nos importa lo que hay en el CSV. Asumiremos que no tenemos que lidiar con las citas y escapar, sino que las cosas se complican.
Hay algunas maneras de lidiar con esto. Una es usar una expresión regular para tokenizar todo en una sola toma.
my($linenum, @csvs) = $line =~ /^(.*?) : ([^;]*) ; (.*)$/x;
Ahora que tiene los @csvs
separados de todo lo demás, necesitan ser tokenizados. Puede convertirlos en más fichas dividiendo en comas.
push @$a, split /,/, $csvs[0];
push @$b, split /,/, $csvs[1];
Y ahí tienes. Al tokenizar cada capa, se evita la complejidad de intentar analizar todo de una vez.
En cuanto a su función, hay muchas cosas que se pueden hacer para mejorarla. En su mayoría tienen que hacer una cosa , analizar el archivo. Algo más abre el archivo.
Además, todo lo que necesita debe pasarse y devolverse, sin usar elementos globales (sí, my
desde fuera de la función cuenta como global).
use strict;
use warnings;
use v5.10; # for say()
my($left, $right) = parse_whatever_this_format_is_called(*DATA);
say "Left: ". join ", ", @$left;
say "Right: ". join ", ", @$right;
sub parse_whatever_this_format_is_called {
# Take the filehandle to read as input
my $fh = shift;
# Declare our outputs
my(@left, @right);
# Parse each line
while( my $line = <$fh>) {
# Tokenize LINE = LINENUM : CSV ; CSV
my($linenum, @csvs) = $line =~ /^(.*?) : ([^;]*) ; (.*)$/x;
# Skip lines that didn''t match
next if !$linenum;
# Split the CSVs
push @left, split /,/, $csvs[0];
push @right, split /,/, $csvs[1];
}
# Return our outputs as references.
# It''s the only way to return multiple lists.
# Also it avoids the expense of a copy.
return( /@left, /@right );
}
__DATA__
1:X,Y,X,A,B;C,D,E,F
2:A,C,B,D
3:W,R,T,E;E
Muchos problemas: open necesita un nombre de archivo, no contenido de manejo de archivos (a menos que DATA contenga el nombre del archivo, que no). Para mantener los valores en las matrices, use push , not assignment; de todos modos, no puede asignar dos matrices al mismo tiempo, ya que el primero se come todo. Además, hacer todo en un comando podría ser posible, pero definitivamente no legible y mantenible.
#!/usr/bin/perl
use warnings;
use strict;
my $fh = *DATA{IO};
my (@A, @B); # The comments just fix
# the stupid SO syntax highlighter.
my %result_hash = map { chomp; split /:/ } <$fh>; #/
for my $key (sort { $a <=> $b } keys %result_hash) {
my ($left, $right) = split /;/, $result_hash{$key}; #/
push @A, split /,/, $left; #/
push @B, split /,/, $right // q();
}
use Data::Dumper; print Dumper(/@A, /@B, /%result_hash);
__DATA__
1:X,Y,X,A,B;C,D,E,F
2:A,C,B,D
3:W,R,T,E;E