Replace with values a given pattern in a string - c#

Given a file with this format
// Colour
$primary-colour: if(#Model.PrimaryColour, #primaryColour, #afd05c);
$secondary-colour: if(#secondaryColour, #secondaryColour, #323f47);
// and so on
I'm trying to replace the #Model.Whatever based on a dictionary with would be something like this
var dictionary = new Dictionary<string, string>
{
{"primaryColour", "blue"},
{"secondaryColour", "red"}
};
But I'm struggling to find a way to so.
I was thinking of doing something like this:
private static String Replace(String str)
{
var dictionary = new Dictionary<string, string>
{
{"primaryColour", "blue"},
{"secondaryColour", "red"}
};
string variableValue;
string pattern = #"#Model.(?<name>\w)";
dictionary.TryGetValue(FirstCharacterToLower("${name}"), out variableValue);
var replacePattern = String.Format("{0}", variableValue);
return Regex.Replace(str, pattern, replacePattern, RegexOptions.IgnoreCase);
}
private static string FirstCharacterToLower(string str)
{
Console.WriteLine(str);
if (String.IsNullOrEmpty(str) || Char.IsLower(str, 0))
return str;
return Char.ToLowerInvariant(str[0]) + str.Substring(1);
}
But what I'm passing to the FirstCharacterToLower is just a string {name} and I'm stuck there. Can't think of a way to do it.
Any idea where to go from here?
Thanks
Edit: Based on sln comment I made this and it works
using System;
using System.Text.RegularExpressions;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var input = #"
// Colour
$primary-colour: if(#Model.PrimaryColour, #Model.PrimaryColour, #afd05c);
$secondary-colour: if(#Model.SecondaryColour, #Model.SecondaryColour, #323f47);";
Console.WriteLine(Replace(input));
}
private static String Replace(String str)
{
var dictionary = new Dictionary<string, string>
{
{"primaryColour", "blue"},
{"secondaryColour", "red"}
};
var regex = new Regex(#"#Model\.(?<name>\w+)");
var output = regex.Replace(str, v =>
{
string outVariable;
dictionary.TryGetValue(GetNameOfVariable(v.Groups["name"].Value), out outVariable);
return outVariable;
});
return output;
}
private static string GetNameOfVariable(string str)
{
Console.WriteLine(str);
if (String.IsNullOrEmpty(str) || Char.IsLower(str, 0))
return str;
return Char.ToLowerInvariant(str[0]) + str.Substring(1);
}
}

As #sln told, you have to use delegate.
private static String Replace(String str)
{
var dictionary = new Dictionary<string, string>
{
{"primaryColour", "blue"},
{"secondaryColour", "red"}
};
string pattern = #"#Model\.(?<name>\w+)";
return Regex.Replace(str, pattern, m =>
{
string key = m.Groups["name"].Value;
key = FirstCharacterToLower(key);
string value = null;
if (dictionary.TryGetValue(key, out value))
return value;
else
return m.Value;
});
}

You'd be better off describing a general regex that matches all your
keys. Then using a delegate replacement.
var dictionary = new Dictionary<string, string>
{
{"primarycolour", "blue"},
{"secondarycolour", "red"}
};
string line_original =
#"
// Colour
$primary-colour: if(#Model.PrimaryColour, #primaryColour, #afd05c);
$secondary-colour: if(#secondaryColour, #secondaryColour, #323f47);
// and so on
";
Regex RxColors = new Regex( #"#Model\.(?<name>\w+)" );
string line_new = RxColors.Replace(
line_original,
delegate(Match match)
{
string outVal;
if ( dictionary.TryGetValue( match.Groups["name"].Value.ToLower(), out outVal) )
return outVal;
return match.Groups[0].Value;
}
);
Console.WriteLine("New line: \r\n\r\n{0}", line_new );
Output:
New line:
// Colour
$primary-colour: if(blue, #primaryColour, #afd05c);
$secondary-colour: if(#secondaryColour, #secondaryColour, #323f47);
// and so on

Related

how to make a case insensitive dictionary for var input - C#

var fruitDictionary = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase) { { "Apple" , "Fruit" }, { "Orange", "Fruit" }, { "Spinach", "Greens" } };
TextRange textRange = new TextRange(richTextBox1.Document.ContentStart, richTextBox1.Document.ContentEnd);
string data = textRange.Text;
var output = new StringBuilder(data);
foreach (var kvp in fruitDictionary)
output.Replace(kvp.Key, kvp.Value);
var result = output.ToString();
richTextBox2.AppendText(result);
It works normally but if the input isnt in format it wont work. For example on Apple the output is Fruit but on apple it still says apple
By setting the dictionary's comparer to StringComparer.InvariantCultureIgnoreCase, key lookup became culture and case invariant -- i.e. var a = fruitDictionary["apple"]; and var b = fruitDictionary["ApPlE"] will yield the same results. That said, you perform your replace operation on an instance of StringBuilder which is not related to that. Both StringBuilder.Replace and String.Replace don't have overloads that let you configure string comparison options, so you would have to make an extension method.
public static string Replace(this string str, string oldValue, string newValue,
StringComparison comparison = StringComparison.Ordinal)
{
var index = str.IndexOf(oldValue, comparison);
while (index >= 0)
{
str = str.Remove(index, oldValue.Length);
str = str.Insert(index, newValue);
index = str.IndexOf(oldValue, comparison);
}
return str;
}
var fruitDictionary = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase) { { "Apple" , "Fruit" }, { "Orange", "Fruit" }, { "Spinach", "Greens" } };
TextRange textRange = new TextRange(richTextBox1.Document.ContentStart, richTextBox1.Document.ContentEnd);
string data = textRange.Text;
foreach (var kvp in fruitDictionary)
data = data.Replace(kvp.Key, kvp.Value, StringComparison.InvariantCultureIgnoreCase)
richTextBox2.AppendText(data);

C# Replace regex matched pattern using dictionary

I am trying to replace a pattern in my string where only the words between the tags should be replaced. The word that needs to be replaced resides in a dictionary as key and value pair.
Currently this is what I am trying:
string input = "<a>hello</a> <b>hello world</b> <c>I like apple</c>";
string pattern = (#"(?<=>)(.)?[^<>]*(?=</)");
Regex match = new Regex(pattern, RegexOptions.IgnoreCase);
MatchCollection matches = match.Matches(input);
var dictionary1 = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
dictionary1.Add("hello", "Hi");
dictionary1.Add("world", "people");
dictionary1.Add("apple", "fruit");
string output = "";
output = match.Replace(input, replace => { return dictionary1.ContainsKey(replace.Value) ? dictionary1[replace.Value] : replace.Value; });
Console.WriteLine(output);
Console.ReadLine();
Using this, it does replace but only the first 'hello' and not the second one. I want to replace every occurrence of 'hello' between the tags.
Any help will be much appreciated.
The problem is that the matches are:
hello
hello world
I like apple
so e.g. hello world is not in your dictionary.
Based on your code, this could be a solution:
using System;
using System.Text.RegularExpressions;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var dictionary1 = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
dictionary1.Add("hello", "Hi");
dictionary1.Add("world", "people");
dictionary1.Add("apple", "fruit");
string input = "<a>hello</a> <b>hello world</b> <c>I like apple</c>";
string pattern = ("(?<=>)(.)?[^<>]list|" + GetKeyList(dictionary1) + "(?=</)");
Regex match = new Regex(pattern, RegexOptions.IgnoreCase);
MatchCollection matches = match.Matches(input);
string output = "";
output = match.Replace(input, replace => {
Console.WriteLine(" - " + replace.Value);
return dictionary1.ContainsKey(replace.Value) ? dictionary1[replace.Value] : replace.Value;
});
Console.WriteLine(output);
}
private static string GetKeyList(Dictionary<string, string> list)
{
return string.Join("|", new List<string>(list.Keys).ToArray());
}
}
Fiddle: https://dotnetfiddle.net/zNkEDv
If someone wants to dig into this an tell me why do I need a "list|" in the list (because the first item is being ignored), I'll appreciate it.
This is another way of doing it - I parse the string into XML and then select elements containing the keys in your dictionary and then replace each element's value.
However, you have to have a valid XML document - your example lacks a root node.
var xDocument = XDocument.Parse("<root><a>hello</a> <b>hello world</b> <c>I like apple</c></root>");
var dictionary1 = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { { "hello", "Hi" }, { "world", "people" }, { "apple", "fruit" } };
string pattern = #"\w+";
Regex match = new Regex(pattern, RegexOptions.IgnoreCase);
var xElements = xDocument.Root.Descendants()
.Where(x => dictionary1.Keys.Any(s => x.Value.Contains(s)));
foreach (var xElement in xElements)
{
var updated = match.Replace(xElement.Value,
replace => {
return dictionary1.ContainsKey(replace.Value)
? dictionary1[replace.Value] : replace.Value; });
xElement.Value = updated;
}
string output = xDocument.ToString(SaveOptions.DisableFormatting);
This pattern of "\w+" matches words, not spaces.
This LINQ selects descendants of the root node where the element value contains any of the keys of your dictionary:
var xElements = xDocument.Root.Descendants().Where(x => dictionary1.Keys.Any(s => x.Value.Contains(s)));
I then iterate through the XElement enumerable collection returned and apply your replacement MatchEvaluator to just the string value, which is a lot easier!
The final output is <root><a>Hi</a><b>Hi people</b><c>I like fruit</c></root>. You could then remove the opening and closing <root> and </root> tags, but I don't know what your complete XML looks like.
This will do what you want (from what you have provided so far):
private static Dictionary<string, string> dict;
static void Main(string[] args)
{
dict =
new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
{ "hello", "Hi" },
{ "world", "people" },
{ "apple", "fruit" }
};
var input = "<a>hello</a> <b>hello world</b> apple <c>I like apple</c> hello";
var pattern = #"<.>([^<>]+)<\/.>";
var output = Regex.Replace(input, pattern, Replacer);
Console.WriteLine(output);
Console.ReadLine();
}
static string Replacer(Match match)
{
var value = match.Value;
foreach (var kvp in dict)
{
if (value.Contains(kvp.Key)) value = value.Replace(kvp.Key, kvp.Value);
}
return value;
}

Method that extracts a list of URLs that match the pattern

Well, I'm trying to create a method, using Regex , that will extract a list of URLs that matches this pattern #"http://(www\.)?([^\.]+)\.com", and so far I've done this :
public static List<string> Test(string url)
{
const string pattern = #"http://(www\.)?([^\.]+)\.com";
List<string> res = new List<string>();
MatchCollection myMatches = Regex.Matches(url, pattern);
foreach (Match currentMatch in myMatches)
{
}
return res;
}
main issue is , which code should I use in foreach loop
res.Add(currentMatch.Groups.ToString());
or
res.Add(currentMatch.Value);
Thanks!
You just need to get all .Match.Values. In your code, you should use
res.Add(currentMatch.Value);
Or, just use LINQ:
res = Regex.Matches(url, pattern).Cast<Match>()
.Select(p => p.Value)
.ToList();
res.Add(currentMatch.Groups.ToString()); will give: System.Text.RegularExpressions.GroupCollection so you didn't test it.
How many matches do you expect from the parameter url?
I would use this:
static readonly Regex _domainMatcher = new Regex(#"http://(www\.)?([^\.]+)\.com", RegexOptions.Compiled);
public static bool IsValidDomain(string url)
{
return _domainMatcher.Match(url).Success;
}
or
public static string ExtractDomain(string url)
{
var match = _domainMatcher.Match(url);
if(match.Success)
return match.Value;
else
return string.Empty;
}
Because the parameter is called url so it should be one url
If there are more possibilities and you want to extract all domainnames that matches the pattern:
public static IEnumerable<string> ExtractDomains(string data)
{
var result = new List<string>();
var match = _domainMatcher.Match(data);
while (match.Success)
{
result.Add(match.Value);
match = match.NextMatch();
}
return result;
}
Notice the IEnumerable<> instead of List<> because there is no need to modify the result by the caller.
Or this lazy variant:
public static IEnumerable<string> ExtractDomains(string data)
{
var match = _domainMatcher.Match(data);
while (match.Success)
{
yield return match.Value;
match = match.NextMatch();
}
}

Remove specific symbol from string

I have different string that starts and ends with { } like so {somestring}. I want to remove the delimiters from the string so that it shows somestring only. I can't do anything that counts the letters because I don't always know the length of the string.
Maybe this will help. Here is the code, somewhere here I want to delete the delimiters.
private static MvcHtmlString RenderDropDownList(FieldModel model)
{
ISerializer serializer = new SerializeJSon();
var value = "";
var tb1 = new TagBuilder("select");
tb1.MergeAttribute("id", model.QuestionId);
tb1.MergeAttribute("name", model.QuestionId);
tb1.MergeAttributes(GetHtmlAttributes(model.HtmlAttributes));
tb1.AddCssClass("form-field");
var sb = new StringBuilder();
MatchCollection matches = RegexHelper.GetBetweenDelimiter(model.FieldValues, "{", "}");
foreach (Match match in matches)
{
var o = match; //Solution var o = match.toString();
var tb2 = new TagBuilder("option");
//Solution string newString = o.trim(new [] { "{","}"});
tb2.SetInnerText(o.ToString()); //Solution tb2.SetInnerText(newString);
sb.Append(tb2.ToString(TagRenderMode.Normal) + "\n");
}
tb1.InnerHtml = sb.ToString();
return new MvcHtmlString(tb1.ToString(TagRenderMode.Normal));
}
string newString = originalString.Trim(new[] {'{', '}'});
Can you use Replace
string somestring = somestring.Replace("{","").Replace("}","");
Alternatively, you can use StartsWith and EndsWith which will only remove from the beginning and the end of the string, for example:
string foo = "{something}";
if (foo.StartsWith("{"))
{
foo = foo.Remove(0, 1);
}
if (foo.EndsWith("}"))
{
foo = foo.Remove(foo.Length-1, 1);
}
You could use replace e.g.
string someString = "{somestring}";
string someOtherString = someString.Replace("{","").Replace("}","");

Question on searching a string using regex and storing in a List

Below is code used to search a string where Identity=" " exists and stores that line in a List. I need to add to this search so that it not only picks up Identity=" " but ALSO where FrameworkSiteID=" ". How can I modify the below code to do this?
Many thanks.
List<KeyValuePair<string, string>> IdentityLines = new List<KeyValuePair<string, string>>();
foreach(FileInfo file in Files)
{
string line = "";
using(StreamReader sr = new StreamReader(file.FullName))
{
while(!String.IsNullOrEmpty(line = sr.ReadLine()))
{
if (line.ToUpper().Contains("IDENTITY="))
{
string login = reg.Match(line).Groups[0].Value;
IdentityLines.Add(new KeyValuePair<string, string>(file.Name, login));
}
else
{
IdentityLines.Add(new KeyValuePair<string, string>(file.Name,"NO LOGIN"));
}
}
//More additional code, not included..
Fixed:
static void TestRegularExpression()
{
String line = "Some text here, blah blah Identity=\"EDN\\nuckol\" and FRAMEworkSiteID=\"DesotoGeneral\" and other stuff.";
Match m1 = Regex.Match(line, "((identity)(=)('|\")([a-zA-Z]*)([\\\\]*)([a-zA-Z]*)('|\"))", RegexOptions.IgnoreCase);
Match m2 = Regex.Match(line, "((frameworkSiteID)(=)('|\")([a-zA-Z]*)('|\"))", RegexOptions.IgnoreCase);
if (m1.Success && m2.Success)
{
//...
Console.WriteLine("Success!");
Console.ReadLine();
}
}
Here's a regular expression tester I like to use.
http://gskinner.com/RegExr/
-Matt

Categories

Resources