Generic extraction of multiple decimals using Regular expressions - c#

Hi how do you extract multiple decimals with different number of decimal places from a string?
I'm looking to find a generic way to extract 3 numbers out the following strings.
e.g
CC77X1722X12 => 77,1722,12
PC77.5X10102X12.5 => 77.5, 10102, 12.5
XP60.25X0.333X12 => 60.25, 0.333, 12
The three numbers are always separated by 'X', and the string always starts with 2 characters
Thanks!

Since you have such a specific pattern, you don't even need to use regular expressions. Because the first two characters can be ignored and all the numbers are separated by 'X' characters, this C# code should do the trick (with appropriate error handling added, of course)
public IEnumerable<decimal> ExtractNumbers(string s)
{ // For s = "CC77X1722X12"
string[] nums = s.Substring(2).Split('X'); // nums = ["77", "1722", "12"];
return nums.Select(num => decimal.Parse(num)); // returns [77, 1722, 12]
}
For production code, though, I would recommend decimal.TryParse over decimal.Parse. To use that method, you could write something like
public IEnumerable<decimal> ExtractNumbers(string s)
{
string[] nums = s.Substring(2).Split('X');
return nums
.Select(num => {
decimal d;
if (decimal.Parse(num, out d))
return new {Number = d, Succeeded = true};
return new {Number = 0, Succeeded = false};
})
.Filter(result => result.Succeeded)
.Select(result => result.Number);
}

Related

Selecting max number from a list of string in LINQ?

