2010-09-27 9 views
6

Tengo cadenas que son multilingües y consisten en dos idiomas que usan espacios en blanco como separador de palabras (inglés, francés, etc.) e idiomas que no (chino, japonés, coreano).Python: ¿alguna forma de realizar esta división "híbrida"() en cadenas multilingües (por ejemplo, chino e inglés)?

Dada una cadena de caracteres, quiero separar la parte en inglés/francés/etc en palabras usando espacios en blanco como separador, y separar la parte china/japonesa/coreana en caracteres individuales.

Y quiero poner todos esos componentes separados en una lista.

Algunos ejemplos serían, probablemente, dejar esto claro:

Caso 1: Inglés-única cadena. Este caso es fácil:

>>> "I love Python".split() 
['I', 'love', 'Python'] 

Caso 2: sólo china cadena:

>>> list(u"我爱蟒蛇") 
[u'\u6211', u'\u7231', u'\u87d2', u'\u86c7'] 

En este caso puedo convertir la cadena en una lista de los caracteres chinos. Pero dentro de la lista que estoy recibiendo representaciones Unicode:

[u'\u6211', u'\u7231', u'\u87d2', u'\u86c7'] 

¿Cómo consigo que muestre los caracteres reales en lugar de la Unicode? Algo como:

['我', '爱', '蟒', '蛇'] 

??

Caso 3: Una mezcla de Inglés & chino:

Quiero convertir una cadena de entrada como

"我爱Python" 

y lo convierte en una lista como esta:

['我', '爱', 'Python'] 

¿Es posible hacer algo como eso?

+0

Por desgracia, hay una misfeature en la corriente de Python 're' módulo que impide' re.split() 'para dividir en las coincidencias de longitud cero: http://stackoverflow.com/questions/2713060/why-doesnt-pythons-re-split-split-on-zero-length - coincidencias - por lo tanto, no puede usar expresiones regulares en Python para esto directamente. –

+1

Coreano usa espacios en blanco para la separación de palabras. – Leovt

Respuesta

3

Pensé en mostrar el enfoque de la expresión regular también. No me parece correcto, pero eso se debe principalmente a que todas las rarezas i18n específicas del idioma que he visto me preocupan de que una expresión regular no sea lo suficientemente flexible para todas ellas, pero es posible que no necesites ninguna de eso. (En otras palabras - overdesign.)

# -*- coding: utf-8 -*- 
import re 
def group_words(s): 
    regex = [] 

    # Match a whole word: 
    regex += [ur'\w+'] 

    # Match a single CJK character: 
    regex += [ur'[\u4e00-\ufaff]'] 

    # Match one of anything else, except for spaces: 
    regex += [ur'[^\s]'] 

    regex = "|".join(regex) 
    r = re.compile(regex) 

    return r.findall(s) 

if __name__ == "__main__": 
    print group_words(u"Testing English text") 
    print group_words(u"我爱蟒蛇") 
    print group_words(u"Testing English text我爱蟒蛇") 

En la práctica, lo que probablemente sólo desea compilar la expresión regular una vez, no en cada llamada. Una vez más, completar los detalles de la agrupación de personajes depende de usted.

+0

@Glenn Maynard. Muchas gracias. Esto es exactamente lo que necesito. ¿Podrían darme consejos sobre dónde buscar el "rango" unicode para varios idiomas? – Continuation

+0

No realmente. Los personajes no se agrupan bien por idioma; probablemente puedas elegir los rangos principales de manera bastante sencilla. –

+0

-1 @Glenn Maynard: en la configuración regional "C", esto no funciona en alfabéticamente no ASCII que no sean CJK, p. como se encuentra en francés [Requisito OP], alemán, ruso - 'u" München "' -> '[u'M ', u' \ xfc ', u'nchen']'. Lamentablemente, esto se puede solucionar utilizando el indicador 're.UNICODE' pero eso hace que' \ w' coincida con la mayoría de los caracteres CJK (categoría 'Lo'). –

2

Al formatear una lista, se muestra el repr de sus componentes. Si desea ver las cadenas de forma natural en lugar de escapado, tendrá que formatearlo usted mismo. (repr debe no estar escapando estos personajes; repr(u'我') deberían volver "u'我'", no "u'\\u6211' Al parecer esto sucede en Python 3;. 2.x solamente se ha quedado atascado con el Inglés centrada en escapar de las cadenas Unicode.)

Un básico El algoritmo que puede usar es asignar una clase de carácter a cada personaje y luego agrupar letras por clase. El código de inicio está abajo.

No utilicé un doctest para esto porque acerté algunos problemas de codificación extraños que no quiero analizar (fuera del alcance). Tendrá que implementar una función de agrupamiento correcta.

Tenga en cuenta que si está utilizando esto para el ajuste de palabras, existen otras consideraciones por idioma. Por ejemplo, no quiere romper espacios sin interrupción; quieres romper con los guiones; para los japoneses no quieres separarte き ゅ; y así.

# -*- coding: utf-8 -*- 
import itertools, unicodedata 

def group_words(s): 
    # This is a closure for key(), encapsulated in an array to work around 
    # 2.x's lack of the nonlocal keyword. 
    sequence = [0x10000000] 

    def key(part): 
     val = ord(part) 
     if part.isspace(): 
      return 0 

     # This is incorrect, but serves this example; finding a more 
     # accurate categorization of characters is up to the user. 
     asian = unicodedata.category(part) == "Lo" 
     if asian: 
      # Never group asian characters, by returning a unique value for each one. 
      sequence[0] += 1 
      return sequence[0] 

     return 2 

    result = [] 
    for key, group in itertools.groupby(s, key): 
     # Discard groups of whitespace. 
     if key == 0: 
      continue 

     str = "".join(group) 
     result.append(str) 

    return result 

if __name__ == "__main__": 
    print group_words(u"Testing English text") 
    print group_words(u"我爱蟒蛇") 
    print group_words(u"Testing English text我爱蟒蛇") 
0

Modificado solución de Glenn a caer símbolos y trabajo para los alfabetos ruso, francés, etc:

def rec_group_words(): 
    regex = [] 

    # Match a whole word: 
    regex += [r'[A-za-z0-9\xc0-\xff]+'] 

    # Match a single CJK character: 
    regex += [r'[\u4e00-\ufaff]'] 

    regex = "|".join(regex) 
    return re.compile(regex) 
1

En Python 3, que también divide el número si lo necesitas.

def spliteKeyWord(str): 
    regex = r"[\u4e00-\ufaff]|[0-9]+|[a-zA-Z]+\'*[a-z]*" 
    matches = re.findall(regex, str, re.UNICODE) 
    return matches 

print(spliteKeyWord("Testing English text我爱Python123")) 

=> [ 'Prueba', 'Inglés', 'texto', '我', '爱', 'Python', '123']

Cuestiones relacionadas