替换花括号中的逗号
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 换句话说:就语法上不是有效的量词而言,它将按字面意思匹配。以上是关于替换花括号中的逗号的主要内容,如果未能解决你的问题,请参考以下文章