Replace commas enclosed in braces

I am trying to replace commas with a semicolon enclosed in braces.

Example line:

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

I know this boils down to lookbehinds and lookaheads, but for some reason this won't work the way I want:

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

It returns:

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

However, I sought the following:

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

Any idea how I can achieve this? Any help is much appreciated :)

+5
source share
3 answers

You can map the entire block {...} (with {[^{}]+} ) and replace the commas inside it with only lambda:

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

Watch the IDEONE demo

Output: a,b,{'c';'d';'e';'f'},g,h

By declaring lambda x , we get access to each matching object and get the full matching value using x.group(0) . Then we need to replace the comma with a semicolon.

This regular expression does not support recursive patterns . To use a recursive template, you need the PyPi regex module . Something like m = regex.sub(r"\{(?:[^{}]|(?R))*}", lambda x: x.group(0).replace(",", ";"), text) should work.

+2
source

Below I posted a solution that does not rely on regex. It uses the stack ( list ) to determine if the character is inside the braces { . The regular expression is more elegant, however it can be more difficult to change when changing requirements. Note that the example below also works for nested brackets.

 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 

This gives:

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

You can also rewrite this as a map function if you use a global variable for stack :

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

Regarding performance, when I run the above two methods and solve the regular expression suggested by @stribizhev on the test line at the end of this post, I get the following timings:

  • Regular Expression (@stribizshev): 0.38 seconds
  • Card Function: 26.3 seconds
  • Per cycle: 251 seconds

This is a test string with a length of 55,300.00 characters:

  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 
+2
source

If you donโ€™t have nested braces, it may be quite simple to look forward at each, if there is a closing } forward without opening { between them. Search

 ,(?=[^{]*}) 

and replace with ;

  • matches a comma literally
  • (?= ... ) lookahead to check
  • if the front [^{]* any sum of characters that are not {
  • followed by a closing brace }

See demo at regex101

+1
source

Source: https://habr.com/ru/post/1240606/


All Articles