Vie Jul 28, 2006 12:38 pm
|
 |
rookie
Perlero Nuevo

|
Registrado: 01 Mar 2006
Mensajes: 39
|
|
| Cómo ordenar esta cadena... (dirección postal) |
|
|
Saludos .
Tengo una dirección de un domicilio en un campo de texto de una tabla como esta:
| Cita: | | Edificio E Manzana 36 Departamento 15 |
El problema es que el orden es incorrecto, ya que primero debe ir Manzana, seguido de Edificio y despues Departamento. De tal forma que debe quedar asi:
| Cita: | | Manzana 36 Edificio E Departamento 15 |
Y asi tengo una gran cantidad de domicilios que hay que ordenar, hasta el momento ya tengo un script en perl que me indica cuales domicilios tienen un orden correcto y cuales no. Ademas el mismo script me indica cual es el orden correcto en el que deberían estar escritas las direcciones.
El problema es: ¿cómo sacar de la cadena incorrecta la parte de la cadena que debe ir primero y sacarla con todo y el número o letra correspondiente.? En el ejemplo anterior deberia ser:
y así consecutivamente el orden de las otras partes del domicilio.
Hay que tomar en cuenta que en otras direcciones puede variar el identificador y el número, por ejemplo
¿Cómo puedo sacar cada una de las partes de los domicilios para formar el correcto?
Gracias por su ayuda. |
|
|
|

Vie Jul 28, 2006 1:46 pm
|
 |
explorer
Moderador

|
Registrado: 24 Jul 2005
Mensajes: 4082
Ubicación: Valladolid, España
|
|
|
|
|
Como el separador me parece que es el espacio en blanco, se puede hacer muy rápido para sacar los campos:
| Código: |
@campos = split " ", $dirección;
# Ahora los campos están en $campo[0], $campo[1], $campo[2]...
# Por ejemplo, para el caso de
# Edificio E Manzana 36 Departamento 15
print "@campos[2,3,0,1,4,5]"; # Manzana 36 Edificio E Departamento 15 |
El problema viene si en la $dirección no hay exactamente 6 campos... |
|
Sab Jul 29, 2006 11:21 am
|
 |
kidd
Creador de Perl en Español

|
Registrado: 15 Oct 2003
Mensajes: 1389
Ubicación: México
|
|
|
|
|
Hola:
A mi se me ocurre que lo podrías hacer con un regexp:
| Código: |
$direccion =~ s/(Manzana [-1-9A-Za-z]+ )//;
$direccion = $1 . $direccion;
|
Saludos |
|

Lun Ago 07, 2006 4:22 pm
|
 |
rookie
Perlero Nuevo

|
Registrado: 01 Mar 2006
Mensajes: 39
|
|
|
|
|
Saludos a todos.
Disculpas por tardar tanto en responder. Bien... antes que nada gracias por su ayuda.
Efectivamente como dice 'explorer' es un problema bastante interesante pues no hay una palabra a ordenar unicamente, sino un conjunto de palabras. Miren:
| Código: |
ejemplo 1 Cadena DESordenada: "depto 5 edificio A"
Debería quedar ordenada así: "edificio A depto 5"
ejemplo 2 Cadena DESordenada: "Manzana 4 colonia 3 casa 21"
Debería quedar ordenada así: " Colonia 3 Manzana 4 casa 21"
ejemplo 3 Cadena DESordenada: "depto 8 edificio 2 colonia XY"
Debería quedar ordenada así: " Colonia XY edificio 2 depto 8" |
Tengo ya un script en Perl que me ordena la cadena, pero no me funciona para el 3er caso. Algo falta por hacer. La verdad es un script muy básico, pero es lo más cercano que he hecho para solucionar el problema, les paso parte del script . Sus opiniones son importantes para ayudarme... gracias de antemano.
Hasta antes de este punto, ya tengo en un arreglo el orden en el que debe de estar ordenada la cadena, tengo los elementos separados en un arreglo. Ejemplo: @ordenado=('Colonia','Manzana','Casa'). Si ahora me dispongo a reordenar la cadena, creo que el problema está en esta parte. Si bandera es 0 la cadena está en orden correcto, sino se procede a ordenarla. (perdón si es poco elegante la manera de programar).
| Código: |
if ($bandera==0) {
$m++;
print "orden correcto \n";
}
if ($bandera>=1) {
print "Orden incorrecto en avza: $fila[0]---$fila[1] --- Deberia ser asi";
$n++;
print "TAM ORDEN: $tam_orden...";
#ordenado =arreglo en donde se tienen las palabras con el orden correcto
#tam_orden= tamaño del arreglo en donde tengo las palabras en el orden en el que deberian estar.
#avza = cadena original que se debe ordenar
for($i=0;$i<=$tam_orden;$i++) {
$k=$i+1;
if ($k<$tam_orden) {
$pos1=index($avza,$ordenado[$i]);
$pos2=index($avza,$ordenado[$k]);
if ($pos2<$pos1) {
@arr_avza=split("",$avza);
$t=scalar(@arr_avza); #avza por letras
$res=$t-$pos1;
print " T es $t sacando a $ordenado[$i] apartir de la posicion $pos1 con $res elementos\n";
$resultado[0] = substr($avza,$pos1,$res);
$avza =~ s/$resultado[0]//;
#print "NUEVO _ AVZA :$avza";
}
else {
$res=$pos2-$pos1;
$resultado[1] = substr($avza,$pos1,$res);
}
}
else {
$resultado[1]=$avza;
}
}
$avza2=join(' ',@resultado);
print "----> $avza2\n";
#actualizo la BD de donde lei la direccion.
$dbh2->do("UPDATE ced_info_gral SET avza='$avza2' WHERE folio=$fila[0]")
or die ("Fallo la actualizacion de AVZA\n");
} |
|
|

