替换花括号中的逗号

Posted

技术标签:

【中文标题】替换花括号中的逗号【英文标题】:Replace commas enclosed in curly braces 【发布时间】:2016-04-19 15:34:01 【问题描述】:

我尝试用花括号括起来的分号替换逗号。

示例字符串:

text = "a,b,'c','d','e','f',g,h"

我知道它归结为向后和向前看,但不知何故它不会像我想要的那样工作:

substr = re.sub(r"(?<=\)(.+?)(,)(?=.+\)",r"\1;", text)

返回:

a,b,'c';'d','e','f',g,h

但是,我的目标是:

a,b,'c';'d';'e';'f',g,h

知道如何实现这一目标吗? 非常感谢任何帮助:)

【问题讨论】:

你的字符串中总是只有一组花括号吗? 很遗憾没有。这是 UTF-8 编码的 API 响应的摘录,我想将其解析为 CSV 文件。 【参考方案1】:

您可以匹配整个块 ...(与 [^]+)并仅将其中的逗号替换为 lambda:

import re
text = "a,b,'c','d','e','f',g,h"
print(re.sub(r"[^]+", lambda x: x.group(0).replace(",", ";"), text))

见IDEONE demo

输出:a,b,'c';'d';'e';'f',g,h

通过声明lambda x,我们可以访问每个匹配对象,并使用x.group(0)获取整个匹配值。然后,我们只需要用分号替换逗号即可。

此正则表达式不支持递归模式。要使用递归模式,您需要PyPi regex module。像m = regex.sub(r"\(?:[^]|(?R))*", lambda x: x.group(0).replace(",", ";"), text) 这样的东西应该可以工作。

【讨论】:

哇,这就像一个魅力。我一直在尝试捕捉多个逗号,以至于我完全忘记了你可以捕捉大括号之间的所有内容,然后只需使用好的旧替换功能来替换你需要替换的那些字符。 如果你嵌套了s,你可能想看看Jaco的解决方案。 它不适用于嵌套括号,例如:"a,b,'c','d','e','f',g,h" @Jaco:它不支持,因为re 不支持递归。但是,regex 模块可以。 @stribizhev 刚刚下载了regex 模块。你能解释一下r"\(?:[^]|(?R))*" 正则表达式的(?R) 部分吗?它是递归指示器吗,例如如果左大括号后面的字符不是 或者如果它们以现在匹配的 开头重新运行正则表达式,则匹配?【参考方案2】:

下面我发布了一个不依赖正则表达式的解决方案。它使用堆栈 (list) 来确定字符是否在花括号 内。正则表达式更优雅,但是,当需求发生变化时,它们可能更难修改。请注意,下面的示例也适用于嵌套括号。

text = "a,b,'c','d','e','f',g,h"
output=''
stack = []
for char in text:
    if char == '':
        stack.append(char)
    elif char == '':
        stack.pop()    
    #Check if we are inside a curly bracket
    if len(stack)>0 and char==',':
        output += ';'
    else:
        output += char
print output

这给出了:

'a,b,'c';'d';'e';'f',g,h

如果您使用stack 的全局变量,您也可以将其重写为map 函数:

stack = []


def replace_comma_in_curly_brackets(char):
    if char == '':
       stack.append(char)
    elif char == '':
        stack.pop()    
    #Check if we are inside a curly bracket
    if len(stack)>0 and char==',':
        return ';'

    return char

text = "a,b,'c','d','e','f',g,h"
print ''.join(map(str, map(replace_comma_in_curly_brackets,text)))

关于性能,在本文末尾的测试字符串上运行上述两种方法和@stribizhev 提出的正则表达式解决方案时,我得到以下时序:

    正则表达式 (@stribizshev):0.38 秒 地图功能:26.3秒 For 循环:251 秒

这是 55,300,00 个字符长的测试字符串:

 text = "a,able,about,across,after,all,almost,also,am,among,an,and,any,are,as,at,be,because,been,but,by,can,cannot,could,dear,did,do,does,either,else,ever,every,for,from,get,got,had,has,have,he,her,hers,him,his,how,however,i,if,in,into,is,it,its,just,least,let,like,likely,may,me,might,most,must,my,neither,no,nor,not,of,off,often,on,only,or,other,our,own,rather,said,say,says,she,should,since,so,some,than,that,the,their,them,then,there,these,they,this,tis,to,too,twas,us,wants,was,we,were,what,when,where,which,while,who,whom,why,will,with,would,yet,you,your" * 100000

【讨论】:

好主意。如果查看大块文本 - 正则表达式或列表的迭代(重新)创建,您是否知道哪种方法可能更快?如果处理大量输入,Afaik 迭代器往往会变得相对较慢。 我将运行一个快速测试来比较两者。 我对一个 55,300,000 个字符的字符串进行了测试。 for 循环真的很慢,需要 251 秒,map 函数需要 26 秒,@stribizshev 提出的正则表达式需要 0.38 秒。测试不包括输出的打印。【参考方案3】:

如果您没有嵌套的大括号,那么在每个 , 前面有一个闭合的 并且中间没有任何打开的 可能就足够了。搜索

,(?=[^]*)

并替换为;

, 按字面意思匹配逗号 (?=...)lookahead查看 如果前面有[^]* any amount 个字符,that are not 后跟一个右花括号

See demo at regex101

【讨论】:

感谢您提供这个甜蜜的解决方案。奇迹般有效。我可能误解了一些东西:我一直认为 需要用` \ ` 转义,但是当属于捕获组时似乎并非如此。谁能澄清一下? @VincentHahn 这取决于上下文和正则表达式的风格。在大多数情况下,如果你想匹配一个文字,例如匹配字符串a0,1,你只需要转义它。在我的回答中 不被解析器视为 quantifier 的一部分。 @VincentHahn 换句话说:就语法上不是有效的量词而言,它将按字面意思匹配。

以上是关于替换花括号中的逗号的主要内容,如果未能解决你的问题,请参考以下文章

用 JSX 中的 HTML 跨度节点替换字符串中的花括号

C语言规范第一个花括号可以不独占一行吗?

C语言规范第一个花括号可以不独占一行吗?

正则表达式替换除大小写“0”之外的所有花括号

cp,mv等花括号用法

花括号拓展 {,}