Matching exactly one occurrence in a string with a regular expression - regex

The other day I sat with a regular expression problem. Eventually I solved it a different way, without regular expressions, but I would still like to know how you do it :)
The problem I was having was running svn update via an automated script, and I wanted to detect conflicts. Doing this with or without regex is trivial, but it got me thinking about a more obscure problem: How do you match exactly ONE occurrence of a character inside a fixed length field of whitespace?
For instance, let's say we wanted to match "C" inside a six-byte wide field:
"C " MATCH
" C " MATCH
" C C " NO MATCH
" M " NO MATCH
" " NO MATCH
"C " NO MATCH (7 characters, not 6)
" C " NO MATCH (5 characters, not 6)

I know it's not right to answer your own question, but I basically merged your answers ... please don't flame :)
^(?=.{6}$) *C *$
Edit:
Replacing . with Tomalak's response below [ C] increases the speed with about 4-5% or so
^(?=[ C]{6}$) *C *$

^(?=[ C]{6}$) *C(?! *C)
Explanation:
^ # start-of-string
(?=[ C]{6}$) # followed by exactly 6 times " " or "C" and the end-of-string
*C # any number of spaces and a "C"
(?! *C) # not followed by another C anywhere (negative lookahead)
Notes:
The ^(?=…{6}$) construct can be used anywhere you want to measure string length but not actually match anything yet.
Since the end of the string is already checked in the look-ahead, you do not need to put a $ at the end of the regex, but it does not hurt to do it.

^[^C]*C[^C]*$
but this will not verify the length of your string.

Related

Regular Expression starting and ending with special characters