trying to select the max digit from a list of strings:
int maxDigit = this.myList.Where(x=> x.Name.Any(Char.IsDigit))
.Select(x => int.Parse(x.Name)).DefaultIfEmpty(0).Max();
It is int.Parse(x.Name) which is causing an exception as this is returning the entire name string e.g. 'myValue99' which of course cannot be parsed to an int. I just want to return 99. I don't know where the digits will be in the string therefore cannot for example take the last two.
I need the DefaultIfEmpty for cases where the string does not contain a number.
Assuming you want the max number and not the max digit, all you need is a function to convert "stuff99" to 99. Then the Linq part becomes child's play:
int maxNumber = myList.Max(ExtractNumberFromText);
or, to be closer to your specs:
int maxNumber = myList
.Select(ExtractNumberFromText)
.DefaultIfEmpty(0)
.Max();
#Codecaster already pointed to a few applicable answers on this site for the second part. I adopted a simple one. No error checking.
// the specs: Only ever going to be my stuff1, stuff99
int ExtractNumberFromText(string text)
{
Match m = Regex.Match(text, #"\d*");
return int.Parse(m.Groups[0].Value); // exception for "abc"
// int.Parse("0" + m.Groups[0].Value); // use this for default to 0
}
you should only select and parse the Digit characters out of your string
int maxDigit = this.myList.Where(x => x.Name.Any(Char.IsDigit))
.Select(x => int.Parse(new string(x.Name.Where(Char.IsDigit).ToArray())))
.DefaultIfEmpty(0).Max();
Assuming the input can contain the following categories:
nulls
Empty strings
Strings with only alphabetical characters
Strings with mixed alphabetical and numerical characters
Strings with only numerical characters
You want to introduce a method that extracts the number, if any, or returns a meaningful value if not:
private static int? ParseStringContainingNumber(string input)
{
if (String.IsNullOrEmpty(input))
{
return null;
}
var numbersInInput = new String(input.Where(Char.IsDigit).ToArray());
if (String.IsNullOrEmpty(numbersInInput))
{
return null;
}
int output;
if (!Int32.TryParse(numbersInInput, out output))
{
return null;
}
return output;
}
Note that not all characters for which Char.IsDigit returns true can be parsed by Int32.Parse(), hence the TryParse.
Then you can feed your list to this method:
var parsedInts = testData.Select(ParseStringContainingNumber)
.Where(i => i != null)
.ToList();
And do whatever you want with the parsedInts list, like calling IEnumerable<T>.Max() on it.
With the following test data:
var testData = new List<string>
{
"۱‎", // Eastern Arabic one, of which Char.IsDigit returns true.
"123",
"abc456",
null,
"789xyz",
"foo",
"9bar9"
};
This returns:
123
456
789
99
Especially note the latest case.
To find the max digit (not number) in each string:
static void Main(string[] args)
{
List<string> strList = new List<string>() { "Value99", "46Text" };
List<int> resultList = new List<int>();
foreach (var str in strList)
{
char[] resultString = Regex.Match(str, #"\d+").Value.ToCharArray();
int maxInt = resultString.Select(s => Int32.Parse(s.ToString())).Max();
resultList.Add(maxInt);
}
}
It can be simple using Regex.
You stated 99, so you need to span more than one digit:
var maxNumber = myTestList.SelectMany(x => getAllNumnbersFromString(x.Name)).DefaultIfEmpty(0).Max();
static List<int> getAllNumnbersFromString(string str)
{
List<int> results = new List<int>();
var matchesCollection = Regex.Matches(str, "[0-9]+");
foreach (var numberMatch in matchesCollection)
{
results.Add(Convert.ToInt32(numberMatch.ToString()));
}
return results;
}
One digit only check:
int maxNumber = myTestList.SelectMany(x => x.Name.ToCharArray().ToList())
.Select(x => Char.IsDigit(x) ? (int)Char.GetNumericValue(x) : 0)
.DefaultIfEmpty(0).Max();
Probably there's a slicker way to do this but I would just do:
int tmp = 0;
int maxDigit = this.myList.Where(x=> x.Name.Any(Char.IsDigit))
.Select(x =>
(int.TryParse(x.Name,out tmp ) ? int.Parse(x.Name) : 0 ) ).Max();
You have to remember that Parse will error out if it can't parse the value but TryParse will just give you false.

How to read Two numbers in c# [duplicate]

This question already has answers here:
reading two integers in one line using C#
(12 answers)
Closed 8 years ago.
INPUT
67 89 (in single line)
I have to input two numbers from console , and store in two different integers variable .
HOw to do it.
This will read a line from the console, split the string, parse the integers, and output a list. You can then take each number from the list as needed.
Console.ReadLine().Split().Select(s => int.Parse(s)).ToList()
If there will always be two numbers you can do it as follows:
var integers = Console.ReadLine().Split().Select(s => int.Parse(s)).ToArray();
int first = integers[0];
int second = integers[1];
Areas for improvement:
You might want to use TryParse instead of Parse and output a friendly error message if the input does not parse
If you require exactly 2 numbers (no more, no less) you might want to check the length of integers and output a friendly error message if <> 2
TryParse() example as requested:
var numbers = new List<int>();
foreach (string s in Console.ReadLine().Split())
{
if (int.TryParse(s, out int number))
numbers.Add(number);
else
Console.WriteLine($"{s} is not an integer");
}
using System;
public class Program
{
static void Main(string[] args)
{
var numbers = Console.ReadLine();
var numberList = numbers.Split(' ');
var number1 = Convert.ToInt32(numberList[0]);
var number2 = Convert.ToInt32(numberList[1]);
Console.WriteLine(number1 + number2);
Console.ReadKey();
}
}
If you executing from other program the you need to read from the args
var result = Console.ReadLine().Split(new [] { ' '});
Something along those lines, top of my head.
See the documentation for Console.ReadLine() and String.Split()
Using Linq you can then project into an int array:
var result = Console.ReadLine()
.Split(new[] { ' ' }) //Explicit separator char(s)
.Select(i => int.Parse(i))
.ToArray();
And even a bit terser:
var result = Console.ReadLine()
.Split() //Assuming whitespace as separator
.Select(i => int.Parse(i))
.ToArray();
Result is now an array of ints.

Use Regex in this Linq query?

I am using this Linq to find the highest int in a list, so that I can increment it and add it to the end of the next string:
var CableNumber = CList.Select(v => int.Parse(v.CableNumber.Substring(n))).Max();
However, because the strings aren't a fixed length, I was thinking of inserting a Regex.Match in there, possibly something like:
n = Regex.Match(CableNumber, #"\d{3}", RegexOptions.RightToLeft);
To specify; the only format the input strings follow is that it will always have a 3 digit number on the end, possibly followed by a single letter. Some examples:
CP1-P-CP2-001 (001)
MOT1PSP2023A (023)
TKSP3-C-FLT2-234-A (234)
How could I implement this? Is there a better way?
The following uses the regex pattern inside the linq query:
string[] strings = { "CP1-P-CP2-001 (001)","MOT1PSP2023A (023)", "TKSP3-C-FLT2-234-A (234)",
"InvalidString" };
int? maxValue = strings.Max(x =>
{
var match = Regex.Match(x, #"\d{3}(?=\D*$)");
return match.Success ? (int?) int.Parse(match.Value) : null;
});
The int? is so we can bypass any string.Empty coming back from an invalid match and only parse valid matches. If none matched, will return null.
How about this?
var CableNumber = CList.Select(v =>
int.Parse(v.CableNumber.Substring(v.CableNumber.Length - 3))).Max();
Or to be safe (to prevent string that is less than 3 characters).
var CableNumber = CList.Select(v =>
int.Parse(("000" + v.CableNumber).Substring(("000" + v.CableNumber).Length - 3))).Max();
update
Use LastIndexOfAny
var CableNumber = CList.Select(v =>
int.Parse(v.CableNumber.Substring(v.CableNumber
.LastIndexOfAny("0123456789".ToCharArray()) - 2, 3))).Max();
You can use the next regex to get last 3 digits in each string:
(\d{3})\D*$

What is the best way to check if a string can be parsed into an int array?

I need to determine if a string can be parsed into an array of int. The string MAY be in the format
"124,456,789,0"
In case which can it can converted thus:
int[] Ids = SearchTerm.Split(',').Select(int.Parse).ToArray();
However the string may also be something like:
"Here is a string, it is very nice."
In which case the parsing fails.
The logic currently branches in two directions based on whether the string contains a comma character (assuming that only the array-like strings will contain this character) but this logic is now flawed and comma characters are now appearing in other strings.
I could put a Try..Catch around it but I am generally adverse to controlling logic flow by exceptions.
Is there an easy way to do this?
I could put a Try..Catch around it but I am generally adverse to controlling logic flow by exceptions
Good attitude. If you can avoid the exception, do so.
A number of answers have suggested
int myint;
bool parseFailed = SearchTerm.Split(',')
.Any( s => !int.TryParse(s, out myint));
Which is not bad, but not great either. I would be inclined to first, write a better helper method:
static class Extensions
{
public static int? TryParseAsInteger(this string s)
{
int j;
bool success = int.TryParse(s, out j);
if (success)
return j;
else
return null;
}
}
Now you can say:
bool parseFailed = SearchTerm.Split(',')
.Any( s => s.TryParseAsInteger() == null);
But I assume that what you really want is the parsed state if it can succeed, rather than just answering the question "would a parse succeed?" With this helper method you can say:
List<int?> parse = SearchTerm.Split(',')
.Select( s => s.TryParseAsInteger() )
.ToList();
And now if the list contains any nulls, you know that it was bad; if it doesn't contain any nulls then you have the results you wanted:
int[] results = parse.Contains(null) ? null : parse.Select(x=>x.Value).ToArray();
int myint;
bool parseFailed = SearchTerm.Split(',')
.Any( s => !int.TryParse(s, out myint));
You can use multiline lambda expression to get int.TryParse for every Split method result:
var input = "124,456,789,0";
var parts = input.Split(new [] {","}, StringSplitOptions.RemoveEmptyEntries);
var numbers
= parts.Select(x =>
{
int v;
if (!int.TryParse(x, out v))
return (int?)null;
return (int?)v;
}).ToList();
if (numbers.Any(x => !x.HasValue))
Console.WriteLine("string cannot be parsed as int[]");
else
Console.WriteLine("OK");
It will not only check if value can be parsed to int, but also return the value if it can, so you don't have to do the parsing twice.
you can use RegEx to determine if the string match your pattern
something like this
string st = "124,456,789,0";
string pattS = #"[0-9](?:\d{0,2})";
Regex regex = new Regex(pattS);
var res = regex.Matches(st);
foreach (var re in res)
{
//your code here
}
tested on rubular.com here
How about,
int dummy;
var parsable = SearchTerm.Split(',').All(s => int.TryParse(s, out dummy));
but if you are doing that you might as well just catch the exception
Why dont you first remove the characters from the string and use
bool res = int.TryParse(text1, out num1);
Example below has no limit.
The BigInteger type is an immutable type that represents an arbitrarily large integer whose value in theory has no upper or lower bounds.
BigInteger MSDN
string test = "20,100,100,100,100,100,100";
test = test.Replace(",", "");
BigInteger num1 = 0;
bool res = BigInteger.TryParse(test, out num1);

Parse string and return only the information between bracket symbols. C# Winforms

I would like to parse a string to return only a value that is in between bracket symbols, such as [10.2%]. Then I would need to strip the "%" symbol and convert the decimal to a rounded up/down integer. So, [10.2%] would end up being 10. And, [11.8%] would end up being 12.
Hopefully I have provided sufficient information.
Math.Round(
double.Parse(
"[11.8%]".Split(new [] {"[", "]", "%"},
StringSplitOptions.RemoveEmptyEntries)[0]))
Why not use Regex?
In this example, I am assuming that your value inside the brackets always are a double with decimals.
string WithBrackets = "[11.8%]";
string AsDouble = Regex.Match(WithBrackets, "\d{1,9}\.\d{1,9}").value;
int Out = Math.Round(Convert.ToDouble(AsDouble.replace(".", ","));
var s = "[10.2%]";
var numberString = s.Split(new char[] {'[',']','%'},StringSplitOptions.RemoveEmptyEntries).First();
var number = Math.Round(Covnert.ToDouble(numberString));
If you can ensure that the content between the brackets is of the form <decimal>%, then this little function will return the value between the fist set of brackets. If there are more than one values you need to extract then you will need to modify it somewhat.
public decimal getProp(string str)
{
int obIndex = str.IndexOf("["); // get the index of the open bracket
int cbIndex = str.IndexOf("]"); // get the index of the close bracket
decimal d = decimal.Parse(str.Substring(obIndex + 1, cbIndex - obIndex - 2)); // this extracts the numerical part and converts it to a decimal (assumes a % before the ])
return Math.Round(d); // return the number rounded to the nearest integer
}
For example getProp("I like cookies [66.7%]") gives the Decimal number 67
Use regular expressions (Regex) to find the required words within one bracket.
This is the code you need:
Use an foreach loop to remove the % and convert to int.
List<int> myValues = new List<int>();
foreach(string s in Regex.Match(MYTEXT, #"\[(?<tag>[^\]]*)\]")){
s = s.TrimEnd('%');
myValues.Add(Math.Round(Convert.ToDouble(s)));
}

Categories