Lun Ago 07, 2006 6:52 pm
|
 |
explorer
Moderador

|
Registrado: 24 Jul 2005
Mensajes: 4082
Ubicación: Valladolid, España
|
|
|
|
|
Respuesta rápida:
* En el bucle for no puedes ir desde 0 a tam_orden, porque entonces estás mirando un elemento más que el tamaño de @ordenado.
* Deberías inicializar @resultado en cada vuelta para evitar que el resultado de un bucle afecte al siguiente.
* La variable @arr_avza te la puedes ahorrar usando la función length: $t = length($avza);
* No compruebas el caso de que los index fallen. Es decir, que no encuentren la palabra que buscas. En ese caso devuelven el valor -1. Y ese valor no te sirve de nada para los cálculos siguientes.
* ¿Qué ocurre con los casos de mayúsculas y minúsculas? index puede encontrarte Manzana, pero no manzana.
* Hay un problema de lógica:
Supongamos que tenemos que ordenar "Manzana 4 colonia 3 casa 21". Al empezar el bucle comenzamos por buscar la combinación colonia/Manzana, y la encontramos, pero invertida, por lo que procedemos a su inversión, pero la inversión 'se lleva consigo' a la 'casa 21', por lo que entonces queda 'colonia 3 casa 21 Manzana 4', que es incorrecto. No entiendo como a tí te sale bien... El tercer caso tampoco funcionará por lo mismo: a la hora de hacer la inversión podemos colocar otros campos fuera de orden. |
|

Lun Ago 07, 2006 7:55 pm
|
 |
explorer
Moderador

