2009-10-27 10 views
8

Tengo un lote de cadenas que necesito cortar. Básicamente son un descriptor seguido de códigos. Solo quiero conservar el descriptor.¿Corta una cadena después de una frase determinada?

'a descriptor dps 23 fd' 
'another 23 fd' 
'and another fd' 
'and one without a code' 

los códigos anteriores son dps, y 23fd. Pueden venir en cualquier orden, no están relacionados entre sí y pueden no existir en absoluto (como en el último caso).

La lista de códigos es fija (o puede predecirse, al menos), por lo que suponiendo que un código nunca se utiliza dentro de un descriptor legítimo, ¿cómo puedo quitar todo después de la primera instancia de un código?

Estoy usando Python.

+4

En sus ejemplos, ¿cuáles son los códigos, y lo que debe buscar la salida como? –

Respuesta

21

La respuesta corta, como @ THC4K señala en un comentario:

string.split(pattern, 1)[0] 

donde string es su cadena original, pattern es su patrón de "ruptura", 1 indica que dividir no más de 1 hora y [0] significa tomar el primer elemento devuelto por división.

en acción:

>>> s = "a descriptor 23 fd" 
>>> s.split("23", 1)[0] 
'a descriptor ' 
>>> s.split("fdasfdsafdsa", 1)[0] 
'a descriptor 23 fd' 

Esta es una forma mucho más corta de expresar lo que había escrito antes, lo que voy a seguir aquí de todos modos.

Y si tiene que quitar varios patrones, esto es un gran candidato para el reduce orden interna:

>>> string = "a descriptor dps foo 23 bar fd quux" 
>>> patterns = ["dps", "23", "fd"] 
>>> reduce(lambda s, pat: s.split(pat, 1)[0], patterns, string) 
'a descriptor ' 
>>> reduce(lambda s, pat: s.split(pat, 1)[0], patterns, "uiopuiopuiopuipouiop") 
'uiopuiopuiopuipouiop' 

Esto básicamente dice: para cada pat en patterns: tomar string y repetidamente aplicar string.split(pat, 1)[0] (como se ha explicado anteriormente), operando sobre el resultado del valor devuelto previamente cada vez. Como puede ver, si ninguno de los patrones está en la cadena, la cadena original aún se devuelve.


La respuesta más simple es una rebanada lista/cadena combinada con una string.find:

>>> s = "a descriptor 23 fd" 
>>> s[:s.find("fd")] 
'a descriptor 23 ' 
>>> s[:s.find("23")] 
'a descriptor ' 
>>> s[:s.find("gggfdf")] # <-- look out! last character got cut off 
'a descriptor 23 f' 

Un mejor enfoque (para evitar cortar el último carácter de un patrón de falta cuando s.find devuelve -1) podría ser para envolver en una función simple:

>>> def cutoff(string, pattern): 
...  idx = string.find(pattern) 
...  return string[:idx if idx != -1 else len(string)] 
... 
>>> cutoff(s, "23") 
'a descriptor ' 
>>> cutoff(s, "asdfdsafdsa") 
'a descriptor 23 fd' 

la sintaxis [:s.find(x)] significa tomar la parte de la cadena desde el índice 0 hasta el derecho-h y lado del colon; y en este caso, el RHS es el resultado de s.find, que devuelve el índice de la cadena que pasó.

+0

'the_string.split (patrón, 1) [0]' es lo mismo, creo. –

+0

Buen ojo, @ THC4k. –

+0

Marque con la división, ¿qué sucede si no hay ningún código en la cadena (una posibilidad leve aunque existe)? Y con ambos no existe una mejor manera de verificar múltiples códigos a la vez. Ambos ejemplos solo parecen tratar con uno a la vez. – Oli

2

usted parece estar describiendo algo como esto:

def get_descriptor(text): 
    codes = ('12', 'dps', '23') 
    for c in codes: 
     try: 
      return text[:text.index(c)].rstrip() 
     except ValueError: 
      continue 

    raise ValueError("No descriptor found in `%s'" % (text)) 

P. ej,

>>> get_descriptor('a descriptor dps 23 fd') 
'a descriptor' 
1
codes = ('12', 'dps', '23') 

def get_descriptor(text): 
    words = text.split() 
    for c in codes: 
     if c in words: 
      i = words.index(c) 
      return " ".join(words[:i]) 
    raise ValueError("No code found in `%s'" % (text)) 
1

probablemente haría uso de una expresión regular para hacer esto:

>>> import re 
>>> descriptors = ('foo x', 'foo y', 'bar $', 'baz', 'bat') 
>>> data = ['foo x 123', 'foo y 123', 'bar $123', 'baz 123', 'bat 123', 'nothing'] 
>>> p = re.compile("(" + "|".join(map(re.escape, descriptors)) + ")") 
>>> for s in data: 
     m = re.match(p, s) 
     if m: print m.groups()[0] 
foo x 
foo y 
bar $ 
baz 
bat 

No era del todo claro si quieres lo que estás extracción para incluir texto que precede a los descriptores, o si espera que cada línea de texto comience con un descriptor; lo anterior trata de este último. En el primer caso, basta con cambiar el patrón ligeramente para que sea capturar todos los caracteres antes de la primera aparición de un descriptor:

>>> p = re.compile("(.*(" + "|".join(map(re.escape, descriptors)) + "))") 
0

Aquí es una respuesta que funcione para todos los códigos en lugar de obligar a que llame a la función de cada código, y es un poco más simple que algunas de las respuestas anteriores. También funciona para todos tus ejemplos.

strings = ('a descriptor dps 23 fd', 'another 23 fd', 'and another fd', 
        'and one without a code') 
codes = ('dps', '23', 'fd') 

def strip(s): 
    try: 
     return s[:min(s.find(c) for c in codes if c in s)] 
    except ValueError: 
     return s 

print map(strip, strings) 

Salida:

['a descriptor ', 'another ', 'and another ', 'and one without a code'] 

Creo que esto satisface todos los criterios.

Editar: Me di cuenta rápidamente que podía quitar el intento de captura si no te gusta esperando la excepción:

def strip(s): 
    if not any(c in s for c in codes): 
     return s 
    return s[:min(s.find(c) for c in codes if c in s)] 
Cuestiones relacionadas