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

2025-02-27 09:06:00
admin
原创
59
摘要:问题描述:我需要像这样用分号分割字符串。但我不想用字符串内部的分号(' 或 ")进行分割。我不是在解析文件;只是一个没有换行符的简单字符串。part 1;"this is ; part 2;";'this is ; part 3';part 4;this "is ; part&q...

问题描述:

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

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

结果应该是:

  • 第一部分

  • “这是;第二部分;”

  • “这是;第 3 部分”

  • 第 4 部分

  • 这是“部分” 5

我认为这可以用正则表达式来完成,但如果不可以,我愿意尝试另一种方法。


解决方案 1:

大多数答案似乎过于复杂。您不需要反向引用。您不需要依赖 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]

输出为:

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

正如 Jean-Luc Nacif Coelho 正确指出的那样,这无法正确处理空组。根据情况,这可能很重要,也可能不重要。如果确实很重要,则可以通过以下方式处理它,例如,';;'';<marker>;'where替换<marker>某些字符串(不带分号),您知道这些字符串在拆分之前不会出现在数据中。您还需要在以下情况下恢复数据:

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

然而这只是一个临时解决方案。还有其他更好的建议吗?

解决方案 2:

re.split(''';(?=(?:[^'"]|'[^']*'|"[^"]*")*$)''', data)

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

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

解决方案 3:

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

解决方案 4:

以下是带注释的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)

给予

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

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

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

['a', 'b', 'c']

解决方案 5:

您似乎有一个分号分隔的字符串。为什么不使用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 

这应该会给你类似

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

如果您可以将数据更改为仅在适当的位置包含单引号或双引号,它应该可以正常工作,但是这有点否定了这个问题。

解决方案 6:

>>> x = '''part 1;"this is ; part 2;";'this is ; part 3';part 4;this "is ; part" 5'''
>>> import re
>>> re.findall(r'''(?:[^;'"]+|'(?:[^']|\\.)*'|"(?:[^']|\\.)*")+''', x)
['part 1', "this is ';' part 2", "'this is ; part 3'", 'part 4', 'this "is ; part" 5']

解决方案 7:

虽然它可以通过前瞻/后瞻/反向引用用 PCRE 来完成,但由于需要匹配平衡的引号对,这实际上并不是正则表达式设计的任务。

相反,最好的办法可能是只制作一个微型状态机并像那样解析字符串。

编辑

事实证明,由于 Python 的附加功能re.findall可以保证不重叠匹配,因此使用 Python 中的正则表达式可以比其他方式更直接地完成此操作。有关详细信息,请参阅注释。

但是,如果你对非正则表达式的实现感到好奇:

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

results = [[]]
quote = None
for c in x:
  if c == "'" or c == '"':
    if c == quote:
      quote = None
    elif quote == None:
      quote = c
  elif c == ';':
    if quote == None:
      results.append([])
      continue
  results[-1].append(c)

results = [''.join(x) for x in results]

# results = ['part 1', '"this is ; part 2;"', "'this is ; part 3'",
#            'part 4', 'this "is ; part" 5']

解决方案 8:

由于您没有 '\n',因此请使用它来替换引号字符串中不存在的任何 ';'

>>> new_s = ''
>>> is_open = False

>>> for c in s:
...     if c == ';' and not is_open:
...         c = '
'
...     elif c in ('"',"'"):
...         is_open = not is_open
...     new_s += c

>>> result = new_s.split('
')

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

解决方案 9:

我们可以创建一个自己的函数

def split_with_commas_outside_of_quotes(string):
    arr = []
    start, flag = 0, False
    for pos, x in enumerate(string):
        if x == '"':
            flag= not(flag)
        if flag == False and x == ',':
            arr.append(string[start:pos])
            start = pos+1
    arr.append(string[start:pos])
    return arr

解决方案 10:

这个正则表达式将执行以下操作:(?:^|;)("(?:[^"]+|"")*"|[^;]*)

