Related
This question already has answers here:
what's the quickest way to extract a 5 digit number from a string in c#
(8 answers)
Closed 9 years ago.
What's the best way to extract the number part from this string? I looked at RegularExpressions but they confuse the hell out of me. Is it possible with SubString?
/store/457987680928164?id=2
All I require is the numbers.
RegEx is a good way to go with this problem, but if you're set on using SubString...
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string test = "/store/457987680928164?id=2";
int start = test.IndexOfAny("0123456789".ToCharArray());
int end = test.IndexOf("?");
Console.WriteLine(test.Substring(start, end - start));
Console.ReadLine();
}
}
}
You should get RadExpression Designer and teach yourself RegEx's with the cheat sheets.
If RegEx's frighten you, simply do it in a loop which will often be quicker:
string s = "/store/457987680928164?id=2";
string numericInput = string.Empty;
foreach(char c in s)
{
if (char.IsDigit(c))
numericInput += c;
}
I'd prefer Regex, this isn't using substring runs faster than it in my examples
static long GrabFirstLongFromString(string input)
{
string intAsString = String.Empty;
bool startedInt = false;
foreach(char c in input)
{
if (Char.IsDigit(c))
{
startedInt = true; //really only care about the first digit
intAsString += c;
}
else if (startedInt)
return long.Parse(intAsString);
}
return -1; //define a default, since this only does a 0 or positive I picked a negative
}
This does it with regular expressions
private static Regex digitsOnly = new Regex(#"[^\d]");
public static string RemoveNonNumbers(string input)
{
return digitsOnly.Replace(input, "");
}
Or just simple Regex:
Regex r = new Regex(#"\d+");
MatchCollection m = r.Matches("/store/457987680928164?id=2");
if (m.Count > 0)
{
Console.WriteLine(string.Format("Big number: {0} - Little number: {1}", m[0], m[1]));
}
The above prints:
Big number: 457987680928164 - Little number: 2
I would definitely recommend using RegEx for this. String pattern matching and extraction is really an ideal scenario for Regular Expressions.
Here is a RegEx that will match the string sample you provided, with capturing parenthesis for the numeric parts of the String:
^/store/(\d+)\?id=(\d+)
You can verify it here in the Regex Tester. I tested it using your sample String and the RegEx I wrote above.
I came up with these extension methods to simplify common string parsing tasks:
private static string Substring(string str, string value, bool isLastValue, bool isAfterValue, StringComparison comparisonType, string defaultValue)
{
int pos = isLastValue ? str.LastIndexOf(value, comparisonType) : str.IndexOf(value, comparisonType);
if (pos == -1) return defaultValue;
return isAfterValue ? str.Substring(pos + value.Length) : str.Substring(0, pos);
}
public static string SubstringBeforeFirst(this string str, string value, StringComparison comparisonType = StringComparison.CurrentCulture, string defaultValue = "")
{
return Substring(str, value, false, false, comparisonType, defaultValue);
}
public static string SubstringBeforeLast(this string str, string value, StringComparison comparisonType = StringComparison.CurrentCulture, string defaultValue = "")
{
return Substring(str, value, true, false, comparisonType, defaultValue);
}
public static string SubstringAfterFirst(this string str, string value, StringComparison comparisonType = StringComparison.CurrentCulture, string defaultValue = "")
{
return Substring(str, value, false, true, comparisonType, defaultValue);
}
public static string SubstringAfterLast(this string str, string value, StringComparison comparisonType = StringComparison.CurrentCulture, string defaultValue = "")
{
return Substring(str, value, true, true, comparisonType, defaultValue);
}
As for getting the number from your example:
string s = "/store/457987680928164?id=2";
string number = s.SubstringAfterLast("/").SubstringBeforeFirst("?");
YARS (Yet Another Regex Solution)
The following:
var str = "/store/457987680928164?id=2";
var regex = new Regex(#"\d+");
foreach (Match match in regex.Matches(str))
{
Console.WriteLine(match.Value);
}
outputs this:
457987680928164
2
string str = "/store/457987680928164?id=2";
string num = new string(str.Remove(str.IndexOf("?")).Where(a => char.IsDigit(a)).ToArray());
string sentence = "We know it contains 'camel' word.";
// Camel can be in different cases:
string s1 = "CAMEL";
string s2 = "CaMEL";
string s3 = "CAMeL";
// ...
string s4 = "Camel";
// ...
string s5 = "camel";
How to replace 'camel' in sentence with 'horse' despite of string.Replace doesn't support ignoreCase on left string?
Use a regular expression:
var regex = new Regex( "camel", RegexOptions.IgnoreCase );
var newSentence = regex.Replace( sentence, "horse" );
Of course, this will also match words containing camel, but it's not clear if you want that or not.
If you need exact matches you can use a custom MatchEvaluator.
public static class Evaluators
{
public static string Wrap( Match m, string original, string format )
{
// doesn't match the entire string, otherwise it is a match
if (m.Length != original.Length)
{
// has a preceding letter or digit (i.e., not a real match).
if (m.Index != 0 && char.IsLetterOrDigit( original[m.Index - 1] ))
{
return m.Value;
}
// has a trailing letter or digit (i.e., not a real match).
if (m.Index + m.Length != original.Length && char.IsLetterOrDigit( original[m.Index + m.Length] ))
{
return m.Value;
}
}
// it is a match, apply the format
return string.Format( format, m.Value );
}
}
Used with the previous example to wrap the match in a span as:
var regex = new Regex( highlightedWord, RegexOptions.IgnoreCase );
foreach (var sentence in sentences)
{
var evaluator = new MatchEvaluator( match => Evaluators.Wrap( match, sentence, "<span class='red'>{0}</span>" ) );
Console.WriteLine( regex.Replace( sentence, evaluator ) );
}
Add an extension method for string to do the trick:
Usage:
string yourString = "TEXTTOREPLACE";
yourString.Replace("texttoreplace", "Look, I Got Replaced!", StringComparison.OrdinalIgnoreCase);
Code:
using System;
using System.Collections.Generic;
using System.IO;
public static class Extensions
{
public static string Replace(this string source, string oldString, string newString, StringComparison comp)
{
int index = source.IndexOf(oldString, comp);
// Determine if we found a match
bool MatchFound = index >= 0;
if (MatchFound)
{
// Remove the old text
source = source.Remove(index, oldString.Length);
// Add the replacemenet text
source = source.Insert(index, newString);
}
// recurse for multiple instances of the name
if (source.IndexOf(oldString, comp) != -1)
{
source = Replace(source, oldString, newString, comp);
}
return source;
}
}
Here's an extension method taking a StringComparison, using string.IndexOf:
[Pure]
public static string Replace(this string source, string oldValue, string newValue, StringComparison comparisonType)
{
if (source.Length == 0 || oldValue.Length == 0)
return source;
var result = new System.Text.StringBuilder();
int startingPos = 0;
int nextMatch;
while ((nextMatch = source.IndexOf(oldValue, startingPos, comparisonType)) > -1)
{
result.Append(source, startingPos, nextMatch - startingPos);
result.Append(newValue);
startingPos = nextMatch + oldValue.Length;
}
result.Append(source, startingPos, source.Length - startingPos);
return result.ToString();
}
Btw, here's also a similar Contains-method also taking a StringComparison:
[Pure]
public static bool Contains(this string source, string value, StringComparison comparisonType)
{
return source.IndexOf(value, comparisonType) >= 0;
}
Some tests:
[TestFixture]
public class ExternalTests
{
private static string[] TestReplace_args =
{
"ab/B/c/ac",
"HELLO World/Hello/Goodbye/Goodbye World",
"Hello World/world/there!/Hello there!",
"hello WoRlD/world/there!/hello there!",
"///",
"ab///ab",
"/ab/cd/",
"a|b|c|d|e|f/|//abcdef",
"a|b|c|d|e|f|/|/:/a:b:c:d:e:f:",
};
[Test, TestCaseSource("TestReplace_args")]
public void TestReplace(string teststring)
{
var split = teststring.Split("/");
var source = split[0];
var oldValue = split[1];
var newValue = split[2];
var result = split[3];
Assert.That(source.Replace(oldValue, newValue, StringComparison.OrdinalIgnoreCase), Is.EqualTo(result));
}
}
Here is my extension method, which combines Tom Beech's, with the recursiveness of sntbob's, and a cleaner fix to the bug that ksun pointed out.
Code:
public static string Replace(this string source, string oldString,
string newString, StringComparison comparison)
{
int index = source.IndexOf(oldString, comparison);
while (index > -1)
{
source = source.Remove(index, oldString.Length);
source = source.Insert(index, newString);
index = source.IndexOf(oldString, index + newString.Length, comparison);
}
return source;
}
Usage:
string source = "banana";
Console.WriteLine(source.Replace("AN", "banana", StringComparison.OrdinalIgnoreCase));
Result:
bbananabananaa
And, if you still want the recursive nature to be optional:
Code:
public static string Replace(this string source, string oldString,
string newString, StringComparison comparison,
bool recursive = true)
{
int index = source.IndexOf(oldString, comparison);
while (index > -1)
{
source = source.Remove(index, oldString.Length);
source = source.Insert(index, newString);
if (!recursive)
{
return source;
}
index = source.IndexOf(oldString, index + newString.Length, comparison);
}
return source;
}
Usage:
string source = "banana";
Console.WriteLine(source.Replace("AN", "banana", StringComparison.OrdinalIgnoreCase, false));
Result:
bbananaana
Utiltize StringComparison because of its handy OrdinalIgnoreCase
string sentence = "We know it contains 'camel' word.";
string wordToFind = "camel";
string replacementWord = "horse";
int index = sentence.IndexOf(wordToFind , StringComparison.OrdinalIgnoreCase)
// Did we match the word regardless of case
bool match = index >= 0;
// perform the replace on the matched word
if(match) {
sentence = sentence.Remove(index, wordToFind.Length)
sentence = sentence.Insert(index, replacementWord)
}
Sure would be nice if the C# String class had an ignoreCase() method like Java.
You could also use String.IndexOf
http://msdn.microsoft.com/en-us/library/system.string.indexof.aspx
You may get slightly better performance doing it this way than with RegExpressions (I abhor them because they're not intuitive and easy to screw up, although this simple .Net function call abstracts the actual messy RegEx, and doesn't provide much room for error), but that's probably not a concern for you; computers are REALLY fast these days, right? :) The overload for IndexOf that takes a StringComparison object allows you to optionally ignore case, and because IndexOf returns the first occurrence from at a specified position, you'll have to code a loop to process a string having multiple occurrences.
public static string CustomReplace(string srcText, string toFind, string toReplace, bool matchCase, bool replace0nce)
{
StringComparison sc = StringComparison.OrdinalIgnoreCase;
if (matchCase)
sc = StringComparison.Ordinal;
int pos;
while ((pos = srcText.IndexOf(toFind, sc)) > -1)
{
srcText = srcText.Remove(pos, toFind.Length);
srcText = srcText.Insert(pos, toReplace);
if (replace0nce)
break;
}
return srcText;
}
It may not be as efficient as some of the other answers, but I kind of like the CustomReplace function written by sntbob.
However, there is a flaw in it. If the text replacement is recursive it will cause an infinite loop. For example, CustomReplace("I eat bananas!","an","banana",false,false) would cause an infinite loop and the string would continue growing larger.
For example, after the 4th iteration the string would be "I eat bbbbbananaanaanaanaanas!"
If you want to only replace the two instances of "an" inside "banana" then you'll have to take another approach. I modified sntbob's code to account for this case. I admit that it's much more convoluted, but it handles recursive replacements.
public static string CustomReplace(string srcText, string toFind, string toReplace, bool matchCase, bool replaceOnce)
{
StringComparison sc = StringComparison.OrdinalIgnoreCase;
if (matchCase)
sc = StringComparison.Ordinal;
int pos;
int previousProcessedLength = 0;
string alreadyProcessedTxt = "";
string remainingToProcessTxt = srcText;
while ((pos = remainingToProcessTxt.IndexOf(toFind, sc)) > -1)
{
previousProcessedLength = alreadyProcessedTxt.Length;
//Append processed text up until the end of the found string and perform replacement
alreadyProcessedTxt += remainingToProcessTxt.Substring(0, pos + toFind.Length);
alreadyProcessedTxt = alreadyProcessedTxt.Remove(previousProcessedLength + pos, toFind.Length);
alreadyProcessedTxt = alreadyProcessedTxt.Insert(previousProcessedLength + pos, toReplace);
//Remove processed text from remaining
remainingToProcessTxt = remainingToProcessTxt.Substring(pos + toFind.Length);
if (replaceOnce)
break;
}
return alreadyProcessedTxt + remainingToProcessTxt;
}
Why not just import the Microsoft.VisualBasic namespace and use the VB Strings.Replace method?
https://msdn.microsoft.com/en-us/library/microsoft.visualbasic.strings.replace(v=vs.110).aspx
eg
var newString = Strings.Replace(SourceString, FindTextValue, ReplacementTextValue, 1, -1, Constants.vbTextCompare);
vbTextCompare forces a case-insensitive replacement. Job done.
Okay, it's not 'pure' C#, but it gets you to where you want to go with much less complexity and messing around.
Here's one more alternative that uses StringComparison as an extension method. on a StringBuilder object. I've read some articles indicating that a StringBuilder might be a little more efficient with memory than using strings. You can easily alter this to work with strings if that's what you need.
/// <summary>
/// Extension method to find/replace replaces text in a StringBuilder object
/// </summary>
/// <param name="original">Source StringBuilder object</param>
/// <param name="oldString">String to search for</param>
/// <param name="newString">String to replace each occurrance of oldString</param>
/// <param name="stringComparison">String comparison to use</param>
/// <returns>Original Stringbuilder with replacements made</returns>
public static StringBuilder Replace(this StringBuilder original,
string oldString, string newString, StringComparison stringComparison)
{
//If anything is null, or oldString is blank, exit with original value
if ( newString == null || original == null || string.IsNullOrEmpty(oldString))
return original;
//Convert to a string and get starting position using
//IndexOf which allows us to use StringComparison.
int pos = original.ToString().IndexOf(oldString, 0, stringComparison);
//Loop through until we find and replace all matches
while ( pos >= 0 )
{
//Remove the old string and insert the new one.
original.Remove(pos, oldString.Length).Insert(pos, newString);
//Get the next match starting 1 character after last replacement (to avoid a possible infinite loop)
pos = original.ToString().IndexOf(oldString, pos + newString.Length + 1, stringComparison);
}
return original;
}
I want to use regular expression same way as string.Format. I will explain
I have:
string pattern = "^(?<PREFIX>abc_)(?<ID>[0-9])+(?<POSTFIX>_def)$";
string input = "abc_123_def";
Regex regex = new Regex(pattern, RegexOptions.IgnoreCase);
string replacement = "456";
Console.WriteLine(regex.Replace(input, string.Format("${{PREFIX}}{0}${{POSTFIX}}", replacement)));
This works, but i must provide "input" to regex.Replace. I do not want that. I want to use pattern for matching but also for creating strings same way as with string format, replacing named group "ID" with value. Is that possible?
I'm looking for something like:
string pattern = "^(?<PREFIX>abc_)(?<ID>[0-9])+(?<POSTFIX>_def)$";
string result = ReplaceWithFormat(pattern, "ID", 999);
Result will contain "abc_999_def". How to accomplish this?
Yes, it is possible:
public static class RegexExtensions
{
public static string Replace(this string input, Regex regex, string groupName, string replacement)
{
return regex.Replace(input, m =>
{
return ReplaceNamedGroup(input, groupName, replacement, m);
});
}
private static string ReplaceNamedGroup(string input, string groupName, string replacement, Match m)
{
string capture = m.Value;
capture = capture.Remove(m.Groups[groupName].Index - m.Index, m.Groups[groupName].Length);
capture = capture.Insert(m.Groups[groupName].Index - m.Index, replacement);
return capture;
}
}
Usage:
Regex regex = new Regex("^(?<PREFIX>abc_)(?<ID>[0-9]+)(?<POSTFIX>_def)$");
string oldValue = "abc_123_def";
var result = oldValue.Replace(regex, "ID", "456");
Result is: abc_456_def
No, it's not possible to use a regular expression without providing input. It has to have something to work with, the pattern can not add any data to the result, everything has to come from the input or the replacement.
Intead of using String.Format, you can use a look behind and a look ahead to specify the part between "abc_" and "_def", and replace it:
string result = Regex.Replace(input, #"(?<=abc_)\d+(?=_def)", "999");
There was a problem in user1817787 answer and I had to make a modification to the ReplaceNamedGroup function as follows.
private static string ReplaceNamedGroup(string input, string groupName, string replacement, Match m)
{
string capture = m.Value;
capture = capture.Remove(m.Groups[groupName].Index - m.Index, m.Groups[groupName].Length);
capture = capture.Insert(m.Groups[groupName].Index - m.Index, replacement);
return capture;
}
Another edited version of the original method by #user1817787, this one supports multiple instances of the named group (also includes similar fix to the one #Justin posted (returns result using {match.Index, match.Length} instead of {0, input.Length})):
public static string ReplaceNamedGroup(
string input, string groupName, string replacement, Match match)
{
var sb = new StringBuilder(input);
var matchStart = match.Index;
var matchLength = match.Length;
var captures = match.Groups[groupName].Captures.OfType<Capture>()
.OrderByDescending(c => c.Index);
foreach (var capt in captures)
{
if (capt == null)
continue;
matchLength += replacement.Length - capt.Length;
sb.Remove(capt.Index, capt.Length);
sb.Insert(capt.Index, replacement);
}
var end = matchStart + matchLength;
sb.Remove(end, sb.Length - end);
sb.Remove(0, matchStart);
return sb.ToString();
}
I shortened ReplaceNamedGroup, still supporting multiple captures.
private static string ReplaceNamedGroup(string input, string groupName, string replacement, Match m)
{
string result = m.Value;
foreach (Capture cap in m.Groups[groupName]?.Captures)
{
result = result.Remove(cap.Index - m.Index, cap.Length);
result = result.Insert(cap.Index - m.Index, replacement);
}
return result;
}
The simple solution is to refer to the matched groups in replacement. So the Prefix is $1 and Postfix is $3.
I've haven't tested the code below but should work similar to a regEx I've written recently:
string pattern = "^(?<PREFIX>abc_)(?<ID>[0-9])+(?<POSTFIX>_def)$";
string input = "abc_123_def";
Regex regex = new Regex(pattern, RegexOptions.IgnoreCase);
string replacement = String.Format("$1{0}$3", "456");
Console.WriteLine(regex.Replace(input, string.Format("${{PREFIX}}{0}${{POSTFIX}}", replacement)));
In case this helps anyone, I enhanced the answer with the ability to replace multiple named capture groups in one go, which this answer helped massively to achieve.
public static class RegexExtensions
{
public static string Replace(this string input, Regex regex, Dictionary<string, string> captureGroupReplacements)
{
string temp = input;
foreach (var key in captureGroupReplacements.Keys)
{
temp = regex.Replace(temp, m =>
{
return ReplaceNamedGroup(key, captureGroupReplacements[key], m);
});
}
return temp;
}
private static string ReplaceNamedGroup(string groupName, string replacement, Match m)
{
string capture = m.Value;
capture = capture.Remove(m.Groups[groupName].Index - m.Index, m.Groups[groupName].Length);
capture = capture.Insert(m.Groups[groupName].Index - m.Index, replacement);
return capture;
}
}
Usage:
var regex = new Regex(#"C={BasePath:""(?<basePath>[^\""].*)"",ResultHeadersPath:""ResultHeaders"",CORS:(?<cors>true|false)");
content = content.Replace(regex, new Dictionary<string, string>
{
{ "basePath", "www.google.com" },
{ "cors", "false" }
};
All credit should go to user1817787 for this one.
You should check the documentation about RegEx replace here
I created this to replace a named group. I cannot use solution that loop on all groups name because I have case where not all expression is grouped.
public static string ReplaceNamedGroup(this Regex regex, string input, string namedGroup, string value)
{
var replacement = Regex.Replace(regex.ToString(),
#"((?<GroupPrefix>\(\?)\<(?<GroupName>\w*)\>(?<Eval>.[^\)]+)(?<GroupPostfix>\)))",
#"${${GroupName}}").TrimStart('^').TrimEnd('$');
replacement = replacement.Replace("${" + namedGroup + "}", value);
return Regex.Replace(input, regex.ToString(), replacement);
}
Is there a fast way (without having to explicitly looping through each character in a string) and either stripping or keeping it. In Visual FoxPro, there is a function CHRTRAN() that does it great. Its on a 1:1 character replacement, but if no character in the alternate position, its stripped from the final string. Ex
CHRTRAN( "This will be a test", "it", "X" )
will return
"ThXs wXll be a es"
Notice the original "i" is converted to "X", and lower case "t" is stripped out.
I looked at the replace for similar intent, but did not see an option to replace with nothing.
I'm looking to make some generic routines to validate multiple origins of data that have different types of input restrictions. Some of the data may be coming from external sources, so its not just textbox entry validation I need to test for.
Thanks
All you need is a couple of calls to String.Replace().
string s = "This will be a test";
s = s.Replace("i", "X");
s = s.Replace("t", "");
Note that Replace() returns a new string. It does not alter the string itself.
Does this what you want?
"This will be a test".Replace("i", "X").Replace("t", String.Empty)
Here is a simple implementation of the CHRTRAN function - it does not work if the string contains \0 and is quite messy. You could write a nicer one using loops, but I just wanted to try it using LINQ.
public static String ChrTran(String input, String source, String destination)
{
return source.Aggregate(
input,
(current, symbol) => current.Replace(
symbol,
destination.ElementAtOrDefault(source.IndexOf(symbol))),
preResult => preResult.Replace("\0", String.Empty));
}
And the you can use it.
// Returns "ThXs wXll be a es"
String output = ChrTran("This will be a test", "it", "X");
Just to have a clean solution - the same without LINQ and working for the \0 cases, too, and it is almost in place because of using a StringBuilder but won't modify the input, of course.
public static String ChrTran(String input, String source, String destination)
{
StringBuilder result = new StringBuilder(input);
Int32 minLength = Math.Min(source.Length, destination.Length);
for (Int32 i = 0; i < minLength; i++)
{
result.Replace(source[i], destination[i]);
}
for (Int32 i = minLength; i < searchPattern.Length; i++)
{
result.Replace(source[i].ToString(), String.Empty);
}
return result.ToString();
}
Null reference handling is missing.
Inspired by tvanfosson's solution, I gave LINQ a second shot.
public static String ChrTran(String input, String source, String destination)
{
return new String(input.
Where(symbol =>
!source.Contains(symbol) ||
source.IndexOf(symbol) < destination.Length).
Select(symbol =>
source.Contains(symbol)
? destination[source.IndexOf(symbol)]
: symbol).
ToArray());
}
Here was my final function and works perfectly as expected.
public static String ChrTran(String ToBeCleaned,
String ChangeThese,
String IntoThese)
{
String CurRepl = String.Empty;
for (int lnI = 0; lnI < ChangeThese.Length; lnI++)
{
if (lnI < IntoThese.Length)
CurRepl = IntoThese.Substring(lnI, 1);
else
CurRepl = String.Empty;
ToBeCleaned = ToBeCleaned.Replace(ChangeThese.Substring(lnI, 1), CurRepl);
}
return ToBeCleaned;
}
This is a case where I think using LINQ overcomplicates the matter. This is simple and to the point:
private static string Translate(string input, string from, string to)
{
StringBuilder sb = new StringBuilder();
foreach (char ch in input)
{
int i = from.IndexOf(ch);
if (from.IndexOf(ch) < 0)
{
sb.Append(ch);
}
else
{
if (i >= 0 && i < to.Length)
{
sb.Append(to[i]);
}
}
}
return sb.ToString();
}
To "replace with nothing", just replace with an empty string. This will give you:
String str = "This will be a test";
str = str.Replace("i", "X");
str = str.Replace("t","");
A more general version as a string extension. Like the others this does not do a translation in place since strings are immutable in C#, but instead returns a new string with the replacements as specified.
public static class StringExtensions
{
public static string Translate( this string source, string from, string to )
{
if (string.IsNullOrEmpty( source ) || string.IsNullOrEmpty( from ))
{
return source;
}
return string.Join( "", source.ToCharArray()
.Select( c => Translate( c, from, to ) )
.Where( c => c != null )
.ToArray() );
}
private static string Translate( char c, string from, string to )
{
int i = from != null ? from.IndexOf( c ) : -1;
if (i >= 0)
{
return (to != null && to.Length > i)
? to[i].ToString()
: null;
}
else
{
return c.ToString();
}
}
}
The input string is something like this:
LineA: 50
LineB: 120
LineA: 12
LineB: 53
I would like to replace the LineB values with a result of MultiplyCalculatorMethod(LineAValue), where LineAValue is the value of the line above LineB and MultiplyCalculatorMethod is my other, complicated C# method.
In semi-code, I would like to do something like this:
int MultiplyCalculatorMethod(int value)
{
return 2 * Math.Max(3,value);
}
string ReplaceValues(string Input)
{
Matches mat = Regex.Match(LineA:input_value\r\nLineB:output_value)
foreach (Match m in mat)
{
m.output_value = MultiplyCalculatorMethod(m.input_value)
}
return m.OutputText;
}
Example:
string Text = "LineA:5\r\nLineB:2\r\nLineA:2\r\nLineB:7";
string Result = ReplaceValues(Text);
//Result = "LineA:5\r\nLineB:10\r\nLineA:2\r\nLineB:6";
I wrote a Regex.Match to match LineA: value\r\nLineB: value and get these values in groups. But when I use Regex.Replace, I can only provide a "static" result that is combining groups from the match, but I can not use C# methods there.
So my questions is how to Regex.Replace where Result is a result of C# method where input is LineA value.
You can use a MatchEvaluator like this:
public static class Program
{
public static void Main()
{
string input = "LineA:5\r\nLineB:2\r\nLineA:2\r\nLineB:7";
string output = Regex.Replace(input, #"LineA:(?<input_value>\d+)\r\nLineB:\d+", new MatchEvaluator(MatchEvaluator));
Console.WriteLine(output);
}
private static string MatchEvaluator(Match m)
{
int inputValue = Convert.ToInt32(m.Groups["input_value"].Value);
int outputValue = MultiplyCalculatorMethod(inputValue);
return string.Format("LineA:{0}\r\nLineB:{1}", inputValue, outputValue);
}
static int MultiplyCalculatorMethod(int value)
{
return 2 * Math.Max(3, value);
}
}
Try using following Replace overload.
public static string Replace( string input, string pattern, MatchEvaluator evaluator);
MatchEvaluator has access to Match contents and can call any other methods to return the replacement string.