|
Registrado: 24 Jul 2005
Mensajes: 4082
Ubicación: Valladolid, España
|
|
|
|
|
Bueno, he encontrado una forma:
| Código: |
1 #!/usr/bin/perl
2 use warnings;
3
4 # Entrada de prueba
5 @entrada = (
6 "depto 5 edificio A",
7 "Manzana 4 colonia 3 casa 21",
8 "depto 8 edificio 2 colonia XY",
9 );
10
11 @ordenado = qw(
12 colonia
13 manzana
14 casa
15 edificio
16 depto
17 );
18 $tam_ordenado = scalar @ordenado;
19
20 foreach $avza ( @entrada ) {
21 print "=>$avza.\n";
22 $nueva = lc($avza); # Lo pasamos a minúsculas
23
24 for ( $i = 0; $i < $tam_ordenado; $i++ ) {
25 for ( $k = $i+1; $k < $tam_ordenado; $k++ ) {
26 #print "\tBuscando $ordenado[$i]/$ordenado[$k]\n";
27 $pos1 = index($nueva, $ordenado[$i]);
28 $pos2 = index($nueva, $ordenado[$k]);
29 next if $pos1 <0 or $pos2 <0;
30 # Encontrada una pareja. Ver si está al revés
31 if ( $pos1 > $pos2 ) { # Si, hay que dar la vuelta
32 $nueva =
33 substr($nueva, 0, $pos2) .
34 substr($nueva, $pos1) . ' ' .
35 substr($nueva, $pos2, $pos1 - $pos2 - 1);
36 print "|>$nueva.\n";
37 } } }
38 print "+>$nueva.\n";
39 }
40 __END__ |
Salida
| Código: |
=>depto 5 edificio A.
|>edificio a depto 5.
+>edificio a depto 5.
=>Manzana 4 colonia 3 casa 21.
|>colonia 3 casa 21 manzana 4.
|>colonia 3 manzana 4 casa 21.
+>colonia 3 manzana 4 casa 21.
=>depto 8 edificio 2 colonia XY.
|>depto 8 colonia xy edificio 2 .
|>colonia xy edificio 2 depto 8 .
+>colonia xy edificio 2 depto 8 . |
Explicación:
* En la línea 20 hacemos un bucle por toda la @entrada
* En la línea 22, copiamos la cadena a analizar en $nueva, pasándola a minúsculas (esto es opcional, claro)
* De la 24 a 37 tenemos un doble bucle de búsqueda. Un doble bucle es necesario para contemplar el caso de que alguna de las direcciones tenga palabras 'desaparecidas' (por ejemplo, una casa dentro de una colonia)
* En las líneas 27 y 28 buscamos las dos palabras que nos interesan
* Si falta alguna de ellas, es tontería seguir, así que saltamos al siguiente (next) bucle
* Ahora, en la 31 comprobamos si están en orden inverso
* Si lo están, entonces hay que invertir su posición (32 a 35).
El proceso de 'inversión' es el siguiente:
* Hemos encontrado dos palabras, una en la posición $pos1 y otra en la $pos2
* Dividimos la cadena original ($nueva) en 3 partes:
a) La primera parte es la que va desde el comienzo hasta la primera de las palabras encontradas (sabemos que está en $pos2)
b) La segunda parte va desde $pos1 hasta el final de la cadena, más un espacio (ahora explico porqué)
c) La tercera parte es el trozo de cadena que está entre $pos2 y $pos1, menos una posición (ahora explico porqué).
El resultado es que se invierten las posiciones $pos2 y $pos1, respetando lo que ya estuviera ordenado al comienzo de la cadena.
* ¿Por qué sumamos un espacio en blanco en b)? Pues para que no quede pegado a c)
* ¿Y por qué quitamos una posición a c)? Esa posición es el espacio en blanco antes de $pos1. Si hacemos la inversión, ese espacio en blanco quedará al final, por lo que no nos sirve para nada
* De hecho... fíjate que sumamos un espacio en un sitio y se lo quitamos en otro... se podría haber recortado de otra manera para llevar esos espacios en blanco, pero hubiera quedado más complicado, con más cuentas.
Funciona, pero plantea más problemas... Antes hemos quitado las mayúsculas en la línea 22 para que pudiéramos hacer funcionar el index en 27 y 28 sin problemas, con los valores de @ordenado. Pero ¿queremos guardarlo así en la base de datos?.
Otro detalle: fíjate como respeta el caso de que existan dobles espacios en blanco entre las entradas. |
|

Lun Ago 07, 2006 8:21 pm
|
 |
explorer
Moderador

