From a port I receive lines one by one as follows:
"value: 100.00 % "
"value: 100.00 % "
" value: 01.12 % "
ect.
Now in my C# code I need to extract the decimal numbers as AA.BB. But as you see a number with percentage sign AA.BB % can appear anywhere.
How can this be done with or without using regex?
CodePudding user response:
There are unspecified details about how you'd like this to behave. Here's one approach:
public static class InputLineParser
{
public static decimal? ExtractValue(string input)
{
var segments = input.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);
if (segments.Length < 2) return default;
if (decimal.TryParse(segments[1], out decimal result))
{
return result;
}
return default;
}
}
This assumes some consistency within your inputs. If there are two values separated by spaces (excluding leading and trailing) then the second is parsed as a decimal. If there aren't two values or the second one can't be parsed it returns null. You could choose to add a condition that the first "value" must be the literal string "value:".
This is where unit tests are helpful. It's easy to write a few tests that take some inputs and assert that the expected values are returned. So if you want a different behavior but still want to be sure it works as expected in each case, just add or modify the test data.
[TestClass]
public class InputLineParserTests
{
[DataTestMethod]
[DataRow("value: 100.00 % ", 100)]
[DataRow("value: 100.00 % ", 100)]
[DataRow(" value: 01.12 % ", 1.12)]
public void ExtractValue_Returns_Expected_Value(string input, double expected)
{
decimal? actual = InputLineParser.ExtractValue(input);
Assert.IsTrue(actual.HasValue);
// The conversion from double to decimal is because the test
// runner didn't like doing the conversion implicitly and
// an attribute can't specify that the value is a decimal.
Assert.AreEqual((decimal)expected, actual.Value);
}
[DataTestMethod]
[DataRow("value: ")]
[DataRow("value: abc ")]
[DataRow(" ")]
public void ExtractValue_Returns_Null_When_No_Value_Found(string input)
{
decimal? actual = InputLineParser.ExtractValue(input);
Assert.IsFalse(actual.HasValue);
}
}
CodePudding user response:
I would use regex to solve this problem (regex c# documentation: https://www.c-sharpcorner.com/article/c-sharp-regex-examples/)
//some example string
string text = "My text has 25% chance of being right, and 75 % chance of being wrong! That's not great 7";
Console.WriteLine(text); // show text to console
// Create a pattern for number([0-9] ) and maybe space([ ]*) and '%' char or nothing ([%]|)
string pattern = @"\b[0-9] [ ]*([%]|)";
// Create a Regex with this pattern
Regex rg = new Regex(pattern);
// match the pattern (form an array of all found expressions)
MatchCollection matchedNumbers = rg.Matches(text);
// for each found member print it out
for (int count = 0; count < matchedNumbers.Count; count )
Console.WriteLine(matchedNumbers[count].Value);
NOTE: they will be strings not numbers.
print should be:
25%
75 %
7
note this will not catch numbers with decimal points, for this you must make it a more complex pattern like this: string complex_pattern = @"\b[0-9] (([.][0-9] )|)[ ]*([%]|)";
CodePudding user response:
try this
var numberValue = new string( value.Where(ch=> (Char.IsDigit(ch) || ch=='.')).ToArray());
output
100.00
100.00
01.12
CodePudding user response:
You can try regular expressions:
using System.Text.RegularExpressions;
...
decimal result = decimal.Parse(Regex.Match(source, @"[0-9]{2,3}\.[0-9]{2}").Value);
You can extract a method, e.g.
public static bool TryExtract(string source, out decimal result) => double
.TryParse(Regex.Match(source, @"[0-9]{2,3}\.[0-9]{2}").Value, out result);
which you can use as
if (TryExtract(source, out var result)) {
// extracted in result
}
else {
// decimal is not found
}