如何拆分但在python中忽略带引号的字符串中的分隔符?

Syl*_*ain 63 python regex

我需要在分号上分割这样的字符串.但我不想拆分字符串('或')内的分号.我没有解析文件;只是一个没有换行符的简单字符串.

part 1;"this is ; part 2;";'this is ; part 3';part 4;this "is ; part" 5

结果应该是:

  • 第1部分
  • "这是;第2部分;"
  • '这是 ; 第3部分'
  • 第4部分
  • 这是"是;部分"5

我想这可以用正则表达式完成,但如果没有; 我对另一种方法持开放态度.

Dun*_*can 50

大多数答案似乎都过于复杂.你并不需要反向引用.你并不需要依赖于是否re.findall给人重叠的匹配.鉴于无法使用csv模块解析输入,因此正则表达式是唯一可行的方法,您只需要使用与字段匹配的模式调用re.split.

请注意,匹配字段比匹配分隔符要容易得多:

import re
data = """part 1;"this is ; part 2;";'this is ; part 3';part 4;this "is ; part" 5"""
PATTERN = re.compile(r'''((?:[^;"']|"[^"]*"|'[^']*')+)''')
print PATTERN.split(data)[1::2]
Run Code Online (Sandbox Code Playgroud)

输出是:

['part 1', '"this is ; part 2;"', "'this is ; part 3'", 'part 4', 'this "is ; part" 5']
Run Code Online (Sandbox Code Playgroud)

正如Jean-Luc Nacif Coelho正确指出的那样,这将无法正确处理空组.取决于可能或不重要的情况.如果它确实关系有可能通过处理一下,例如,更换';;'';<marker>;'地方<marker>必须是一些字符串(没有分号),你知道不会出现在分割前的数据.您还需要在以下情况后恢复数据:

>>> marker = ";!$%^&;"
>>> [r.replace(marker[1:-1],'') for r in PATTERN.split("aaa;;aaa;'b;;b'".replace(';;', marker))[1::2]]
['aaa', '', 'aaa', "'b;;b'"]
Run Code Online (Sandbox Code Playgroud)

然而,这是一个kludge.有更好的建议吗?

  • 非常感谢——我遇到了同样的问题,但是有空格,所以我只是用分号代替了空格,它工作得很好。 (2认同)

Ala*_*ore 32

re.split(''';(?=(?:[^'"]|'[^']*'|"[^"]*")*$)''', data)
Run Code Online (Sandbox Code Playgroud)

每次找到分号时,前瞻扫描整个剩余的字符串,确保有偶数个单引号和偶数个双引号.(双引号字段内的单引号,反之亦然,将被忽略.)如果前瞻成功,则分号是分隔符.

Duncan的解决方案不同,Duncan的解决方案与字段匹配而不是分隔符,这个字段与空字段没有问题.(甚至不是最后一个:与许多其他split实现不同,Python不会自动丢弃尾随的空字段.)

  • 请注意,这似乎无法处理转义引号,例如 `'"scarlett o\'hara"; 瑞德·巴特勒”——而邓肯的解决方案则如此。 (2认同)

小智 17

>>> a='A,"B,C",D'
>>> a.split(',')
['A', '"B', 'C"', 'D']

It failed. Now try csv module
>>> import csv
>>> from StringIO import StringIO
>>> data = StringIO(a)
>>> data
<StringIO.StringIO instance at 0x107eaa368>
>>> reader = csv.reader(data, delimiter=',') 
>>> for row in reader: print row
... 
['A,"B,C",D']
Run Code Online (Sandbox Code Playgroud)

  • 在Python3.0中,使用“from io import StringIO”而不是“StringIO”。来自 https://docs.python.org/3.0/whatsnew/3.0.html “StringIO 和 cStringIO 模块消失了。相反,导入 io 模块并分别使用 io.StringIO 或 io.BytesIO 来处理文本和数据。” (4认同)
  • 我向下滚动页面到目前为止回答完全相同的事情,这是一个耻辱这个答案是如此下降,csv模块绝对是正确的方法去 (2认同)

Pau*_*McG 11

这是一个带注释的pyparsing方法:

from pyparsing import (printables, originalTextFor, OneOrMore, 
    quotedString, Word, delimitedList)

# unquoted words can contain anything but a semicolon
printables_less_semicolon = printables.replace(';','')

# capture content between ';'s, and preserve original text
content = originalTextFor(
    OneOrMore(quotedString | Word(printables_less_semicolon)))

# process the string
print delimitedList(content, ';').parseString(test)
Run Code Online (Sandbox Code Playgroud)

['part 1', '"this is ; part 2;"', "'this is ; part 3'", 'part 4', 
 'this "is ; part" 5']
Run Code Online (Sandbox Code Playgroud)

通过使用提供的pyparsing quotedString,您还可以获得对转义引号的支持.

您还不清楚如何在分号分隔符之前或之后处理前导空格,并且示例文本中的所有字段都没有.Pyparsing会将"a; b; c"解析为:

['a', 'b', 'c']
Run Code Online (Sandbox Code Playgroud)


Sim*_*lan 9

您似乎有一个分号分隔的字符串.为什么不使用该csv模块来完成所有的艰苦工作?

在我的头顶,这应该工作

import csv 
from StringIO import StringIO 

line = '''part 1;"this is ; part 2;";'this is ; part 3';part 4;this "is ; part" 5'''

data = StringIO(line) 
reader = csv.reader(data, delimiter=';') 
for row in reader: 
    print row 
Run Code Online (Sandbox Code Playgroud)

这应该给你一些类似的东西
("part 1", "this is ; part 2;", 'this is ; part 3', "part 4", "this \"is ; part\" 5")

编辑:
不幸的是,由于混合字符串引号(单引号和双引号),这不起作用(即使你按照我的意图使用StringIO).你真正得到的是

['part 1', 'this is ; part 2;', "'this is ", " part 3'", 'part 4', 'this "is ', ' part" 5'].

如果您可以将数据更改为仅在适当的位置包含单引号或双引号,它应该可以正常工作,但这种方式可以消除这个问题.

  • csv模块不仅不处理多种报价类型,而且还坚持要求字段完全引用或根本不引用.这意味着第5部分将被分成两部分,因为字段中间的双引号只是一个不引用内容的文字.在这种情况下,我担心选项是(a)使用过于复杂的正则表达式,或(b)将输入数据的格式更改为使用某些可识别的CSV变体.如果是我,我会选择(b). (2认同)