Extract Multiple Occurances of Variable Length Text Without Multiple Patterns [closed] - c#

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
From the following data .xxx[val1, val2, val3] the values of val1, val2 and val3 need to be extracted.
If one uses this pattern #"\[(.*?), (.*?), (.*?)\]" the data can be extracted, but when the data string varies it fails to get all data.
Take these variable examples
.xxx[val1]
or .xxx[val1, val2, val3, val4, val5]
or finally .xxx[{1-N},].
What single regular expression pattern can achieve results on all sets of data provided as examples?

What would be the correct pattern for this?
The best practice is not to match the unknown, but design your pattern after the knowns. In similar practice, not blindly match using the .* (zero or more of anything) for backtracking can be horrendously slow; why add to complexity when it is not needed.
Frankly one should favor the + one or more usage more than * zero or more which should really be used when specific items may not appear.
the string can vary.
It appears by your example that if we were to think like a compiler, the tokens are separated by either a , or an ending ]. So let us develop a pattern with that knowledge (the knowns).
The best way to capture is to consume until a known is found. Using the not set of [^ ] pattern is best; which says match a character not in this set. Then add our total quantifier the + which says one or more. Effectively replacing the .* in your old pattern but in reverse.
var data = ".xxx[val1, val2, val3, val4, val5]";
var pattern = #"
[^[]+ # Consume anything that is *not* a brace
# but don't match it , (.xxx is the first anchor)
\[ # Starting brace consumed
( # Start of match captures
(?<Token>[^\s,\]]+) # Named Match grouping called `Token` where one or more
# of anything not a space, comma or end brace is captured.
[\s,\]]+ # Consume the token's `,` or space or final bracket.
)+ # End match captures, one or more
] # Ending brace."
;
// IgnorePatternWhitespace allows us to comment the pattern,
// does not affect parser processing.
Regex.Match(data, pattern, RegexOptions.IgnorePatternWhitespace)
.Groups["Token"]
.Captures
.OfType<Capture>()
.Select(cp => cp.Value);
Result

You could capture #"\[(.*?)\]" in a first step and then split on the , which would certainly be a lot faster than using a regexp to do the same.

An easier way to do this just match everything inside [] and then split the match.
text.match(/\[(.*)\]/)[1].split(", "); //And now you have an array with var1,var2..etc
Here's a javascript example, I don't do c#, so don't want to mess it up :)

Despite a Group overwrites it's value if its repeated, it stores the whole stack of captures as a Capture Collection, returned by each group in Group.Captures Property.
Group.Captures Property
The real utility of the Captures property occurs when a quantifier is applied to a capturing group so that the group captures multiple substrings in a single regular expression. In this case, the Group object contains information about the last captured substring, whereas the Captures property contains information about all the substrings captured by the group.
Then, you can simply use this pattern:
\[(?:([^,\]]+),?\s*)+\]
Code:
string pattern = #"\[(?:([^,\]]+),?\s*)+\]";
var re = new Regex( pattern);
var text = #".xxx[val1, val2, val3]";
MatchCollection matches = re.Matches(text);
for (int mnum = 0; mnum < matches.Count; mnum++)
{ //loop matches
Match match = matches[mnum];
Console.WriteLine("Match #{0} - Value: {1}", mnum + 1, match.Value);
int captureCtr = 0;
foreach (Capture capture in match.Groups[1].Captures)
{ //loop captures for the 1st Group
Console.WriteLine(" Capture {0}: {1}",
captureCtr, capture.Value);
captureCtr += 1;
}
}
Output:
Match #1 - Value: [val1, val2, val3]
Capture 0: val1
Capture 1: val2
Capture 2: val3
ideone DEMO

Related

How to match any repeated chunks of characters?

