在Python中按空格分隔字符串-保留带引号的子字符串

时间:2020-03-05 18:57:55  来源:igfitidea点击:

我有一个像这样的字符串:

this is "a test"

我正在尝试在Python中编写一些内容,以按空格将其拆分,同时忽略引号内的空格。我正在寻找的结果是:

['this','is','a test']

PS。我知道我们会问"如果引号内有引号,会发生什么情况,那么在我的应用程序中,那将永远不会发生。

解决方案

回答

我们要从shlex模块拆分。

>>> import shlex
>>> shlex.split('this is "a test"')
['this', 'is', 'a test']

这应该正是我们想要的。

回答

看一下shlex模块,特别是shlex.split

>>> import shlex
>>> shlex.split('This is "a test"')
['This', 'is', 'a test']

回答

试试这个:

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

回答

如果我们不关心子字符串而不是简单的子字符串

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

性能:字符串模块似乎比字符串方法的性能更好

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

对于非常长的字符串,我们不应将整个字符串加载到内存中,而应拆分行或者使用迭代循环

回答

由于此问题是用正则表达式标记的,因此我决定尝试使用正则表达式方法。我首先用\ x00替换引号部分中的所有空格,然后按空格分割,然后将\ x00替换回每个部分中的空格。

两种版本都做同样的事情,但是splitter2比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)

回答

我在这里看到正则表达式方法看起来很复杂和/或者错误。这让我感到惊讶,因为正则表达式语法可以轻松地描述"空格或者引号引起的东西",并且大多数正则表达式引擎(包括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']

回答

嗯,似乎无法找到" Reply"按钮……无论如何,此答案基于Kate的方法,但正确地将字符串与包含转义引号的子字符串分开,并且还删除了子字符串的开始和结束引号:

[i.strip('"').strip("'") for i in re.split(r'(\s+|(?<!\)".*?(?<!\)"|(?<!\)\'.*?(?<!\)\')', string) if i.strip()]

这适用于'This is a \\\" test \\\" \\\'s的子字符串"'之类的字符串(不幸的是,必须使用疯狂的标记来防止Python删除转义符)。

如果不需要返回列表中的字符串中的结果转义符,则可以使用此函数的稍有改动的版本:

[i.strip('"').strip("'").decode('string_escape') for i in re.split(r'(\s+|(?<!\)".*?(?<!\)"|(?<!\)\'.*?(?<!\)\')', string) if i.strip()]