ignorar espacios al comparar cadenas en python

Estoy usando el paquete de difflib python. No importa si configuro el argumento isjunk , las proporciones calculadas son las mismas. ¿No se ignora la diferencia de espacios cuando isjunk es lambda x: x == " " ?

 In [193]: difflib.SequenceMatcher(isjunk=lambda x: x == " ", a="abc", b="a bc").ratio() Out[193]: 0.8888888888888888 In [194]: difflib.SequenceMatcher(a="abc", b="a bc").ratio() Out[194]: 0.8888888888888888 

isjunk funciona un poco diferente de lo que piensas. En general, isjunk simplemente identifica uno o más caracteres que no afectan la longitud de una coincidencia pero que aún están incluidos en el recuento total de caracteres. Por ejemplo, considere lo siguiente:

 >>> SequenceMatcher(lambda x: x in "abcd", " abcd", "abcd abcd").ratio() 0.7142857142857143 

Los primeros cuatro caracteres de la segunda cadena ( "abcd" ) son todos ignorables, por lo que la segunda cadena puede compararse con la primera cadena que comienza con el espacio. Comenzando con el espacio tanto en la primera cadena como en la segunda cadena, luego, el SequenceMatcher anterior encuentra diez caracteres coincidentes (cinco en cada cadena) y 4 caracteres no coincidentes (los primeros cuatro caracteres ignorables en la segunda cadena). Esto le da una relación de 10/14 (0.7142857142857143).

En su caso, entonces, la primera cadena "abc" coincide con la segunda cadena en los índices 0, 1 y 2 (con valores "ab" ). El índice 3 de la primera cadena ( " " ) no tiene una coincidencia, pero se ignora con respecto a la longitud de la coincidencia. Como el espacio se ignora, el índice 4 ( "c" ) coincide con el índice 3 de la segunda cadena. Por lo tanto, 8 de tus 9 caracteres coinciden, lo que te da una proporción de 0.88888888888888 .

Es posible que desee probar esto en su lugar:

 >>> c = a.replace(' ', '') >>> d = b.replace(' ', '') >>> difflib.SequenceMatcher(a=c, b=d).ratio() 1.0 

Puedes ver lo que considera ser bloques coincidentes:

 >>> difflib.SequenceMatcher(isjunk=lambda x: x == " ", a="abc", b="a bc").get_matching_blocks() [Match(a=0, b=0, size=3), Match(a=4, b=3, size=1), Match(a=5, b=4, size=0)] 

Los dos primeros te dicen que coincide “ab” con “ab” y “c” con “c”. (El último es trivial).

La pregunta es por qué “ab” puede ser emparejado. Encontré la respuesta a esto en el código. Primero, el algoritmo encuentra un montón de bloques coincidentes llamando repetidamente a find_longest_match. Lo que es notable acerca de find_longest_match es que permite que exista el carácter no deseado en los extremos de la cadena:

 If isjunk is defined, first the longest matching block is determined as above, but with the additional restriction that no junk element appears in the block. Then that block is extended as far as possible by matching (only) junk elements on both sides. So the resulting block never matches on junk except as identical junk happens to be adjacent to an "interesting" match. 

Esto significa que primero considera que “a” y “b” son coincidencias (permitiendo el carácter de espacio al final de “a” y al principio de “b”).

Luego, la parte interesante: el código hace una última comprobación para ver si alguno de los bloques es adyacente, y los fusiona si lo son. Ver este comentario en el código:

  # It's possible that we have adjacent equal blocks in the # matching_blocks list now. Starting with 2.5, this code was added # to collapse them. 

Básicamente, es unir “a” y “b”, luego fusionar esos dos bloques en “ab” y llamar a eso una coincidencia, a pesar de que el carácter de espacio es basura.

El número de coincidencias es el mismo para ambas invocaciones (3). Puedes verificar esto usando:

 print difflib.SequenceMatcher(isjunk=lambda x: x == " ", a="abc", b="a bc").get_matching_blocks() print difflib.SequenceMatcher(a="abc", b="a bc").get_matching_blocks() 

(En realidad, son los mismos debido a la forma en que el algoritmo se “ajusta” para coincidencias adyacentes).

Dado que la proporción solo depende de la longitud de estas coincidencias y la longitud de los originales (se incluye la chatarra), se obtienen las mismas raciones.