2010-01-25 16 views
123

Aquí está la manera más simple de explicar esto. Aquí es lo que estoy usando:En Python, ¿cómo divido una cadena y guardo los separadores?

re.split('\W', 'foo/bar spam\neggs') 
-> ['foo', 'bar', 'spam', 'eggs'] 

aquí es lo que quiero:

someMethod('\W', 'foo/bar spam\neggs') 
-> ['foo', '/', 'bar', ' ', 'spam', '\n', 'eggs'] 

La razón es que quiero dividir una cadena en tokens, manipularlo, a continuación, poner de nuevo juntos de nuevo.

+1

lo que hace '\ W' significa? Fallé en google. – Ooker

+2

Un carácter _no-word_ [ver aquí para más detalles] (https://docs.python.org/2/library/re.html#regular-expression-syntax) – Russell

Respuesta

168
>>> re.split('(\W)', 'foo/bar spam\neggs') 
['foo', '/', 'bar', ' ', 'spam', '\n', 'eggs'] 
+12

Eso es genial. No sabía que re.split hizo eso con grupos de captura. –

+7

@Laurence: Bueno, está documentado: http://docs.python.org/library/re.html#re.split: "Separar cadena por las apariciones del patrón. Si se utilizan paréntesis de captura en el patrón, entonces el texto de todos los grupos en el patrón también se devuelven como parte de la lista resultante ". –

+17

Está seriamente infravalorado. He estado usando Python durante 14 años y acabo de descubrirlo. – smci

1

También puede dividir una cadena con una serie de cadenas en lugar de una expresión regular, como esto:

def tokenizeString(aString, separators): 
    #separators is an array of strings that are being used to split the the string. 
    #sort separators in order of descending length 
    separators.sort(key=len) 
    listToReturn = [] 
    i = 0 
    while i < len(aString): 
     theSeparator = "" 
     for current in separators: 
      if current == aString[i:i+len(current)]: 
       theSeparator = current 
     if theSeparator != "": 
      listToReturn += [theSeparator] 
      i = i + len(theSeparator) 
     else: 
      if listToReturn == []: 
       listToReturn = [""] 
      if(listToReturn[-1] in separators): 
       listToReturn += [""] 
      listToReturn[-1] += aString[i] 
      i += 1 
    return listToReturn 


print(tokenizeString(aString = "\"\"\"hi\"\"\" hello + world += (1*2+3/5) '''hi'''", separators = ["'''", '+=', '+', "/", "*", "\\'", '\\"', "-=", "-", " ", '"""', "(", ")"])) 
2
# This keeps all separators in result 
########################################################################## 
import re 
st="%%(c+dd+e+f-1523)%%7" 
sh=re.compile('[\+\-//\*\<\>\%\(\)]') 

def splitStringFull(sh, st): 
    ls=sh.split(st) 
    lo=[] 
    start=0 
    for l in ls: 
    if not l : continue 
    k=st.find(l) 
    llen=len(l) 
    if k> start: 
     tmp= st[start:k] 
     lo.append(tmp) 
     lo.append(l) 
     start = k + llen 
    else: 
     lo.append(l) 
     start =llen 
    return lo 
    ############################# 

li= splitStringFull(sh , st) 
['%%(', 'c', '+', 'dd', '+', 'e', '+', 'f', '-', '1523', ')%%', '7'] 
7

Otra solución sin expresión regular que funciona bien en Python 3

# Split strings and keep separator 
test_strings = ['<Hello>', 'Hi', '<Hi> <Planet>', '<', ''] 

def split_and_keep(s, sep): 
    if not s: return [''] # consistent with string.split() 

    # Find replacement character that is not used in string 
    # i.e. just use the highest available character plus one 
    # Note: This fails if ord(max(s)) = 0x10FFFF (ValueError) 
    p=chr(ord(max(s))+1) 

    return s.replace(sep, sep+p).split(p) 

for s in test_strings: 
    print(split_and_keep(s, '<')) 


# If the unicode limit is reached it will fail explicitly 
unicode_max_char = chr(1114111) 
ridiculous_string = '<Hello>'+unicode_max_char+'<World>' 
print(split_and_keep(ridiculous_string, '<')) 
9

Si está dividiendo en nueva línea, use splitlines(True) .

>>> 'line 1\nline 2\nline without newline'.splitlines(True) 
['line 1\n', 'line 2\n', 'line without newline'] 

(No es una solución general, pero la adición de esto aquí por si alguien viene aquí sin darse cuenta existía este método.)

1

Si uno quiere dividir la cadena, manteniendo los separadores por expresiones regulares y sin grupo de captura:

def finditer_with_separators(regex, s): 
    matches = [] 
    prev_end = 0 
    for match in regex.finditer(s): 
     match_start = match.start() 
     if (prev_end != 0 or match_start > 0) and match_start != prev_end: 
      matches.append(s[prev_end:match.start()]) 
     matches.append(match.group()) 
     prev_end = match.end() 
    if prev_end < len(s): 
     matches.append(s[prev_end:]) 
    return matches 

regex = re.compile(r"[\(\)]") 
matches = finditer_with_separators(regex, s) 

Si se supone que la expresión regular está envuelto en un grupo de captura:

def split_with_separators(regex, s): 
    matches = list(filter(None, regex.split(s))) 
    return matches 

regex = re.compile(r"([\(\)])") 
matches = split_with_separators(regex, s) 

Ambas formas también eliminarán los grupos vacíos que son inútiles y molestos en la mayoría de los casos.

1

Si sólo tiene 1 separador, se puede emplear listas por comprensión:

text = 'foo,bar,baz,qux' 
sep = ',' 

Anexión/separador de anteponiendo:

result = [x+sep for x in text.split(sep)] 
#['foo,', 'bar,', 'baz,', 'qux,'] 
# to get rid of trailing 
result[-1] = result[-1].strip(sep) 
#['foo,', 'bar,', 'baz,', 'qux'] 

result = [sep+x for x in text.split(sep)] 
#[',foo', ',bar', ',baz', ',qux'] 
# to get rid of trailing 
result[0] = result[0].strip(sep) 
#['foo', ',bar', ',baz', ',qux'] 

separador ya que es propio elemento:

result = [u for x in text.split(sep) for u in (x, sep)] 
#['foo', ',', 'bar', ',', 'baz', ',', 'qux', ','] 
results = result[:-1] # to get rid of trailing 
Cuestiones relacionadas