|
Registrado: 24 Jul 2005
Mensajes: 4082
Ubicación: Valladolid, España
|
|
|
|
|
Y esta es otra forma, usando expresiones regulares:
| Código: |
1 #!/usr/bin/perl -l
2 use warnings;
3
4 # Entrada de prueba
5 @entrada = (
6 "depto 5 edificio A",
7 "Manzana 4 colonia 3 casa 21",
8 "depto 8 edificio 2 colonia XY",
9 );
10
11 # Qué buscamos
12 @buscamos = qw(
13 Colonia
14 Manzana
15 Casa
16 Edificio
17 Dpto|Depto|Departamento
18 );
19
20 # Construir la expresión regular
21 @buscamos = map { "($_)" . '\s+(\S+)' } @buscamos;
22
23 # Para todas las entradas...
24 foreach $entrada ( @entrada ) {
25 print ">$entrada"; # Cómo era antes
26 @final = (); # Aquí guardaremos el resultado
27
28 # Para todo lo que buscamos...
29 foreach $buscamos ( @buscamos ) {
30 if ( $entrada =~ m/$buscamos/i ) { # ¿Está?
31 push @final, ucfirst($1) . " $2"; # Pues lo guardamos
32 }
33 }
34
35 $final = join ' ', @final; # Sacamos el resultado
36 print $final if $final;
37 }
38
39 __END__
>depto 5 edificio A
Edificio A Depto 5
>Manzana 4 colonia 3 casa 21
Colonia 3 Manzana 4 Casa 21
>depto 8 edificio 2 colonia XY
Colonia XY Edificio 2 Depto 8 |
Explicación:
* Tenemos por una parte, una @entrada y lo que @buscamos. Notar que lo que buscamos puede ser una expresión regular, como sucede en el caso del Departamento, que puede estar escrito de 3 formas distintas
* En la línea 21, reescribimos lo que @buscamos a algo así: (Colonia)\s+(\S+), que tiene toda la pinta de ser una expresión regular, que quiere decir: Busca la palabra Colonia -y la almacenas en $1- que esté seguida por un conjunto de espacios en blanco (\s+) y seguida por un conjunto de caracteres que no sean caracteres en blanco (\S+) -y esos me los guardas en $2-. Como ves, estamos reproduciendo el aspecto de cada uno de los campos.
* De la 24 a 37 tenemos el bucle por toda la @entrada
* En la línea 26, inicializamos @final como un array donde guardaremos el resultado de la búsqueda
* En la 29 iniciamos un bucle por cada campo que @buscamos
* En la 30, miramos a ver si en nuestra $entrada existe ese campo que $buscamos, coincidente con nuestra expresión regular. Lo hacemos además independientemente de si está en mayúsculas o minúsculas (/i)
* En caso de que así sea (31), guardamos en @final una cadena de caracteres compuesta de: nuestro campo (Colonia, Manzana, Casa, etc), con la primera letra puesta en mayúsculas (ucfirst), seguida de un sólo espacio en blanco y de la segunda parte del campo
* Finalmente, en la 35, unidos todas las partes y lo sacamos fuera.
Es una forma curiosa porque estamos creando expresiones regulares para extraer información de la $entrada, en el orden que nos interesa, en vez de girarla, como antes. El resultado está limpio de espacios en blanco inútiles, mayúsculas en cada campo y los campos ordenados.
Esta idea, la de extracción de información, es la que debe primar en este tipo de problemas. |
|

Mar Ago 08, 2006 11:53 am
|
 |
kidd
Creador de Perl en Español

|
Registrado: 15 Oct 2003
Mensajes: 1389
Ubicación: México
|
|
|
|
|
Hola:
Me pareció interesante tu problema y se me ocurrió una solución:
Primero inicializamos las variables. En el array @entrada vendrán todas las entradas que quieres ordenar, y en @ordenado el orden de los campos que quieres.
| Código: |
#Definimos las entradas
my @entrada = (
"depto 5 edificio A",
"Manzana 4 colonia 3 casa 21",
"depto 8 edificio 2 colonia XY",
);
#Como quieres ordenar los elementos
my @ordenado = qw(
colonia
manzana
casa
edificio
depto
);
|
Después vamos a construir las expresiones regulares de cada uno de los campos, no tiene caso hacerlo una y otra vez por ello lo hacemos de una sentada:
| Código: |
my %Expr;
map { $Expr{$_} = qr/$_ ([-a-zA-Z0-9]+)/i } @ordenado;
|
Luego vamos a crear una función a la que vamos a llamar parse_entrada(). Lo que va a hacer ésta función es recibir una línea de las entradas, separar la entrada por elementos (colonia, manzana, calle, etc) y regresarlo como una referencia a un hash:
| Código: |
sub parse_entrada{
my $string = shift;
my %Data;
for my $ordenado(@ordenado){
if($string =~ $Expr{$ordenado}){
$Data{$ordenado} = $1;
}
}
return \%Data;
}
|
Finalmente vamos a procesar cada una de nuestras entradas:
| Código: |
for my $entrada(@entrada){
my $ref_data = parse_entrada($entrada);
map { print "$_ $ref_data->{$_} " if defined($ref_data->{$_}) } @ordenado;
print "\n";
}
|
Al momento de hacer el print ya lo hace con nuestros elementos ordenados de manera correcta.
Saludos |
|

Mar Ago 08, 2006 4:24 pm
|
 |
rookie
Perlero Nuevo

|
Registrado: 01 Mar 2006
Mensajes: 39
|
|
|
|
|
Saludos a todos..
Muy interesantes las 3 soluciones propuestas... dejenme las llevo a la practica y estaré reportando como me fue .
Mil gracias por la ayuda.
Saludos |
|
Powered by phpBB © 2001, 2005 phpBB Group
|