Нарезать строку после определенной фразы? - PullRequest
9 голосов
/ 28 октября 2009

У меня есть партия струн, которую мне нужно срезать. Они в основном дескриптор, за которым следуют коды. Я только хочу сохранить дескриптор.

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

Коды выше dps, 23 и fd. Они могут приходить в любом порядке, не связаны друг с другом и могут вообще не существовать (как в последнем случае).

Список кодов фиксирован (или, по крайней мере, может быть предсказан), поэтому, если код никогда не используется в допустимом дескрипторе, как я могу удалить все после первого экземпляра кода.

Я использую Python.

Ответы [ 6 ]

24 голосов
/ 28 октября 2009

Краткий ответ, как @ THC4K указывает в комментарии:

string.split(pattern, 1)[0]

, где string - исходная строка, pattern - шаблон прерывания, 1 указывает на разделение не более 1 раза, а [0] означает получение первого элемента, возвращенного split.

В действии:

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

Это гораздо более короткий способ выразить то, что я написал ранее, и я все равно буду здесь его хранить.

И если вам нужно удалить несколько шаблонов, это отличный кандидат для встроенного reduce:

>>> 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'

Это в основном гласит: для каждого pat в patterns: возьмите string и повторно примените string.split(pat, 1)[0] (как объяснено выше), работая каждый раз с результатом ранее возвращенного значения. Как видите, если в строке нет ни одного из шаблонов, исходная строка все равно возвращается.


Самый простой ответ - фрагмент списка / строки в сочетании с 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'

Лучшим подходом (чтобы не обрезать последний символ в пропущенном шаблоне, когда s.find возвращает -1) может быть обтекание простой функцией:

>>> 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'

Синтаксис [:s.find(x)] означает перевод части строки из индекса 0 в правую часть двоеточия; и в этом случае RHS является результатом s.find, который возвращает индекс строки, которую вы передали.

2 голосов
/ 28 октября 2009

Вы, кажется, описываете что-то вроде этого:

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))

Например,

>>> get_descriptor('a descriptor dps 23 fd')
'a descriptor'
1 голос
/ 28 октября 2009

Я бы, вероятно, использовал для этого регулярное выражение:

>>> 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

Мне было не совсем ясно, хотите ли вы, чтобы извлекаемый текст содержал текст, предшествующий дескрипторам, или ожидаете, что каждая строка текста будет начинаться с дескриптора; вышеупомянутое имеет дело с последним. В первом случае просто слегка измените шаблон, чтобы он захватывал все символы до первого появления дескриптора:

>>> p = re.compile("(.*(" + "|".join(map(re.escape, descriptors)) + "))")
1 голос
/ 28 октября 2009
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))
0 голосов
/ 25 июня 2018
    def crop_string(string, pattern):
        del_items = []
        for indx, val in enumerate(pattern):
            a = string.split(val, 1)
            del_items.append(a[indx])

        for del_item in del_items:
            string = string.replace(del_item, "")
        return string

пример:

Я хочу обрезать строку и получить из нее только массив ..

strin = "crop the array [1,2,3,4,5]
pattern["[","]"]

использование:

a = crop_string(strin ,pattern )
print a 

# --- Prints "[1,2,3,4,5]"
0 голосов
/ 28 октября 2009

Вот ответ, который работает для всех кодов, а не заставляет вас вызывать функцию для каждого кода, и немного проще, чем некоторые из ответов выше. Это также работает для всех ваших примеров.

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)

Выход:

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

Я считаю, что это удовлетворяет всем вашим критериям.

Редактировать: Я быстро понял, что вы можете удалить пробную версию, если вам не нравится ожидать исключения:

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)]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...