解决方案 11:

通用解决方案:

import re
regex = '''(?:(?:[^{0}"']|"[^"]*(?:"|$)|'[^']*(?:'|$))+|(?={0}{0})|(?={0}$)|(?=^{0}))'''

delimiter = ';'
data2 = ''';field 1;"field 2";;'field;4';;;field';'7;'''
field = re.compile(regex.format(delimiter))
print(field.findall(data2))

输出:

['', 'field 1', '"field 2"', '', "'field;4'", '', '', "field';'7", '']

此解决方案:

  • 捕获所有空组(包括开头和结尾)

  • 适用于大多数常用分隔符,包括空格、制表符和逗号

  • 将其他类型的引号内的引号视为非特殊字符

  • 如果遇到不匹配的未加引号的引号,则将该行的其余部分视为带引号的引号

解决方案 12:

尽管这个主题已经很老了,而且以前的答案也很好用,但我还是提出了自己在 python 中实现 split 函数的方法。

如果您不需要处理大量字符串并且易于定制,那么这种方法很有效。

这是我的功能:

# l is string to parse; 
# splitchar is the separator
# ignore char is the char between which you don't want to split

def splitstring(l, splitchar, ignorechar): 
    result = []
    string = ""
    ignore = False
    for c in l:
        if c == ignorechar:
            ignore = True if ignore == False else False
        elif c == splitchar and not ignore:
            result.append(string)
            string = ""
        else:
            string += c
    return result

因此你可以运行:

line= """part 1;"this is ; part 2;";'this is ; part 3';part 4;this "is ; part" 5"""
splitted_data = splitstring(line, ';', '"')

结果:

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

优点是该函数适用于空字段和字符串中任意数量的分隔符。

希望这有帮助!

解决方案 13:

尽管我确信有一个干净的正则表达式解决方案(到目前为止我喜欢@noiflection 的答案),但这是一个快速而肮脏的非正则表达式答案。

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

inQuotes = False
current = ""
results = []
currentQuote = ""
for c in s:
    if not inQuotes and c == ";":
        results.append(current)
        current = ""
    elif not inQuotes and (c == '"' or c == "'"):
        currentQuote = c
        inQuotes = True
    elif inQuotes and c == currentQuote:
        currentQuote = ""
        inQuotes = False
    else:
        current += c

results.append(current)

print results
# ['part 1', 'this is ; part 2;', 'this is ; part 3', 'part 4', 'this is ; part 5']

(我从来没有做过这种事情,请随意批评我的形式!)

解决方案 14:

我的方法是将所有未加引号的分号替换为文本中永远不会出现的另一个字符,然后根据该字符进行拆分。以下代码使用带有函数参数的 re.sub 函数来搜索和替换所有srch未用单引号或双引号或括号、方括号或大括号括起来的字符串repl

def srchrepl(srch, repl, string):
    """
    Replace non-bracketed/quoted occurrences of srch with repl in string.
    """
    resrchrepl = re.compile(r"""(?P<lbrkt>[([{])|(?P<quote>['"])|(?P<sep>["""
                          + srch + """])|(?P<rbrkt>[)]}])""")
    return resrchrepl.sub(_subfact(repl), string)


def _subfact(repl):
    """
    Replacement function factory for regex sub method in srchrepl.
    """
    level = 0
    qtflags = 0
    def subf(mo):
        nonlocal level, qtflags
        sepfound = mo.group('sep')
        if  sepfound:
            if level == 0 and qtflags == 0:
                return repl
            else:
                return mo.group(0)
        elif mo.group('lbrkt'):
            if qtflags == 0:
                level += 1
            return mo.group(0)
        elif mo.group('quote') == "'":
            qtflags ^= 1            # toggle bit 1
            return "'"
        elif mo.group('quote') == '"':
            qtflags ^= 2            # toggle bit 2
            return '"'
        elif mo.group('rbrkt'):
            if qtflags == 0:
                level -= 1
            return mo.group(0)
    return subf

