У меня есть строка, которая выглядит примерно так:
this is "a test"
Я пытаюсь написать что-то в Python, чтобы разделить его на пространство, игнорируя пробелы внутри кавычек. Результат, который я ищу, это:
['this','is','a test']
PS. Я знаю, что вы спросите: "Что произойдет, если в кавычках есть кавычки, ну, в моем приложении это никогда не произойдет.
Вы хотите разделить, из shlex.
>>> import shlex
>>> shlex.split('this is "a test"')
['this', 'is', 'a test']
Это должно делать именно то, что вы хотите.
shlex.split()
не работает для юникода. Например, shlex.split(u"test test")
создает дерьмо, такое как 't\x00e\x00s\x00t\x00', '\x00t\x00e\x00s\x00t\x00'
, подробности об ошибках см. В следующем обсуждении проблемы . python.org/issue6988
Посмотрите модуль shlex
, особенно shlex.split
.
>>> import shlex
>>> shlex.split('This is "a test"')
['This', 'is', 'a test']
Я вижу подходы regex здесь, которые выглядят сложными и/или неправильными. Это меня удивляет, потому что синтаксис регулярных выражений может легко описать "пробельные или вещественные окружения за кавычками", и большинство движков регулярных выражений (включая Python) могут разбиваться на регулярное выражение. Итак, если вы собираетесь использовать регулярные выражения, почему бы просто не сказать точно, что вы имеете в виду?
test = 'this is "a test"' # or "this is 'a test'"
# pieces = [p for p in re.split("( |[\\\"'].*[\\\"'])", test) if p.strip()]
# From comments, use this:
pieces = [p for p in re.split("( |\\\".*?\\\"|'.*?')", test) if p.strip()]
Пояснение:
[\\\"'] = double-quote or single-quote
.* = anything
( |X) = space or X
.strip() = remove space and empty-string separators
shlex, вероятно, предоставляет больше возможностей.
В зависимости от вашего варианта использования вы также можете проверить модуль csv:
import csv
lines = ['this is "a string"', 'and more "stuff"']
for row in csv.reader(lines, delimiter=" "):
print row
Выход:
['this', 'is', 'a string']
['and', 'more', 'stuff']
Поскольку этот вопрос помечен регулярным выражением, я решил попробовать подход с регулярным выражением. Сначала я заменил все пробелы в частях кавычек на \x00, затем разделил пробелами, а затем заменил \x00 на пробелы в каждой части.
Обе версии делают то же самое, но сплиттер немного читаем, а затем splitter2.
import re
s = 'this is "a test" some text "another test"'
def splitter(s):
def replacer(m):
return m.group(0).replace(" ", "\x00")
parts = re.sub('".+?"', replacer, s).split()
parts = [p.replace("\x00", " ") for p in parts]
return parts
def splitter2(s):
return [p.replace("\x00", " ") for p in re.sub('".+?"', lambda m: m.group(0).replace(" ", "\x00"), s).split()]
print splitter2(s)
Я использую shlex.split для обработки 70 000 000 строк журнала кальмаров, это так медленно. Поэтому я переключился на re.
Попробуйте это, если у вас есть проблемы с производительностью с помощью shlex.
import re
def line_split(line):
return re.findall(r'[^"\s]\S*|".+?"', line)
Чтобы сохранить кавычки, используйте эту функцию:
def getArgs(s):
args = []
cur = ''
inQuotes = 0
for char in s.strip():
if char == ' ' and not inQuotes:
args.append(cur)
cur = ''
elif char == '"' and not inQuotes:
inQuotes = 1
cur += char
elif char == '"' and inQuotes:
inQuotes = 0
cur += char
else:
cur += char
args.append(cur)
return args
Чтобы обойти проблемы с unicode в некоторых версиях Python 2, я предлагаю:
from shlex import split as _split
split = lambda a: [b.decode('utf-8') for b in _split(a.encode('utf-8'))]
split = lambda a: [b.decode('utf-8') for b in _split(a)]
иначе вы получите: UnicodeDecodeError: 'ascii' codec can't decode byte ... in position ...: ordinal not in range(128)
Проблемы с unicode с shlex, описанные выше (верхний ответ), кажутся разрешенными (косвенно) в 2.7.2+ в соответствии с http://bugs.python.org/issue6988#msg146200
(отдельный ответ, потому что я не могу комментировать)
Хмм, похоже, не может найти кнопку "Ответить"... в любом случае, этот ответ основан на подходе Кейт, но правильно разбивает строки с подстроками, содержащими экранированные кавычки, а также удаляет стартовые и конечные кавычки подстроки:
[i.strip('"').strip("'") for i in re.split(r'(\s+|(?<!\\)".*?(?<!\\)"|(?<!\\)\'.*?(?<!\\)\')', string) if i.strip()]
Это работает с строками типа 'This is " a \\\"test\\\"\\\ substring"'
(к сожалению, безумная разметка необходима, чтобы Python не удалял экраны).
Если результирующие escape-последовательности в строках в возвращаемом списке не нужны, вы можете использовать эту слегка измененную версию функции:
[i.strip('"').strip("'").decode('string_escape') for i in re.split(r'(\s+|(?<!\\)".*?(?<!\\)"|(?<!\\)\'.*?(?<!\\)\')', string) if i.strip()]
Я предлагаю:
тестовая строка:
s = 'abc "ad" \'fg\' "kk\'rdt\'" zzz"34"zzz "" \'\''
для захвата также "и" ":
import re
re.findall(r'"[^"]*"|\'[^\']*\'|[^"\'\s]+',s)
результат:
['abc', '"ad"', "'fg'", '"kk\'rdt\'"', 'zzz', '"34"', 'zzz', '""', "''"]
игнорировать пустые "и":
import re
re.findall(r'"[^"]+"|\'[^\']+\'|[^"\'\s]+',s)
результат:
['abc', '"ad"', "'fg'", '"kk\'rdt\'"', 'zzz', '"34"', 'zzz']
re.findall("(?:\".*?\"|'.*?'|[^\s'\"]+)", s)
.
Если вам не нужны подстроки, чем простые
>>> 'a short sized string with spaces '.split()
Производительность:
>>> s = " ('a short sized string with spaces '*100).split() "
>>> t = timeit.Timer(stmt=s)
>>> print "%.2f usec/pass" % (1000000 * t.timeit(number=100000)/100000)
171.39 usec/pass
Или строковый модуль
>>> from string import split as stringsplit;
>>> stringsplit('a short sized string with spaces '*100)
Производительность: модуль String работает лучше, чем строковые методы
>>> s = "stringsplit('a short sized string with spaces '*100)"
>>> t = timeit.Timer(s, "from string import split as stringsplit")
>>> print "%.2f usec/pass" % (1000000 * t.timeit(number=100000)/100000)
154.88 usec/pass
Или вы можете использовать движок RE
>>> from re import split as resplit
>>> regex = '\s+'
>>> medstring = 'a short sized string with spaces '*100
>>> resplit(regex, medstring)
Производительность
>>> s = "resplit(regex, medstring)"
>>> t = timeit.Timer(s, "from re import split as resplit; regex='\s+'; medstring='a short sized string with spaces '*100")
>>> print "%.2f usec/pass" % (1000000 * t.timeit(number=100000)/100000)
540.21 usec/pass
Для очень длинных строк вы не должны загружать всю строку в память и вместо этого разделять строки или использовать итеративный цикл
Попробуйте следующее:
def adamsplit(s):
result = []
inquotes = False
for substring in s.split('"'):
if not inquotes:
result.extend(substring.split())
else:
result.append(substring)
inquotes = not inquotes
return result
Некоторые тестовые строки:
'This is "a test"' -> ['This', 'is', 'a test']
'"This is \'a test\'"' -> ["This is 'a test'"]