I've seen many questions similar to this but none quite like it.
I have strings like this:
HF-01-HF-01-01
FBC-FBC-04
OZYA-03A-OZYA-03A-03
QC-QC-02
and want them to be returned like so:
HF-01-01
FBC-04
OZYA-03A-03
QC-02
I can't figure this out and the other questions I've seen don't apply because 1) the repeated chunk is more than one character, 2) There are no spaces between the repetition.
Or is regex not the best way to do this?
EDIT:
Rules
Alpha chunks are never repeated more than one time.
Some chunks can be alphanumeric but also never repeated more than one
time.
The part that can be repeated would be from the start of the string
and any additional chunks by hyphen.
So you would never have something like HF-HF-01-01. But in this case using the above rules, it would become HF-01-01 since HF is the only part repeated from the beginning of the string.
Perhaps something like this would work:
Scan string to first hyphen, see if that matches anywhere else after first hyphen, if so scan to second hyphen, see if that matches anywhere else, if not, take the first scan and remove one instance of it from the string, if so, scan to third, etc.
But I don't know how to do that in regex.
I'm not sure if RegExp is the right tool here.
Using MoreLinq RunLengthEncode method (that implement R.L.E.) you can achieve it like this:
string RemoveDuplicate(string input)
{
var chunks = input.Split('-') // cut at -
.RunLengthEncode() // group and count adjacent equals chunck
.Select(kvp => kvp.Key);// just take the chunk value
return string.Join("-", chunks); // reglue with -
}
Edit
Doesn't work for:
OZYA-03A-OZYA-03A-03
I guess,
([^-\r\n]+-|[^-\r\n]+-[^-\r\n]+-)(\1.*)
or with start/end anchors,
^([^-\r\n]+-|[^-\r\n]+-[^-\r\n]+-)(\1.*)$
might work to some extent and the desired output is in the last capturing group:
(\1.*)
RegEx Demo 1
RegEx Demo 2
Test
using System;
using System.Text.RegularExpressions;
public class Example
{
public static void Main()
{
string pattern = #"([^-\r\n]+-|[^-\r\n]+-[^-\r\n]+-)(\1.*)";
string input = #"HF-01-HF-01-01
FBC-FBC-04
OZYA-03A-OZYA-03A-03
QC-QC-02
and want them to be returned like so:
HF-01-01
FBC-04
OZYA-03A-03
QC-02";
RegexOptions options = RegexOptions.Multiline;
foreach (Match m in Regex.Matches(input, pattern, options))
{
Console.WriteLine("'{0}' found at index {1}.", m.Value, m.Index);
}
}
}
If you wish to simplify/modify/explore the expression, it's been explained on the top right panel of regex101.com. If you'd like, you can also watch in this link, how it would match against some sample inputs.
RegEx Circuit
jex.im visualizes regular expressions:
I'm not sure if regex is the right tool here, but atleast it can be somewhat done with this short pattern:
^([A-Z0-9]+)-.*(\1.*)$
Explanation:
^ start of string
( group 1 start
[A-Z0-9]+ one or more capital letters or digits
) end group 1
- literal
.* any number of any chars
( group 2 start
\1 anything that was matched in group 1
.* any number of any chars
) end group 2 (this group will be used as the result)
$ end of string

Include bad groups in the matches

