2011-01-03 22 views
22

¿Python tiene un built-in (es decir, en las bibliotecas estándar) para hacer una división en cadenas que produce un iterador en lugar de una lista? Tengo en mente trabajar en cadenas muy largas y no necesitar consumir la mayor parte de la cadena.División de una cadena en un iterador

+2

"no es necesario consumir la mayor parte de la cadena"? ¿Qué significa esto? El objeto de cadena está todo en la memoria, ¿verdad? Como todo está en la memoria, y ya es una secuencia, no se necesita nada para iterar sobre los caracteres. ¿Puedes definir lo que quieres decir con "no es necesario consumir la mayor parte de la cadena"? –

+0

Sí, la cadena ya está en la memoria. Pero no necesito atravesar toda la cadena para descubrir dónde dividir o crear las subcadenas resultantes de la división. –

+1

Quizás necesite un tokenizador o escáner de algún tipo que proporcione un iterador. La respuesta a continuación con la solución de expresión regular podría funcionar. –

Respuesta

15

No se divide directamente cadenas como tal, pero el módulo re tiene re.finditer() (y el método correspondiente finditer() en cualquier expresión regular compilada).

@Zero pedí un ejemplo:

>>> import re 
>>> s = "The quick brown\nfox" 
>>> for m in re.finditer('\S+', s): 
...  print(m.span(), m.group(0)) 
... 
(0, 3) The 
(4, 9) quick 
(13, 18) brown 
(19, 22) fox 
+2

Sería útil un ejemplo de cómo usar 're.finditer()' para iterar cadenas divididas. – Zero

+1

@Zero, no es exactamente difícil, pero aquí tienes. – Duncan

5

Como S. Lott, no sé muy bien lo que quiere. Aquí está el código que puede ayudar:

s = "This is a string." 
for character in s: 
    print character 
for word in s.split(' '): 
    print word 

También hay s.index() y s.find() para encontrar el siguiente carácter.


Después: Bien, algo como esto.

>>> def tokenizer(s, c): 
...  i = 0 
...  while True: 
...   try: 
...    j = s.index(c, i) 
...   except ValueError: 
...    yield s[i:] 
...    return 
...   yield s[i:j] 
...   i = j + 1 
... 
>>> for w in tokenizer(s, ' '): 
...  print w 
... 
This 
is 
a 
string. 
+1

Ver la aclaración en los comentarios. Esto no responde la pregunta. – marcog

+0

También está pidiendo explícitamente un * built-in * –

+3

@ 7vies: Pensé que esto era mejor que decir "No" o decir "Usar expresiones regulares (es decir, la respuesta anterior)." – hughdbrown

0

Se podría utilizar algo como SPARK (que ha sido absorbido en la distribución de Python en sí, aunque no importables de la librería estándar), pero en última instancia se usa expresiones regulares, así por lo Duncan's answer, posiblemente, le serviría igual de bien si fue tan fácil como simplemente "dividir en espacios en blanco".

La otra opción, mucho más ardua, sería escribir su propio módulo de Python en C para hacerlo si realmente quisiera velocidad, pero esa es una inversión de mucho mayor tiempo, por supuesto.

3

Si no necesita consumir toda la cadena, es porque está buscando algo específico, ¿no? Luego solo busca eso, con re o .find() en lugar de dividir. De esa forma puedes encontrar la parte de la cadena que te interesa y dividirla.

+0

En la aplicación que tenía en mente, quería dividir el espacio en blanco, verificar la tercera subcadena, dependiendo de lo que fuera, verificar la cuarta o sexta subcadena, y luego posiblemente procesar el resto de la cadena. –

+2

@pythonic metáfora: Sí, si esa cadena es * realmente * larga, es posible que desee utilizar 're' o' encontrar'. En el otro caso, solo divídelo en espacios en blanco. No lo sé, pero para mí su pregunta parece ser una optimización prematura. ;) Así que tienes que perfilarlo para estar seguro. –

+3

@pythonic metáfora: para texto normal que solo es una optimización prematura. El texto comienza a ser "grande" en algún lugar >> 10MB. Para la aplicación que describiste, simplemente iría con 'text.split (None, 6)' para obtener las primeras 6 palabras. Si tiene que dividir todo el texto de todos modos, solo hágalo de inmediato. –

0

Mire itertools. Contiene elementos como takewhile, islice y groupby que le permiten dividir un iterable (una cadena es iterable) en otro iterable en función de los índices o una clase de condición booleana.

0

No hay un análogo basado en un iterador incorporado de str.split. Dependiendo de sus necesidades usted podría hacer una lista iterador:

iterator = iter("abcdcba".split("b")) 
iterator 
# <list_iterator at 0x49159b0> 
next(iterator) 
# 'a' 

Sin embargo, una herramienta de esta biblioteca de terceros probable que ofrece lo que quiere, more_itertools.split_at. Vea también this post para un ejemplo.