如果您不关心括号字符,则可以大大简化此代码。

假设您想使用管道或竖线作为替代字符,您可以这样做:

mylist = srchrepl(';', '|', mytext).split('|')

顺便说一句,这是nonlocal从 Python 3.1 开始使用的,如果需要的话,可以将其更改为全局。

解决方案 15:

无需按分隔符模式进行拆分,只需捕获您需要的内容:

>>> import re
>>> data = '''part 1;"this is ; part 2;";'this is ; part 3';part 4;this "is ; part" 5'''
>>> re.findall(r';([\'"][^\'"]+[\'"]|[^;]+)', ';' + data)
['part 1', '"this is ; part 2;"', "'this is ; part 3'", 'part 4', 'this "is ', ' part" 5']

解决方案 16:

最简单的方法是使用 shlex (简单词法分析)——Python 中的内置模块

import shlex
shlex.split("""part 1;"this is ; part 2;";'this is ; part 3';part 4;this "is ; part" 5 """ )

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

解决方案 17:

在我看来这是一个还算优雅的解决方案。

新解决方案:

import re
reg = re.compile('(\'|").*?\\1')
pp = re.compile('.*?;')
def splitter(string):
    #add a last semicolon
    string += ';'
    replaces = []
    s = string
    i = 1
    #replace the content of each quote for a code
    for quote in reg.finditer(string):
        out = string[quote.start():quote.end()]
        s = s.replace(out, '**' + str(i) + '**')
        replaces.append(out)
        i+=1
    #split the string without quotes
    res = pp.findall(s)

    #add the quotes again
    #TODO this part could be faster.
    #(lineal instead of quadratic)
    i = 1
    for replace in replaces:
        for x in range(len(res)):
            res[x] = res[x].replace('**' + str(i) + '**', replace)
        i+=1
    return res

旧解决方案:

我选择匹配是否有一个开头的引号并等待它结束,并且匹配一个结束的分号。你想要匹配的每个“部分”都需要以分号结尾。所以这个匹配是这样的:

  • 'foobar;.sska';

  • “akjshd;asjkdhkj……”;

  • asdkjhakjhajsd.jhdf;

代码:

mm = re.compile('''((?P<quote>'|")?.*?(?(quote)\\2|);)''')
res = mm.findall('''part 1;"this is ; part 2;";'this is ; part 3';part 4''')

您可能需要对 res 进行一些后期处理,但它包含您想要的内容。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   3018  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1880  
  PLM(产品生命周期管理)系统在企业产品研发、生产与管理过程中发挥着至关重要的作用。它能够整合产品全生命周期中的各类数据与流程,提升企业的协同效率、降低成本并加速产品上市。然而,不同企业由于业务模式、产品特性以及管理理念的差异,对PLM系统有着个性化的需求。如何实现这些个性化需求,成为企业在实施PLM系统定制开发时面临...
免费plm软件   0  
  在企业的发展进程中,跨地域协同工作变得愈发普遍和重要。不同地区的团队需要紧密合作,以实现资源共享、提高效率和创新能力。而产品生命周期管理(PLM)系统在这一过程中发挥着关键作用,尤其是其分布式架构,为跨地域协同提供了强大的支持。PLM系统概述PLM系统是一种用于管理产品从概念设计到退役全生命周期过程中所有数据和流程的软...
免费plm管理软件   0  
  在企业项目管理中,资源平衡是确保项目顺利推进、提高效率与效益的关键环节。产品生命周期管理(PLM)系统作为整合产品全生命周期信息与流程的重要工具,为实现资源平衡提供了强大的支持。通过合理运用PLM系统,企业能够优化资源分配、提升协同效率,从而在激烈的市场竞争中占据优势。接下来,我们将深入探讨如何借助PLM系统实现资源平...
plm系统   0  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用