Regular Expression for Digits and Special Characters - C# - c#

I use Html-Agility-Pack to extract information from some websites. In the process I get data in the form of string and I use that data in my program.
Sometimes the data I get includes multiple details in the single string. As the name of this Movie "Dog Eats Dog (2012) (2012)". The name should have been "Dog Eats Dog (2012)" rather than the first one.
Above is the one example from many. In order to correct the issue I tried to use string.Distinct() method but it would remove all the duplicate characters in the string as in above example it would return "Dog Eats (2012)". Now it solved my initial problem by removing the 2nd (2012) but created a new one by changing the actual title.
I thought my problem could be solved with Regex but I have no idea as to how I can use it here. As far as I know if I use Regex it would tell me that there are duplicate items in the string according to the defined Regex code.
But how do I remove it? There can be a string like "Meme 2013 (2013) (2013)".
Now the actual title is "Meme 2013" with year (2013) and the duplicate year (2013). Even if I get a bool value indicating that the string has duplicate year, I cant think of any method to actually remove the duplicate substring.
The duplicate year always comes in the end of the string. So what should be the Regex that I would use to determine that the string actually has two years in it, like (2012) (2012)?
If I can correctly identify the string contains duplicate maybe I can use string.LastIndexOf() to try and remove the duplicate part. If there is any better way to do it please let me know.
Thanks.

The right regex is "( \(\d{4}\))\1+".
string pattern = #"( \(\d{4}\))\1+";
new Regex(pattern).Replace(s, "$1");
Example here : https://repl.it/Evcy/2
Explanation:
Capture one " (dddd)" block, and remove all following identical ones.
( \(\d{4}\)) does the capture, \1+ finds any non empty sequence of that captured block
Finally, replace the initial block and its copies by the initial block alone.

This regex will allow for any pattern of whitespace, even none, as in (2013)(2013)
`#"(\(\d{4}\))(?:\s*\1)+"`
I have a demo of it here

Related

regex to replace last two digits of an assemblyversion

