What is the best way to enumerate a regex-replacement in C#.
For example if I wanted every "<intent-filter" match to be replaced by "<intent-filter android:label=label#". The # sign is a incremental digit. What would be the best way to code it?
You can use an incremented counter in the anonymous method specified as the MatchEvaluator callback. The (?<=…) is positive lookbehind; it is matched by the regex evaluator, but not removed.
string input = "a <intent-filter data=a /> <intent-filter data=b />";
int count = 0;
string result = Regex.Replace(input, #"(?<=\<intent-filter)",
_ => " android:label=label" + count++);
Don't bother with Regexes for this one. Do something along the lines of:
var pieces = text.Split(new string[] { "xx" });
var sb = new StringBuilder();
var idx = 0;
foreach (var piece in pieces)
{
sb.Append(piece);
sb.Append(" android:label=label");
sb.Append(idx);
}
// oops, homework assignment: remove the last "<intent-filter android:label=label#"
Related
I have a large string separated by line breaks.
Example:
This is my first sentence and here i will search for the word my
This is my second sentence
Using the code below, if I search for 'my' it will only return the 2 instances of 'my' from the first sentence and not the second.
I wish to display the sentence the phrase is found in - which works fine but its just that it does not search anything after the first line break if found.
Code;
var regex = new Regex(string.Format("[^.!?;]*({0})[^.?!;]*[.?!;]", userSearchCriteraInHere, RegexOptions.Singleline));
var results = regex.Matches(largeStringInHere);
for (int i = 0; i < results.Count; i++)
{
searchCriteriaFound.Append((results[i].Value.Trim()));
searchCriteriaFound.Append(Environment.NewLine);
}
Code Edit:
string pattern = #".*(" + userSearchCriteraInHere + ")+.*";
RegexOptions options = RegexOptions.Multiline;
foreach (Match m in Regex.Matches(largeStringInHere, pattern, options))
{
searchCriteriaFound.Append(m.Value);
}
var userSearchCriteraInHere = "my";
var largeStringInHere = #"This is my first sentence and here i will search for the word my.
This is my second sentence.";
var regex = new Regex(string.Format("[^.!?;]*({0})[^.?!;]*[.?!;]", userSearchCriteraInHere), RegexOptions.Singleline);
var results = regex.Matches(largeStringInHere);
Console.WriteLine(results.Count);
var searchCriteriaFound = new StringBuilder();
for (int i = 0; i < results.Count; i++)
{
searchCriteriaFound.Append((results[i].Value.Trim()));
searchCriteriaFound.Append(Environment.NewLine);
}
Console.Write(searchCriteriaFound.ToString());
This returns the following output:
2
This is my first sentence and here i will search for the word my.
This is my second sentence.
I did need to add periods at the end of your sentences, as your regex expects them.
Is there a particular reason not to just search for the word "my" multiple times in the following way:
(my)+
You can test it over at the following URL on Regex101: https://regex101.com/r/QIHWKf/1
If you want to match the whole sentence that has "my" you can use the following:
.*(my)+.*
https://regex101.com/r/QIHWKf/2
Here your full match is the whole sentence, and your first group match is the "my".
Change
Regex(string.Format("[^.!?;]*({0})[^.?!;]*[.?!;]", userSearchCriteraInHere, RegexOptions.Singleline)
To
Regex(string.Format("[^.!?;]*({0})[^.?!;]*[.?!;]", userSearchCriteraInHere, RegexOptions.Multiline)
This changes the meaning of the symbols ^ and $ to be at the beginning/end of a line, rather than the entire string.
You could use a word boundary \b to prevent it from being part of a larger match like for example mystery and change the option to RegexOptions.Multiline instead of RegexOptions.Singleline to let ^ and $ match the end of the line.
^.*\bmy\b.*$
Regex demo
Test
To get all lines containing 'my' word, you can try this:
Code
static string GetSentencesContainMyWord(StreamReader file)
{
int counter = 0;
string line;
var sb = new StringBuilder();
while ((line = file.ReadLine()) != null)
{
if (line.Contains("my"))
sb.Append(line + Environment.NewLine);
counter++;
}
return sb.ToString();
}
How could I use a for loop to go through each iteration of a given phrase in a string? For instance, say I had the following string:
Hey, this is an example string. A string is a collection of characters.
And every time there was an "is", I wanted to assign the three characters after it to a new string. I understand how to do that ONCE, but I'm trying to figure out how a for loop could be used to go through multiple instances of the same word.
If you must use a for-loop for whatever reason, you can replace the relevant part of the code provided by ja72 with:
for (int i = 0; i < text.Length; i++)
{
if (text[i] == 'i' && text[i+1] == 's')
sb.Append(text.Substring(i + 2, 3));
}
Unfortunately, I don't have enough reputation to add this as a comment here, hence posting it as an answer!
Is this what you want?
static void Main(string[] args)
{
string text=#"Hey, this is an example string. A string is a collection of characters.";
StringBuilder sb=new StringBuilder();
int i=-1;
while ((i=text.IndexOf("is", i+1))>=0)
{
sb.Append(text.Substring(i+2, 3));
}
string result=sb.ToString();
}
//result " is an a "
You can use a regex like this:
Regex re = new Regex("(?:is)(.{3})");
This regex looks for is (?:is), and takes the next three characters (.{3})
Then you use the regex to find all matches: Regex.Matches(). This will return a match for each is found in the string, followed by 3 characters. Each match has two groups:
Group 0: that includes is and the next three characters
Group 1: which includes the next thress characters
Matches matches = re.Matches("Hey, this is an example string. A string is a collection of characters.");
StringBuilder sb = new StringBuilder();
foreach (Match m in matches)
{
sb.Append(m.Groups1.Value);
}
Using Regex is much faster than looping through the characters of the string. Even more if you use RegexOptions.Compiled in your regex constructor: Regex Constructor (String, RegexOptions)
I need to replace a word that starts with %.
For example Welcome to home | %brand %productName
hoping to split on words begining with % which would give me { brand, productName }.
My regex is less than average so would appreciate help with this.
Following code might help you :
string[] splits = "Welcome to home | %brand %productName".Split(' ');
List<string> lstdata = new List<string>();
for(i=0;i<splits.length;i++)
{
if(splits[i].StartsWith("%"))
lstdata.Add(splits[i].Replace('%',''));
}
Nothing wrong with string.split approach, mind you, but here's a regex approach:
string input = #"Welcome to home | %brand %productName";
string pattern = #"%\S+";
var matches = Regex.Matches(input, pattern);
string result = string.Empty;
for (int i = 0; i < matches.Count; i++)
{
result += "match " + i + ",value:" + matches[i].Value + "\n";
}
Console.WriteLine(result);
Try this:
(?<=%)\w+
This looks for any combination of word characters immediately preceded by a percent symbol.
Now, if you're doing search and replace on these matches, you'll probably want to remove the % sign as well, so you'd need to remove the lookbehind group and just have this:
%\w+
But in doing so, your replacement code would need to trim off the % sign from each match to get the word by itself.
I have a List of words I want to ignore like this one :
public List<String> ignoreList = new List<String>()
{
"North",
"South",
"East",
"West"
};
For a given string, say "14th Avenue North" I want to be able to remove the "North" part, so basically a function that would return "14th Avenue " when called.
I feel like there is something I should be able to do with a mix of LINQ, regex and replace, but I just can't figure it out.
The bigger picture is, I'm trying to write an address matching algorithm. I want to filter out words like "Street", "North", "Boulevard", etc. before I use the Levenshtein algorithm to evaluate the similarity.
How about this:
string.Join(" ", text.Split().Where(w => !ignoreList.Contains(w)));
or for .Net 3:
string.Join(" ", text.Split().Where(w => !ignoreList.Contains(w)).ToArray());
Note that this method splits the string up into individual words so it only removes whole words. That way it will work properly with addresses like Northampton Way #123 that string.Replace can't handle.
Regex r = new Regex(string.Join("|", ignoreList.Select(s => Regex.Escape(s)).ToArray()));
string s = "14th Avenue North";
s = r.Replace(s, string.Empty);
Something like this should work:
string FilterAllValuesFromIgnoreList(string someStringToFilter)
{
return ignoreList.Aggregate(someStringToFilter, (str, filter)=>str.Replace(filter, ""));
}
What's wrong with a simple for loop?
string street = "14th Avenue North";
foreach (string word in ignoreList)
{
street = street.Replace(word, string.Empty);
}
If you know that the list of word contains only characters that do not need escaping inside a regular expression then you can do this:
string s = "14th Avenue North";
Regex regex = new Regex(string.Format(#"\b({0})\b",
string.Join("|", ignoreList.ToArray())));
s = regex.Replace(s, "");
Result:
14th Avenue
If there are special characters you will need to fix two things:
Use Regex.Escape on each element of ignore list.
The word-boundary \b will not match a whitespace followed by a symbol or vice versa. You may need to check for whitespace (or other separating characters such as punctuation) using lookaround assertions instead.
Here's how to fix these two problems:
Regex regex = new Regex(string.Format(#"(?<= |^)({0})(?= |$)",
string.Join("|", ignoreList.Select(x => Regex.Escape(x)).ToArray())));
If it's a short string as in your example, you can just loop though the strings and replace one at a time. If you want to get fancy you can use the LINQ Aggregate method to do it:
address = ignoreList.Aggregate(address, (a, s) => a.Replace(s, String.Empty));
If it's a large string, that would be slow. Instead you can replace all strings in a single run through the string, which is much faster. I made a method for that in this answer.
LINQ makes this easy and readable. This requires normalized data though, particularly in that it is case-sensitive.
List<string> ignoreList = new List<string>()
{
"North",
"South",
"East",
"West"
};
string s = "123 West 5th St"
.Split(' ') // Separate the words to an array
.ToList() // Convert array to TList<>
.Except(ignoreList) // Remove ignored keywords
.Aggregate((s1, s2) => s1 + " " + s2); // Reconstruct the string
Why not juts Keep It Simple ?
public static string Trim(string text)
{
var rv = text.trim();
foreach (var ignore in ignoreList) {
if(tv.EndsWith(ignore) {
rv = rv.Replace(ignore, string.Empty);
}
}
return rv;
}
You can do this using and expression if you like, but it's easier to turn it around than using a Aggregate. I would do something like this:
string s = "14th Avenue North"
ignoreList.ForEach(i => s = s.Replace(i, ""));
//result is "14th Avenue "
public static string Trim(string text)
{
var rv = text;
foreach (var ignore in ignoreList)
rv = rv.Replace(ignore, "");
return rv;
}
Updated For Gabe
public static string Trim(string text)
{
var rv = "";
var words = text.Split(" ");
foreach (var word in words)
{
var present = false;
foreach (var ignore in ignoreList)
if (word == ignore)
present = true;
if (!present)
rv += word;
}
return rv;
}
If you have a list, I think you're going to have to touch all the items. You could create a massive RegEx with all your ignore keywords and replace to String.Empty.
Here's a start:
(^|\s+)(North|South|East|West){1,2}(ern)?(\s+|$)
If you have a single RegEx for ignore words, you can do a single replace for each phrase you want to pass to the algorithm.
I want to split a string like "001A" into "001" and "A"
string[] data = Regex.Split("001A", "([A-Z])");
data[0] -> "001"
data[1] -> "A"
Match match = Regex.Match(s, #"^(\d+)(.+)$");
string numeral = match.Groups[1].Value;
string tail = match.Groups[2].Value;
This is Java, but it should be translatable to other flavors with little modification.
String s = "123XYZ456ABC";
String[] arr = s.split("(?<=\\d)(?=\\D)|(?<=\\D)(?=\\d)");
System.out.println(Arrays.toString(arr));
// prints "[123, XYZ, 456, ABC]"
As you can see, this splits a string wherever \d is followed by a \D or vice versa. It uses positive and negative lookarounds to find the places to split.
If your code is as simple|complicated as your 001A sample, your should not be using a Regex but a for-loop.
And if there's more like 001A002B then you could
var s = "001A002B";
var matches = Regex.Matches(s, "[0-9]+|[A-Z]+");
var numbers_and_alphas = new List<string>();
foreach (Match match in matches)
{
numbers_and_alphas.Add(match.Value);
}
You could try something like this to retrieve the integers from the string:
StringBuilder sb = new StringBuilder();
Regex regex = new Regex(#"\d*");
MatchCollection matches = regex.Matches(inputString);
for(int i=0; i < matches.count;i++){
sb.Append(matches[i].value + " ");
}
Then change the regex to match on characters and perform the same loop.