I have a string like this:
&l&mmabc&od&l&r&mef&lg&l&e&j&rh
I want to get the following matches and groups. (Consider the parentheses to be groups and lines to be matches)
(&l&m)(mabc)
(&o)(d)
(&l&r&m)(ef)
(&l)(g)
(&l)(&e&j)
(&r)(h)
So far, I have got this:
(&[lmnor])+(\w+)
The results of the match are as follows:
You can see that the substring &l&e&j are not included in the matches. I know it's the problem with \w+ but I can't seem to figure out how to include those matches. The first group should only contain anything which matches &[lmnor] (It can contain multiple of those if they are close together. That is the reason I used +)The second group should contain anything other than those letters.
(&[lmnor])+(.*) doesn't work. (&[lmnor])+^(&[lmnor])+ doesn't either.
You can see that the substring &l&e&j are not included in the matches. I know it's the problem with \w+ but I can't seem to figure out how to include those matches.
It is clear that & is not a word character. That is why that substring with & symbols is not matched/captured.
The first group should only contain anything which matches &[lmnor] (It can contain multiple of those if they are close together.
That is a case when we should be using a non-capturing group with a quantifier inside a capturing group: ((?:&[lmnor])+). We match the sequences of characters and capture all that chunk of text into 1 group.
The second group should contain anything other than those letters.
It is a perfect job for a tempered greedy token: (?:(?!&[lmnor]).)*. It matches any text that is not starting &[lmnor] substring. We cannot use a negated character class because the symbols to skip are 2 (not single character).
So, you can use the following regex:
((?:&[lmnor])+)((?:(?!&[lmnor]).)*)
See regex demo
There is another regex you can use that follows the same logics, but using a lazy dot matching and a boundary expressed with a positive look-ahead checking for end of string or with the first set of symbols &[lmnor]:
((?:&[lmnor])+)(.*?)(?=$|&[lmnor])
See another regex demo
It can be done with less complication using the built in power of
C# regular expressions.
This shows using the Capture Collections, and I believe is much faster.
C#
Match aM = Regex.Match(
#"&l&mmabc&od&l&r&mef&lg&l&e&j&rh",
#"^(?:((?:&[lmnor])+)(.*?))+$" );
if ( aM.Success ) {
CaptureCollection cc1 = aM.Groups[1].Captures;
CaptureCollection cc2 = aM.Groups[2].Captures;
for (int i = 0; i < cc1.Count; i++)
Console.WriteLine("[{0}] = {1} {2}", i, cc1[i].Value, cc2[i].Value);
}
Output:
[0] = &l&m mabc
[1] = &o d
[2] = &l&r&m ef
[3] = &l g
[4] = &l &e&j
[5] = &r h

getting the correct regex to print out in c#

Below is a regex statement I have been working on for quite sometime:
Match parsedRequestData = Regex.Match(requestData, #"^.*\[(.*)\]$");
What this is supposed to be doing is taking the email out of the email below:
2.3|[0246303#up.com]
For clarification, this email comes from a table in SQL Server. There are many emails that are formatted like this in there and the regex is supposed to be getting all of that from inside the brackets. However, it is matching the entirety of this line instead of whats inside of it. So my question is, is there something wrong with my regex statement or do I have something in my code I need to add?
Your regex is storing the email address in capture group 1. Try referencing group 1 like this:
parsedRequestData.Groups[1];
Code Sample:
string requestData = "2.3|[0246303#up.com]";
Match parsedRequestData = Regex.Match(requestData, #"^.*\[(.*)\]$");
if (parsedRequestData.Success)
{
Console.WriteLine(parsedRequestData.Groups[1]);
}
Results:
0246303#up.com
Your regex is OK. All you need is to use the Group[1]
var email = Regex.Match("2.3|[0246303#up.com]", #"^.*\[(.*)\]$").Groups[1].Value;
However, it is matching the entirety of this line instead of whats inside of it.
Unless one uses named match captures, the match capture groups are indexed.
Match.Groups[0].Value is the whole match; it shows all the match captures and all the grouped matched text.
Match.Groups[{1-N}].Value is the match captures in the order of specification in the pattern for anything in a ( ) parenthesis set(s). If there is only one ( ) there will be two indexed groups; 0 as mentioned above, and 1 of the items specified to be captured to N.
You only have one ( ) set so the data you want is found in match capture group 1. Group 0 has the non match capture items along with the match capture data.
If one names the match capture such as (?<MyNameHere> ) one can also access the match via Match.Groups["MyNameHere"].Value.
Suggestion on your pattern away from the answer
Usage of * (zero or more) in patterns can be problematic in that it can significantly increase the time of the parser takes due to backtracking false scenarios.
If one knows there is text to be found, don't tell the parser zero items may happen when that is impossible, change it to + one or more. That slight change can greatly affect the parsing operations, both in time and operations.
Change ^.*\[(.*)\]$ to ^.+\[(.+)\]$.
But to even increase the efficiency of the pattern, focus on the knowns of the characters [ and ] as anchors.
Pattern Restructure To Use Anchors
^[^[]+\[([^\]]+)[\s\]]+$
Why is this pattern better? Because we will look for "[" and "]" as anchors.
Let us break it down
^ - Beginning of the pattern (a hard anchor)
[^ ]+ This is a set notation where the ^ says NOT.
[^\[]+ So we want to capture all text + (one or more) that is NOT a [. This tells the pattern to match up to our anchor [ in the text. Note that we don't have to escape it for regex parser treats all characters in a set [ ] as a literal so [^[] is valid. (To be clear this is a match but don't capture text anchor so we will not find this text in an index above the 0 index; only in 0).
\[ Our literal anchor the "[" character.
([^\]]+) This is our match capture which says match this set where any character is valid but not an "]". Here we have to escape the ] because otherwise it would signify the end of our set.
[\s\]]+ we know the end of our text there will be spaces and the "]" character, so let us match (but not to capture) any combination of spaces and a ] before the end.
$ our final anchor, the end of the file/buffer indicator (or line if the right parser rule is set).

Find a string pattern using Regular expression

How can I use regular expressions to find if the string matches a pattern like [sometextornumber] is a [sometextornumber].
For instance, if the input is This is a test, the output should be this and test.
I was thinking something like ([a-zA-Z0-9]) is a([a-zA-Z0-9]) but looks like I am way off the correct path.
Your question is geared towards grabbing the first and last word of a sentence. If this is all you're going to be interested in, this pattern will suffice:
"^(\\w+)|(\\w+)$"
Pattern breakdown:
^ indicates the beginning of a line
^(\\w+) capture group for a word at the beginning of the line. This is equivalent to [a-zA-Z0-9]+, where the + says you want a one or more letters and numbers.
| acts as an OR operator in Regex
$ indicates the end of a line
(\\w+)$ capture group for a word at the end of the line. This is equivalent to [a-zA-Z0-9]+, where the + says you want a one or more letters and numbers.
This pattern allows you to ignore what's in between the first and last word, so it doesn't care about "is a", and give you one capture group to pull from.
Usage:
string data = "This is going to be a test";
Match m = Regex.Match(data, "^(\\w+)|(\\w+)$");
while (m.Success)
{
Console.WriteLine(m.Groups[0]);
m = m.NextMatch();
}
Results:
This
test
If you're really only interested in the first and last word of a sentence, you also don't need to bother with Regex. Just split the sentence by a space and grab the first and last element of the array.
string[] dataPieces = data.Split(' ');
Console.WriteLine(dataPieces[0]);
Console.WriteLine(dataPieces[dataPieces.Length - 1]);
And the results are the same.
References:
https://msdn.microsoft.com/en-us/library/hs600312(v=vs.110).aspx
https://msdn.microsoft.com/en-us/library/az24scfc(v=vs.110).aspx
Try this:
([a-zA-Z0-9])+ is a ([a-zA-Z0-9])+
Edit:
You need a space after the a since it is another word. Without the + it will only match from last letter of the first word till the first letter of the last word. The + will match 1 or more of whatever is in the (), so in this case the whole word.
If you're looking to match a specific pattern such as "This" or "Test" you can simply do a case insensitive string compare.
From your question, I'm not sure that you necessarily need a regular expression here.
Here is a quick LINQpad:
var r = new Regex("(.*) is a (.*)");
var match = r.Match("This is a test");
match.Groups.OfType<Group>().Skip(1).Select(g=>g.Value).Dump();
That outputs:
IEnumerable<String> (2 items)
This
test

Regex nested parentheses

I have the following string:
a,b,c,d.e(f,g,h,i(j,k)),l,m,n
Would know tell me how I could build a regex that returns me only the "first level" of parentheses something like this:
[0] = a,b,c,
[1] = d.e(f,g,h,i.j(k,l))
[2] = m,n
The goal would be to keep the section that has the same index in parentheses nested to manipulate future.
Thank you.
EDIT
Trying to improve the example...
Imagine I have this string
username,TB_PEOPLE.fields(FirstName,LastName,TB_PHONE.fields(num_phone1, num_phone2)),password
My goal is to turn a string into a dynamic query.
Then the fields that do not begin with "TB_" I know they are fields of the main table, otherwise I know informandos fields within parentheses, are related to another table.
But I am having difficulty retrieving all fields "first level" since I can separate them from related tables, I could go recursively recovering the remaining fields.
In the end, would have something like:
[0] = username,password
[1] = TB_PEOPLE.fields(FirstName,LastName,TB_PHONE.fields(num_phone1, num_phone2))
I hope I have explained a little better, sorry.
You can use this:
(?>\w+\.)?\w+\((?>\((?<DEPTH>)|\)(?<-DEPTH>)|[^()]+)*\)(?(DEPTH)(?!))|\w+
With your example you obtain:
0 => username
1 => TB_PEOPLE.fields(FirstName,LastName,TB_PHONE.fields(num_phone1, num_phone2))
2 => password
Explanation:
(?>\w+\.)? \w+ \( # the opening parenthesis (with the function name)
(?> # open an atomic group
\( (?<DEPTH>) # when an opening parenthesis is encountered,
# then increment the stack named DEPTH
| # OR
\) (?<-DEPTH>) # when a closing parenthesis is encountered,
# then decrement the stack named DEPTH
| # OR
[^()]+ # content that is not parenthesis
)* # close the atomic group, repeat zero or more times
\) # the closing parenthesis
(?(DEPTH)(?!)) # conditional: if the stack named DEPTH is not empty
# then fail (ie: parenthesis are not balanced)
You can try it with this code:
string input = "username,TB_PEOPLE.fields(FirstName,LastName,TB_PHONE.fields(num_phone1, num_phone2)),password";
string pattern = #"(?>\w+\.)?\w+\((?>\((?<DEPTH>)|\)(?<-DEPTH>)|[^()]+)*\)(?(DEPTH)(?!))|\w+";
MatchCollection matches = Regex.Matches(input, pattern);
foreach (Match match in matches)
{
Console.WriteLine(match.Groups[0].Value);
}
I suggest a new strategy, R2 - do it algorithmically. While you can build a Regex that will eventually come close to what you're asking, it'll be grossly unmaintainable, and hard to extend when you find new edge cases. I don't speak C#, but this pseudo code should get you on the right track:
function parenthetical_depth(some_string):
open = count '(' in some_string
close = count ')' in some_string
return open - close
function smart_split(some_string):
bits = split some_string on ','
new_bits = empty list
bit = empty string
while bits has next:
bit = fetch next from bits
while parenthetical_depth(bit) != 0:
bit = bit + ',' + fetch next from bits
place bit into new_bits
return new_bits
This is the easiest way to understand it, the algorithm is currently O(n^2) - there's an optimization for the inner loop to make it O(n) (with the exception of String copying, which is kind of the worst part of this):
depth = parenthetical_depth(bit)
while depth != 0:
nbit = fetch next from bits
depth = depth + parenthetical_depth(nbit)
bit = bit + ',' + nbit
The string copying can be made more efficient with clever use of buffers and buffer size, at the cost of space efficiency, but I don't think C# gives you that level of control natively.
If I understood correctly your example, your are looking for something like this:
(?<head>[a-zA-Z._]+\,)*(?<body>[a-zA-Z._]+[(].*[)])(?<tail>.*)
For given string:
username,TB_PEOPLE.fields(FirstName,LastName,TB_PHONE.fields(num_phone1, num_phone2)),password
This expression will match
username, for group head
TB_PEOPLE.fields(FirstName,LastName,TB_PHONE.fields(num_phone1, num_phone2)) for group body
,password for group tail

Categories