Using parentheses in input string for regex - c#

I am trying to add items to a multi line TextBox. The TextBox should not take duplicate lines. If a duplicate is present then it should rename it to name (1). I am using Regex for this purpose.
Following is my function:
private string Rename(string input, string[] lines)
{
string output = string.Empty;
if (lines.Contains(input))
{
Regex regEx = new Regex(string.Format(#"\b{0}\b", input), RegexOptions.ExplicitCapture);
string[] str = lines.Select(x => x).Where(y => regEx.IsMatch(y)).ToArray();
regEx = new Regex(string.Format(#"\b{0}\b \(\d+\)", input));
string[] matchedStrings = str.Select(x => x).Where(y => regEx.IsMatch(y)).ToArray();
if (matchedStrings.Length > 0)
{
return string.Format("{0} ({1})", input, (matchedStrings.Length + 1));
}
else
{
return string.Format("{0} (1)", input, matchedStrings.Length);
}
}
else
{
return input;
}
}
This is how I call the function in a button click:
// textBox2 is a multiline text bor. textBox1 is where the input is taken from
textBox2.Text += Rename(textBox1.Text, textBox2.Lines) + Environment.NewLine;
The above code works for normal text. For example:
if input is abc and same input is given again, it returns abc (1). After this if I give input as abc (1), then the first regex pattern returns zero matches. Because of this, I am unable to rename appropriately.

You need to escape the input when injecting it to a regex:
// vvvvvvvvvvvvv v
Regex regEx = new Regex(string.Format(#"\b{0}\b", Regex.Escape(input)), RegexOptions.ExplicitCapture);
When input is abc (1), without escaping the regex becomes \babc (1)\b. See? The parentheses are unescaped, which makes literal (1) string a pattern of literal 1 captured in a group.
Please note the RegexOptions.ExplicitCapture you're using does not make parentheses treated literally. This option only tells the regex engine to use parentheses for grouping and not for capturing.
As a rule of thumb, do always escape strings injected into a regex.
The second issue is the closing \b fails to match after closing ). ) is not considered a word character so end of string after ) is not a word boundary. To fix it you may supply the closing \b with an alternative matching anything prepended by a ). This may be put as (?<=\)), which is an empty string preceded by a ). So the regex initialisation turns to:
// v vvvvvvvvv
Regex regEx = new Regex(string.Format(#"\b{0}(\b|(?<=\)))", Regex.Escape(input)), RegexOptions.ExplicitCapture);

Related

Find pattern to solve regex in one step

I have a problem to find the pattern that solves the problem in onestep.
The string looks like this:
Text1
Text1$Text2$Text3
Text1$Text2$Text3$Text4$Text5$Text6 etc.
What i want to get is: Take up to 4x Text. If there are more than "4xText" take only the last sign.
Example:
Text1$Text2$Text3$Text4$Text5$Text6 -> Text1$Text2$Text3$Text4&56
My current solution is:
First pattern:
^([^\$]*)\$?([^\$]*)\$?([^\$]*)\$?([^\$]*)\$?
After this i will do a substitution with the first pattern
New string: Text5$Text6
second pattern is:
([^\$])\b
result: 56
combine both and get the result:
Text1$Text2$Text3$Text4$56
For me it is not clear why i cant easily put the second pattern after the first pattern into one pattern. Is there something like an anchor that tells the engine to start the pattern from here like it would do if is would be the only pattern ?
You might use an alternation with a positive lookbehind and then concatenate the matches.
(?<=^(?:[^$]+\$){0,3})[^$]+\$?|[^$](?=\$|$)
Explanation
(?<= Positive lookbehind, assert what is on the left is
^(?:[^$]+\$){0,3} Match 0-3 times any char except $ followed by an optional $
) Close lookbehind
[^$]+\$? Match 1+ times any char except $, then match an optional $
| Or
[^$] Match any char except $
(?=\$|$) Positive lookahead, assert what is directly to the right is either $ or the end of the string
.NET regex demo | C# demo
Example
string pattern = #"(?<=^(?:[^$]*\$){0,3})[^$]*\$?|[^$](?=\$|$)";
string[] strings = {
"Text1",
"Text1$Text2$Text3",
"Text1$Text2$Text3$Text4$Text5$Text6"
};
Regex regex = new Regex(pattern);
foreach (String s in strings) {
Console.WriteLine(string.Join("", from Match match in regex.Matches(s) select match.Value));
}
Output
Text1
Text1$Text2$Text3
Text1$Text2$Text3$Text4$56
I strongly believe regular expression isn't the way to do that. Mostly because of the readability.
You may consider using simple algorithm like this one to reach your goal:
using System;
public class Program
{
public static void Main()
{
var input = "Text1$Text2$Text3$Text4$Text5$Text6";
var parts = input.Split('$');
var result = "";
for(var i=0; i<parts.Length; i++){
result += (i <= 4 ? parts[i] + "$" : parts[i].Substring(4));
}
Console.WriteLine(result);
}
}
There are also linq alternatives :
using System;
using System.Linq;
public class Program
{
public static void Main()
{
var input = "Text1$Text2$Text3$Text4$Text5$Text6";
var parts = input.Split('$');
var first4 = parts.Take(4);
var remainings = parts.Skip(4);
var result2 = string.Join("$", first4) + "$" + string.Join("", remainings.Select( r=>r.Substring(4)));
Console.WriteLine(result2);
}
}
It has to be adjusted to the actual needs but the idea is there
Try this code:
var texts = new string[] {"Text1", "Text1$Text2$Text3", "Text1$Text2$Text3$Text4$Text5$Text6" };
var parsed = texts
.Select(s => Regex.Replace(s,
#"(Text\d{1,3}(?:\$Text\d{1,3}){0,3})((?:\$Text\d{1,3})*)",
(match) => match.Groups[1].Value +"$"+ match.Groups[2].Value.Replace("Text", "").Replace("$", "")
)).ToArray();
// parsed is now: string[3] { "Text1$", "Text1$Text2$Text3$", "Text1$Text2$Text3$Text4$56" }
Explanation:
solution uses regex pattern: (Text\d{1,3}(?:\$Text\d{1,3}){0,3})((?:\$Text\d{1,3})*)
(...) - first capturing group
(?:...) - non-capturing group
Text\d{1,3}(?:\$Text\d{1,3} - match Text literally, then match \d{1,3}, which is 1 up to three digits, \$ matches $ literally
Rest is just repetition of it. Basically, first group captures first four pieces, second group captures the rest, if any.
We also use MatchEvaluator here which is delegate type defined as:
public delegate string MatchEvaluator(Match match);
We define such method:
(match) => match.Groups[1].Value +"$"+ match.Groups[2].Value.Replace("Text", "").Replace("$", "")
We use it to evaluate match, so takee first capturing group and concatenate with second, removing unnecessary text.
It's not clear to me whether your goal can be achieved using exclusively regex. If nothing else, the fact that you want to introduce a new character '&' into the output adds to the challenge, since just plain matching would never be able to accomplish that. Possibly using the Replace() method? I'm not sure that would work though...using only a replacement pattern and not a MatchEvaluator, I don't see a way to recognize but still exclude the "$Text" portion from the fifth instance and later.
But, if you are willing to mix regex with a small amount of post-processing, you can definitely do it:
static readonly Regex regex1 = new Regex(#"(Text\d(?:\$Text\d){0,3})(?:\$Text(\d))*", RegexOptions.Compiled);
static void Main(string[] args)
{
for (int i = 1; i <= 6; i++)
{
string text = string.Join("$", Enumerable.Range(1, i).Select(j => $"Text{j}"));
WriteLine(KeepFour(text));
}
}
private static string KeepFour(string text)
{
Match match = regex1.Match(text);
if (!match.Success)
{
return "[NO MATCH]";
}
StringBuilder result = new StringBuilder();
result.Append(match.Groups[1].Value);
if (match.Groups[2].Captures.Count > 0)
{
result.Append("&");
// Have to iterate (join), because we don't want the whole match,
// just the captured text.
result.Append(JoinCaptures(match.Groups[2]));
}
return result.ToString();
}
private static string JoinCaptures(Group group)
{
return string.Join("", group.Captures.Cast<Capture>().Select(c => c.Value));
}
The above breaks your requirement into three different capture groups in a regex. Then it extracts the captured text, composing the result based on the results.

C# - Save text into variable

I want to save an e-mail-address out of a .txt-file into a string variable. This is my code:
String path = "C:\\Users\\test.txt";
string from;
var fro = new Regex("from: (?<fr>)");
using (var reader = new StreamReader(File.OpenRead(#path)))
{
while (true)
{
var nextLine = reader.ReadLine();
if (nextLine == null)
break;
var matchb = fro.Match(nextLine);
if (matchb.Success)
{
from = matchb.Groups["fr"].Value;
Console.WriteLine(from);
}
}
}
I know that matchb.Success is true, however from won't be displayed correctly. I'm afraid it has something to do with the escape sequence, but I was unable to find anything helpful on the internet.
The textfile might look like this:
LOG 00:01:05 processID=123456-12345 from: test#test.org
LOG 00:01:06 processID=123456-12345 OK
Your (?<fr>) pattern defines a named group "fr" that matches an empty string.
To fill the group with some value you need to define the group pattern.
If you plan to match the rest of the line, you may use .*. To match a sequence of non-whitespace chars, use \S+. To match a sequence of non-whitespace chars that has a # inside, use \S+#\S+. All the three approaches will work for the current scenario.
In C#, it will look like
var fro = new Regex(#"from: *(?<fr>\S+#\S+)");
Note that #"..." is a verbatim string literal where a single backslash defines a literal backslash, so you do not have to double it. I also suggest using the * quantifier to match 0 or more spaces before the email. You might want to use \s* (to match any 0+ whitespace chars) or [\p{Zs}\t]* (to match only horizontal whitespace chars) instead.

Simple regex-matching

I have a String
String test = #"Lists/Versions/2_.000";
I'm a bit confused on how to use regex to do this.
I'm using the pattern
String pattern = #"\D+";
The msdn page for regular expression says \D is "Matches any character other than a decimal digit"
So shouldn't it be returning 'Lists/Versions/' , '2'?
However its returning
'' , '2', '000'
I would like the string to only match the 2(Or any Integer). How would I do that?
String url = #"Lists/Versions/2_.000";
String pattern = #"\D+";
string[] substrings = Regex.Split(url, pattern);
foreach (string match in substrings)
{
Console.WriteLine("'{0}'", match);
}
The reason your receiving the issue, is because the /D is to capture non digits, so it detects two separate numeric values (2 and 000) because of the _. So that is how it is grabbing the data. So you have a couple of choices:
Break the string into manageable portions, then anchor to the array.
Build a better pattern to separate.
So the question will be, what are you trying to parse? 2.00 ? Or are you trying to separate numeric numbers in your string?
I'm assuming you have a typo also:
\d Matches a digit character. Equivalent to [0-9].
\D Matches a non-digit character. Equivalent to [^0-9].
\w Matches any word character including underscore. Equivalent to
"[A-Za-z0-9_]".
\W Matches any non-word character. Equivalent to "[^A-Za-z0-9_]".
You should be able to use:
You should simply do the following:
string url = #"Lists/Versions/2_.000";
var data = Regex.Split(url, #"\D+");
Console.WriteLine(#"Value: {0} and Secondary Value: {1}", data[0], data[1]);
That should find all integer values, so it should provide an output of:
2
000
Which should return as a normal string []. My syntax or expression may be off, but you can find a nice cheat sheet for Regular Expressions here. You'll also want to ensure you check the bounds of the array.
https://dotnetfiddle.net/BU6gp2
using System;
using System.Text.RegularExpressions;
public class Program
{
public static void Main()
{
String url = #"Lists/Versions/2_.000";
String pattern = #"\D+";
string[] substrings = Regex.Split(url, pattern);
Console.WriteLine("'{0}'", substrings[1]);
}
}
Please try the following:
// using System.Linq;
String url = #"Lists/Versions/2_.000";
String pattern = #"(?<=/)\d+";
string[] substrings = Regex.Matches(url, pattern)
.Cast<Match>()
.Select(_ => _.Value)
.ToArray();
foreach (string match in substrings)
{
Console.WriteLine("'{0}'", match);
}
Alternatively, if you don't need an array.
String url = #"Lists/Versions/2_.000";
String pattern = #"(?<=/)\d+";
Console.WriteLine("'{0}'", Regex.Match(url, pattern).Value);

Detecting "\" (backslash) using Regex

I have a C# Regex like
[\"\'\\/]+
that I want to use to evaluate and return error if certain special characters are found in a string.
My test string is:
\test
I have a call to this method to validate the string:
public static bool validateComments(string input, out string errorString)
{
errorString = null;
bool result;
result = !Regex.IsMatch(input, "[\"\'\\/]+"); // result is true if no match
// return an error if match
if (result == false)
errorString = "Comments cannot contain quotes (double or single) or slashes.";
return result;
}
However, I am unable to match the backslash. I have tried several tools such as regexpal and a VS2012 extension that both seem to match this regex just fine, but the C# code itself won't. I do realize that C# is escaping the string as it is coming in from a Javascript Ajax call, so is there another way to match this string?
It does match /test or 'test or "test, just not \test
The \ is used even by Regex(es). Try "[\"\'\\\\/]+" (so double escape the \)
Note that you could have #"[""'\\/]+" and perhaps it would be more readable :-) (by using the # the only character you have to escape is the ", by the use of a second "")
You don't really need the +, because in the end [...] means "one of", and it's enough for you.
Don't eat what you can't chew... Instead of regexes use
// result is true if no match
result = input.IndexOfAny(new[] { '"', '\'', '\\', '/' }) == -1;
I don't think anyone ever lost the work because he preferred IndexOf instead of a regex :-)
You can solve this by making the string verbatim like this #:
result = !Regex.IsMatch(input, #"[\""\'\\/]+");
Since backslashes are used as escapes inside regex themselves, I find it best to use verbatim strings when working with the regex library:
string input = #"\test";
bool result = !Regex.IsMatch(input, #"[""'\\]+");
// ^^
// You need to double the double-quotes when working with verbatim strings;
// All other characters, including backslashes, remain unchanged.
if (!result) {
Console.WriteLine("Comments cannot contain quotes (double or single) or slashes.");
}
The only issue with that is that you must double your double-quotes (which is ironically what you need to do in your case).
Demo on ideone.
For the trivial case, I am able to use regexhero.net for your test expression using the simple:
\\
to validate
\test
The code generated by RegExHero:
string strRegex = #"\\";
RegexOptions myRegexOptions = RegexOptions.IgnoreCase;
Regex myRegex = new Regex(strRegex, myRegexOptions);
string strTargetString = #"\test";
foreach (Match myMatch in myRegex.Matches(strTargetString))
{
if (myMatch.Success)
{
// Add your code here
}
}

Regexp skip pattern

Problem
I need to replace all asterisk symbols('*') with percent symbol('%'). The asterisk symbols in square brackets should be ignored.
Example
[Test]
public void Replace_all_asterisks_outside_the_square_brackets()
{
var input = "Hel[*o], w*rld!";
var output = Regex.Replace(input, "What_pattern_should_be_there?", "%")
Assert.AreEqual("Hel[*o], w%rld!", output));
}
Try using a look ahead:
\*(?![^\[\]]*\])
Here's a bit stronger solution, which takes care of [] blocks better, and even escaped \[ characters:
string text = #"h*H\[el[*o], w*rl\]d!";
string pattern = #"
\\. # Match an escaped character. (to skip over it)
|
\[ # Match a character class
(?:\\.|[^\]])* # which may also contain escaped characters (to skip over it)
\]
|
(?<Asterisk>\*) # Match `*` and add it to a group.
";
text = Regex.Replace(text, pattern,
match => match.Groups["Asterisk"].Success ? "%" : match.Value,
RegexOptions.IgnorePatternWhitespace);
If you don't care about escaped characters you can simplify it to:
\[ # Skip a character class
[^\]]* # until the first ']'
\]
|
(?<Asterisk>\*)
Which can be written without comments as: #"\[[^\]]*\]|(?<Asterisk>\*)".
To understand why it works we need to understand how Regex.Replace works: for every position in the string it tries to match the regex. If it fails, it moves one character. If it succeeds, it moves over the whole match.
Here, we have dummy matches for the [...] blocks so we may skip over the asterisks we don't want to replace, and match only the lonely ones. That decision is made in a callback function that checks if Asterisk was matched or not.
I couldn't come up with a pure RegEx solution. Therefore I am providing you with a pragmatic solution. I tested it and it works:
[Test]
public void Replace_all_asterisks_outside_the_square_brackets()
{
var input = "H*]e*l[*o], w*rl[*d*o] [o*] [o*o].";
var actual = ReplaceAsterisksNotInSquareBrackets(input);
var expected = "H%]e%l[*o], w%rl[*d*o] [o*] [o*o].";
Assert.AreEqual(expected, actual);
}
private static string ReplaceAsterisksNotInSquareBrackets(string s)
{
Regex rx = new Regex(#"(?<=\[[^\[\]]*)(?<asterisk>\*)(?=[^\[\]]*\])");
var matches = rx.Matches(s);
s = s.Replace('*', '%');
foreach (Match match in matches)
{
s = s.Remove(match.Groups["asterisk"].Index, 1);
s = s.Insert(match.Groups["asterisk"].Index, "*");
}
return s;
}
EDITED
Okay here is my final attempt ;)
Using negative lookbehind (?<!) and negative lookahead (?!).
var output = Regex.Replace(input, #"(?<!\[)\*(?!\])", "%");
This also passes the test in the comment to another answer "Hel*o], w*rld!"

Categories

Resources