I need to extract all matches from a huge text that start with [" and end with "]. These special characters separate each record from database. I need to extract all records.
Inside this record there are letters, numbers and special characters like -, ., &, (), /, {space} or so.
I'm writing this in Office VBA.
The pattern I have come so far looks like this: .Pattern = "[[][""][a-z|A-Z|w|W]*".
With this pattern, I am able to extract the first word from each record, with the starting characters [". The count of found matches is correct.
Example of one record:
["blabla","blabla","blabla","\u00e1no","nie","\u00e1no","\u00e1no","\u00e1no","\u003Ca class=\u0022btn btn-default\u0022 href=\u0022\u0026#x2F;siea\u0026#x2F;suppliers\u0026#x2F;42\u0022\u003E\u003Ci class=\u0022fa fa-pencil\u0022\u003E\u003C\/i\u003E Upravi\u0165\u003C\/a\u003E \u003Ca class=\u0022btn btn-default\u0022 href=\u0022\u0026#x2F;siea\u0026#x2F;suppliers\u0026#x2F;form\u0026#x2F;42\u0022\u003E\u003Ci class=\u0022fa fa-file-pdf-o\u0022\u003E\u003C\/i\u003E Zmluva\u003C\/a\u003E \u003Ca class=\u0022btn btn-default\u0022 href=\u0022\u0026#x2F;siea\u0026#x2F;suppliers\u0026#x2F;crz-form\u0026#x2F;42\u0022\u003E\u003Ci class=\u0022fa fa-file-pdf-o\u0022\u003E\u003C\/i\u003E Zmluva CRZ\u003C\/a\u003E"]
The question is : How can I extract the all records starting with [" and ending with "]?
I don't necessary need the starting and ending characters, but I can clean that up later.
Thanks for help.
The easiest way is to get rid of the initial and trailing [" and "] with either Replace or Left/Right/Mid functions, and then Split with "," (in VBA, """,""").
E.g.
input = "YOUR_STRING"
input = Replace(Replace(input, """]", ""), "[""", "")
result = Split(input, """,""")
If you plan to use Regex, you can use \["[\s\S]*?"] pattern, but it is not that efficient with long inputs and may even freeze the macro if timeout issue occurs. You can unroll it as
\["[^"]*(?:"(?!])[^"]*)*"]
See the regex demo. In VBA, Pattern = "\[""[^""]*(?:""(?!])[^""]*)*""]"
Note that with this unrolled pattern, you do not even need to use the workarounds for dot matching newline issue (negated character class [^"] matches any char but ", including a newline).
Pattern details:
\[" - [" literally
[^"]* - zero or more characters other than "
(?:"(?!])[^"]*)* - zero or more sequences of
"(?!]) - " not followed with ]
[^"]* - zero or more characters other than "
"] - literal character sequence "]

Tricky substring problems

I'm having a problem with substrings, I have a string in the format below I'm
currently using getline.
Richard[12345/678910111213141516] was murdered
What I have been using is find_last_of and find_first_of to get the positions in between the brackets and forward slashes to retrieve each field. I have this working and functional but I have ran into a problem. The name field can be 32 characters in length, and can contain / and [] so when I finally ran into a user with a URL for his name it did not like that. The numbers are also random on a per user basis. I'm retrieving each field from the string, the name and the two identifying numbers.
Another string can look like this, so I would be grabbing 6 total substrings.
Richard[12345/678910111213141516] was murdered by Ralph[54321/161514131211109876]
Which is just a just another huge mess, what I was thinking about doing was starting from the back and moving to the front, but if the second name field (Ralph) contains any / or [] its going to ruin the count for retrieving the first part. Any insight would be helpful. Thank you.
In a nutshell. how do I account for these.
Names can also contain any alpha / numerical and special character.
Richard///[][][12345/678910111213141516] was murdered by Ralph[/[54321/161514131211109876]
The end result would be 6 substrings containing this.
Richard///[][]
12345
678910111213141516
Ralph[/
54321
161514131211109876
Regex has been mentioned to me, but I don't know if it would be better suited for the task or not, I included the tag so someone more experienced with it might answer/comment.
Here is a regex way to obtain all the values:
string str = "Richard///[][][12345/678910111213141516] was murdered by Ralph[/[54321/161514131211109876]";
regex rgx1(R"(([A-Z]\w*\s*\S*)\[(\d+)?(?:\/(\d+))?\])");
smatch smtch;
while (regex_search(str, smtch, rgx1)) {
std::cout << "Name: " << smtch[1] << std::endl;
std::cout << "ID1: " << smtch[2] << std::endl;
std::cout << "ID2: " << smtch[3] << std::endl;
str = smtch.suffix().str();
}
See IDEONE demo
The regex (\S*)\[(\d+)?(?:/(\d+))?\] matches:
(\S*) - (Group 1) 0 or more non-whitespace symbols, as many as possible.
\[ - an opening square bracket (must be escaped as it is a special character in regex reserved for character classes)
(\d+)? - (Group 2) 1 or more digits (optional group, can be empty)
(?:/(\d+))? - non-capturing optional group matching
/ - literal /
(\d+) - (Group 3) 1 or more digits.
\] - closing square bracket.
A possible regex solution would be to use a pattern like follows:
(\S+)\[(\d+)/(\d+)\](?:\s|$)
which will match and store the names (with their meta attributes). I am currently thinking of ways when it could break.
You can test it on regex101.

Replace group with spaces

I need to hide part of the string. Hide all before some ending part.
It easy to implement by regexp like this:
replace("123-134-04", ".(?=.*-)", " ")
replace any symbol if future part of string contains "-".
So result is: " -04"
It is important to keep spaces.
But, I can't use lookahead or lookbehind.
I can catch the group before ending part, but how to replace this for right number of spaces?
Or maybe some other ways to resolve this with regex?
Tnanks in advance!
If the number of to be replaced characters does not differ too much, and you have a means to match the part to be preserved, you could run through a series of search and replace:
replace("12-14-04", "^.{5}(-[^-]+)$", " \1")
replace("123-134-04", "^.{7}(-[^-]+)$", " \1")
replace("adfasd-adf-da7474-04", "^.{17}(-[^-]+)$", " \1")
Or you do:
split the string at the position, where the to be preserved part begins,
run the replace("ALL OF THIS SHOULD BECOME BLANKS", ".", " ") on the first part, and
join them up again.

Regular expression for extracting excerpt from long String

I want to extract excerpt from a long string using Regular expression
Example string: "" Is it possible that Germany, which beat Argentina 1-0 today to win the World Cup, that will end up as a loser in terms of economic growth? ""
String to search: " that "
Expected result from regex
" possible that Germany "
" rd Cup, that will end "
I want to search the desired text from the string with -9 and +9 characters from the forward and the backward of the occurence of the searched string. Search string can occur multiple times within the given string.
I am working on an iOS app
using iOS 7.
I have so far created this expression with my little knowledge about reguler expressions but not able to get desired result from that
" (.){0,9} (that) {0,9} "
Remove the spaces in your regex. If you want to capture the matched ones. Then enclose the pattern within capturing groups (ie, ()),
.{9}that.{9}
OR
(?:.{9}|.{0,9})that(?:.{9}|.{0,9})
DEMO
Make the preceding and following characters as optional to match the line which looks like that will change history
Well, in your expression you were just missing the second "." and maybe the "?" for spaces.
.{0,9} ?that ?.{0,9}
Try that.
You can add ( ) for making groups if you want. I added the "?" to make it comply with your other example:
" that will change history"

Regex for quoted string with escaping quotes

How do I get the substring " It's big \"problem " using a regular expression?
s = ' function(){ return " It\'s big \"problem "; }';
/"(?:[^"\\]|\\.)*"/
Works in The Regex Coach and PCRE Workbench.
Example of test in JavaScript:
var s = ' function(){ return " Is big \\"problem\\", \\no? "; }';
var m = s.match(/"(?:[^"\\]|\\.)*"/);
if (m != null)
alert(m);
This one comes from nanorc.sample available in many linux distros. It is used for syntax highlighting of C style strings
\"(\\.|[^\"])*\"
As provided by ePharaoh, the answer is
/"([^"\\]*(\\.[^"\\]*)*)"/
To have the above apply to either single quoted or double quoted strings, use
/"([^"\\]*(\\.[^"\\]*)*)"|\'([^\'\\]*(\\.[^\'\\]*)*)\'/
Most of the solutions provided here use alternative repetition paths i.e. (A|B)*.
You may encounter stack overflows on large inputs since some pattern compiler implements this using recursion.
Java for instance: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6337993
Something like this:
"(?:[^"\\]*(?:\\.)?)*", or the one provided by Guy Bedford will reduce the amount of parsing steps avoiding most stack overflows.
/(["\']).*?(?<!\\)(\\\\)*\1/is
should work with any quoted string
"(?:\\"|.)*?"
Alternating the \" and the . passes over escaped quotes while the lazy quantifier *? ensures that you don't go past the end of the quoted string. Works with .NET Framework RE classes
/"(?:[^"\\]++|\\.)*+"/
Taken straight from man perlre on a Linux system with Perl 5.22.0 installed.
As an optimization, this regex uses the 'posessive' form of both + and * to prevent backtracking, for it is known beforehand that a string without a closing quote wouldn't match in any case.
This one works perfect on PCRE and does not fall with StackOverflow.
"(.*?[^\\])??((\\\\)+)?+"
Explanation:
Every quoted string starts with Char: " ;
It may contain any number of any characters: .*? {Lazy match}; ending with non escape character [^\\];
Statement (2) is Lazy(!) optional because string can be empty(""). So: (.*?[^\\])??
Finally, every quoted string ends with Char("), but it can be preceded with even number of escape sign pairs (\\\\)+; and it is Greedy(!) optional: ((\\\\)+)?+ {Greedy matching}, bacause string can be empty or without ending pairs!
An option that has not been touched on before is:
Reverse the string.
Perform the matching on the reversed string.
Re-reverse the matched strings.
This has the added bonus of being able to correctly match escaped open tags.
Lets say you had the following string; String \"this "should" NOT match\" and "this \"should\" match"
Here, \"this "should" NOT match\" should not be matched and "should" should be.
On top of that this \"should\" match should be matched and \"should\" should not.
First an example.
// The input string.
const myString = 'String \\"this "should" NOT match\\" and "this \\"should\\" match"';
// The RegExp.
const regExp = new RegExp(
// Match close
'([\'"])(?!(?:[\\\\]{2})*[\\\\](?![\\\\]))' +
'((?:' +
// Match escaped close quote
'(?:\\1(?=(?:[\\\\]{2})*[\\\\](?![\\\\])))|' +
// Match everything thats not the close quote
'(?:(?!\\1).)' +
'){0,})' +
// Match open
'(\\1)(?!(?:[\\\\]{2})*[\\\\](?![\\\\]))',
'g'
);
// Reverse the matched strings.
matches = myString
// Reverse the string.
.split('').reverse().join('')
// '"hctam "\dluohs"\ siht" dna "\hctam TON "dluohs" siht"\ gnirtS'
// Match the quoted
.match(regExp)
// ['"hctam "\dluohs"\ siht"', '"dluohs"']
// Reverse the matches
.map(x => x.split('').reverse().join(''))
// ['"this \"should\" match"', '"should"']
// Re order the matches
.reverse();
// ['"should"', '"this \"should\" match"']
Okay, now to explain the RegExp.
This is the regexp can be easily broken into three pieces. As follows:
# Part 1
(['"]) # Match a closing quotation mark " or '
(?! # As long as it's not followed by
(?:[\\]{2})* # A pair of escape characters
[\\] # and a single escape
(?![\\]) # As long as that's not followed by an escape
)
# Part 2
((?: # Match inside the quotes
(?: # Match option 1:
\1 # Match the closing quote
(?= # As long as it's followed by
(?:\\\\)* # A pair of escape characters
\\ #
(?![\\]) # As long as that's not followed by an escape
) # and a single escape
)| # OR
(?: # Match option 2:
(?!\1). # Any character that isn't the closing quote
)
)*) # Match the group 0 or more times
# Part 3
(\1) # Match an open quotation mark that is the same as the closing one
(?! # As long as it's not followed by
(?:[\\]{2})* # A pair of escape characters
[\\] # and a single escape
(?![\\]) # As long as that's not followed by an escape
)
This is probably a lot clearer in image form: generated using Jex's Regulex
Image on github (JavaScript Regular Expression Visualizer.)
Sorry, I don't have a high enough reputation to include images, so, it's just a link for now.
Here is a gist of an example function using this concept that's a little more advanced: https://gist.github.com/scagood/bd99371c072d49a4fee29d193252f5fc#file-matchquotes-js
here is one that work with both " and ' and you easily add others at the start.
("|')(?:\\\1|[^\1])*?\1
it uses the backreference (\1) match exactley what is in the first group (" or ').
http://www.regular-expressions.info/backref.html
One has to remember that regexps aren't a silver bullet for everything string-y. Some stuff are simpler to do with a cursor and linear, manual, seeking. A CFL would do the trick pretty trivially, but there aren't many CFL implementations (afaik).
A more extensive version of https://stackoverflow.com/a/10786066/1794894
/"([^"\\]{50,}(\\.[^"\\]*)*)"|\'[^\'\\]{50,}(\\.[^\'\\]*)*\'|“[^”\\]{50,}(\\.[^“\\]*)*”/
This version also contains
Minimum quote length of 50
Extra type of quotes (open “ and close ”)
If it is searched from the beginning, maybe this can work?
\"((\\\")|[^\\])*\"
I faced a similar problem trying to remove quoted strings that may interfere with parsing of some files.
I ended up with a two-step solution that beats any convoluted regex you can come up with:
line = line.replace("\\\"","\'"); // Replace escaped quotes with something easier to handle
line = line.replaceAll("\"([^\"]*)\"","\"x\""); // Simple is beautiful
Easier to read and probably more efficient.
If your IDE is IntelliJ Idea, you can forget all these headaches and store your regex into a String variable and as you copy-paste it inside the double-quote it will automatically change to a regex acceptable format.
example in Java:
String s = "\"en_usa\":[^\\,\\}]+";
now you can use this variable in your regexp or anywhere.
(?<="|')(?:[^"\\]|\\.)*(?="|')
" It\'s big \"problem "
match result:
It\'s big \"problem
("|')(?:[^"\\]|\\.)*("|')
" It\'s big \"problem "
match result:
" It\'s big \"problem "
Messed around at regexpal and ended up with this regex: (Don't ask me how it works, I barely understand even tho I wrote it lol)
"(([^"\\]?(\\\\)?)|(\\")+)+"