I'm working with teamcity and a C# project, and I want to use the file content patcher to replace the last two digits in an assemblyversion (eg: the two stars in [assembly: AssemblyVersion("1.0.*.*")]). I've found the docs on the file content patcher and it suggests using
(^\s*\[\s*assembly\s*:\s*((System\s*\.)?\s*Reflection\s*\.)?\s*AssemblyVersion(Attribute)?\s*\(\s*#?\")(([0-9\*]+\.)+)[0-9\*]+(\"\s*\)\s*\]) if you just want to change the LAST digit, which got me partway there.
I figured if I did (^\s*\[\s*assembly\s*:\s*((System\s*\.)?\s*Reflection\s*\.)?\s*AssemblyVersion(Attribute)?\s*\(\s*#?\")(([0-9\*]+(\.))+)[0-9\*]+(\"\s*\)\s*\]) it would capture the last period as it's own group, letting me replace the two stars without a problem. However it looks like the first star is still captured in the group with the 1.0 (so the group becomes 1.0.*.).
What I want is to restrict the first group to capturing the {major}.{minor}. and then have the last period be it's own group so I could do something like: $1$5\%build.number%$7%build.vcs.number%$8 which would give me AssemblyVersion("1.0.{build#}.{vcs#}")]
Generally I can stumble through regex without many problems but I've been working on this for the last few hours and I can't seem to get it correct. Any information on reaching this conclusion would he appreciated.
If you want to keep to the solution you found to replace while also validating, you may use
(^\s*\[\s*assembly\s*:\s*((System\s*\.)?\s*Reflection\s*\.)?‌​\s*AssemblyVersion(A‌​ttribute)?\s*\(\s*#?‌​\")([0-9\*]+\.[0-9\*‌​]+)\.([0-9\*]+\.[0-9‌​\*]+)(\"\s*\)\s*\])
and replace with $1$5.%build.number%.%build.vcs.number%$7.
See the regex demo
I just unrolled the ([0-9\*]+(\.))+ into ([0-9\*]+\.[0-9\*‌​]+)\.([0-9\*]+\.[0-9‌​\*]+), 2 groups (([0-9\*]+\.[0-9\*‌​]+)) separated with a literal dot (\.). I also had to remove the [0-9\*]+ that followed the ([0-9\*]+(\.))+ pattern.
I would first extract 1.0.*.* and then use Version.Parse.
Much smaller regex (and can be shortened more)..
string input = #"[assembly:AssemblyVersion(""1.2.3.4"")]";
var verStr = Regex.Match(input, #"\[.+?\(\""(.+?)\""\)\]").Groups[1].Value;
var version = Version.Parse(verStr);

C# Matching a recursive structure in a string

I'm currently working on a program that loads up a text file, searches through it to find a specific structure, and then replaces a certain part of that structure with different text.
The structure I need to find and extract is "N"(N) where N is any character. For example. Lets say I had a text file like this:
Everyone knows the saying "Do not do more than you can do" (Jim Doe).
Well, I'm here to tell you that this saying is awesome. Here is
another, "The sky is blue and the sun is bright" (Sally Wantsmore).
I would want to be able to match the text "Do not do more than you can do" (Jim Doe) along with "The sky is blue and the sun is bright" (Sally Wantsmore).
I don't think there is really a way to do this with a regular expression from the best of my knowledge. I've been trying for the last few days. I can't get it to work, it's a recursive pattern by nature. My question is, how would I go about writing C# code to parse through and find these patterns. I would like to do something where I can find the position within the string and the length, that way I can then extract it from the string.
EDIT
I need to be able to capture all characters in the quote. This means that there could also be another set of quotes within the quote and even another set of parenthesis. This means that the structure could also contain a match within itself.
I'm now trying to use this expression because I need to be able to capture all characters within a quote: \"(.+)\" \(([\w ]+)\)
The listed answers below both work. However, I've discovered a limitation. There is a possible recursive structure to this. The problem I am currently having is when there is a "N"(N) inside of a "N"(N)". For example:
"Random quote" (random person) Here is a fun saying, "The sky is blue and
the sun is bright, some even say "really bright" (others)" (Sally
Wantsmore).
This presents many problems. There is only one match because it takes the very first ", and then finds the last " just after (others) and finds the set of parens for (Sally Wantsmore) and only finds that match. However, I desire for it to find all the matches, especially the beginning one and last one separably, and even the inner quote. Is this possible with Regular expressions? If not, how do I go about solving this with Recursive c# code.
The following regex should find the two things you're looking for:
\"([\w ]+)\" \(([\w ]+)\)
In C# you can use Regex.Match to retrieve the two items in brackets.
An example on how you could have it in C#:
var quotes = Regex.Matches(#"Everyone knows the saying ""Do not do more than you can do"" (Jim Doe). Well, I'm here to tell you that this saying is awesome. Here is another, ""The sky is blue and the sun is bright"" (Sally Wantsmore).",
"(?<Quotes>\"(?<Text>[\\w ]+)\\\" \\((?<Author>[\\w ]+)\\))", RegexOptions.Singleline);
foreach (Match quote in quotes)
{
var text = quote.Groups["Text"].Value;
var author = quote.Groups["Author"].Value;
Console.WriteLine($"Text: {text}, Author: {author}");
}

how to replace the exact word by another in a list?

I have a list like :
george fg
michel fgu
yasser fguh
I would like to replace fg, fgu, and fguh by "fguhCool" I already tried something like this :
foreach (var ignore in NameToPoulate)
{
tempo = ignore.Replace("fg", "fguhCool");
NameToPoulate_t.Add(tempo);
}
But then "fgu" become "fguhCoolu" and "fguh" become "fguhCooluh" is there are a better idea ?
Thanks for your help.
I assume that this is a homework assignment and that you are being tested for the specific algorihm rather than any code that does the job.
This is probably what your teacher has in mind:
Students will realize that the code should check for "fguh" first, then "fgu" then "fg". The order is important because replacing "fg" will, as you have noticed, destroy a "fguh".
This will by some students be implemented as a loop with if-else conditions in them. So that you will not replace a "fg" that is within an already replaced "fguhCool".
But then you will find that the algorithm breaks down if "fg" and "fgu" are both within the same string. You cannot then allow the presence of "fgu" prevent you to check for "fg" at a different part of the string.
The answer that your teacher is looking for is probably that you should first locate "fguh", "fgu" and "fg" (in that order) and replace them with an intermediary string that doesn't contain "fg". Then after you have done that, you can search for that intermediary string and replace it with "fguhCool".
You could use regular expressions:
Regex.Replace(#"\bfg\b", "fguhCool");
The \b matches a so-called word boundary which means it matches the beginnnig or end of a word (roughly, but for this purpose enough).
Use a regular expression:
Regex.Replace("fg(uh?)?", "fguhCool");
An alternative would be replacing the long words for the short ones first, then replacing the short for the end value (I'm assuming all words - "fg", "fgu" and "fguh" - would map to the same value "fguhCool", right?)
tempo = ignore
.Replace("fguh", "fg")
.Replace("fgu", "fg")
.Replace("fg", "fguhCool");
Obs.: That assumes those words can appear anywhere in the string. If you're worried about whole words (i.e. cases where those words are not substrings of a bigger word), then see #Joey's answer (in this case, simple substitutions won't do, regexes are really the best option).

Regex between, from the last to specific end

Today my wish is to take text form the string.
This string must be, between last slash and .partX.rar or .rar
First I tried to find edge's end of the word and then the beginning. After I get that two elements I merged them but I got empty results.
String:
http://hosting.xyz/1234/15-game.part4.rar.html
http://hosting.xyz/1234/16-game.rar.html
Regex:
Begin:(([^/]*)$) - start from last /
End:(.*(?=.part[0-9]+.rar|.rar)) stop before .partX.rar or .rar
As you see, if I merge that codes I won't get any result.
What is more, "end" select me only .partX instead of .partX.rar
All what I want is:
15-game.part4.rar and 16-game.rar
What i tried:
(([^/]*)$)(.*(?=.part[0-9]+.rar|.rar))
(([^/]*)$)
(.*(?=.part[0-9]+.rar|.rar))
I tried also
/[a-zA-Z0-9]+
but I do not know how select symbols.. This could be the easiest way. But this select only letters and numbers, not - or _.
If I could select symbols..
You don't really need a regex for this as you can merely split the url on / and then grab the part of the file name that you need. Since you didn't mention a language, here's an implementation in Perl:
use strict;
use warnings;
my $str1="http://hosting.xyz/1234/15-game.part4.rar.html";
my $str2="http://hosting.xyz/1234/16-game.rar.html";
my $file1=(split(/\//,$str1))[-1]; #last element of the resulting array from splitting on slash
my $file2=(split(/\//,$str2))[-1];
foreach($file1,$file2)
{
s/\.html$//; #for each file name, if it ends in ".html", get rid of that ending.
print "$_\n";
}
The output is:
15-game.part4.rar
16-game.rar
Nothing could be simpler! :-)
Use this:
new Regex("^.*\/(.*)\.html$")
You'll find your filename in the first captured group (don't have a c# compiler at hand, so can't give you working sample, but you have a working regex now! :-) )
See a demo here: http://rubular.com/r/UxFNtJenyF
I'm not a C# coder so can't write full code here but I think you'll need support of negative lookahead here like this:
new Regex("/(?!.*/)(.+?)\.html$");
Matched Group # 1 will have your string i.e. "16-game.rar" OR "15-game.part4.rar"
Use two regexes:
start to substitute .*/ with nothing;
then substitute \.html with nothing.
Job done!

Parsing a CSV File with C#, ignoring thousand separators

Working on a program that takes a CSV file and splits on each ",". The issue I have is there are thousand separators in some of the numbers. In the CSV file, the numbers render correctly. When viewed as a text document, they are shown like below:
Dog,Cat,100,100,Fish
In a CSV file, there are four cells, with the values "Dog", "Cat", "100,000", "Fish". When I split on the "," to an array of strings, it contains 5 elements, when what I want is 4. Anyone know a way to work around this?
Thanks
There are two common mistakes made when reading csv code: using a split() function and using regular expressions. Both approaches are wrong, in that they are prone to corner cases such as yours and slower than they could be.
Instead, use a dedicated parser such as Microsoft.VisualBasic.TextFieldParser, CodeProject's FastCSV or Linq2csv, or my own implemention here on Stack Overflow.
Typically, CSV files would wrap these elements in quotes, causing your line to be displayed as:
Dog,Cat,"100,100",Fish
This would parse correctly (if using a reasonable method, ie: the TextFieldParser class or a 3rd party library), and avoid this issue.
I would consider your file as an error case - and would try to correct the issue on the generation side.
That being said, if that is not possible, you will need to have more information about the data structure in the file to correct this. For example, in this case, you know you should have 4 elements - if you find five, you may need to merge back together the 3rd and 4th, since those two represent the only number within the line.
This is not possible in a general case, however - for example, take the following:
100,100,100
If that is 2 numbers, should it be 100100, 100, or should it be 100, 100100? There is no way to determine this without more information.
you might want to have a look at the free opensource project FileHelpers. If you MUST use your own code, here is a primer on the CSV "standard" format
well you could always split on ("\",\"") and then trim the first and last element.
But I would look into regular expressions that match elements with in "".
Don't just split on the , split on ", ".
Better still, use a CSV library from google or codeplex etc
Reading a CSV file in .NET?
You may be able to use Regex.Replace to get rid of specifically the third comma as per below before parsing?
Replaces up to a specified number of occurrences of a pattern specified in the Regex constructor with a replacement string, starting at a specified character position in the input string. A MatchEvaluator delegate is called at each match to evaluate the replacement.
[C#] public string Replace(string, MatchEvaluator, int, int);
I ran into a similar issue with fields with line feeds in. Im not convinced this is elegant, but... For mine I basically chopped mine into lines, then if the line didnt start with a text delimeter, I appended it to the line above.
You could try something like this : Step through each field, if the field has an end text delimeter, move to the next, if not, grab the next field, appaend it, rince and repeat till you do have an end delimeter (allows for 1,000,000,000 etc) ..
(Im caffeine deprived, and hungry, I did write some code but it was so ugly, I didnt even post it)
Do you know that it will always contain exactly four columns? If so, this quick-and-dirty LINQ code would work:
string[] elements = line.Split(',');
string element1 = elements.ElementAt(0);
string element2 = elements.ElementAt(1);
// Exclude the first two elements and the last element.
var element3parts = elements.Skip(2).Take(elements.Count() - 3);
int element3 = Convert.ToInt32(string.Join("",element3parts));
string element4 = elements.Last();
Not elegant, but it works.

Categories

Resources