Reading a string from one char to another - c#

I have the following string:
FB:77:CB:0B:EC:09{W: 0,623413, X: 0,015374, Y: 0,005306, Z: -0,781723}
I want to read out the values of W,X,Y,Z as a float/decimal. The values are not always the same length.
How can I read this string from one character to another without using relative positions?

I'd suggest matching the "inner" part with a regular expression, but removing the "outer" part manually first - just to keep the regex as simple as possible.
Here's a complete example, with a method that returns the result as a Dictionary<string, string>. It's not clear how you'd then want to convert the sample values you've given (e.g. "0,623413") into integers, but I'd treat that as a separate task from the initial parsing.
I'm assuming that it's fine to strip all trailing commas from values:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
class Test
{
static void Main()
{
string input = "FB:77:CB:0B:EC:09{W: 0,623413, X: 0,015374, Y: 0,005306, Z: -0,781723}";
var parsed = Parse(input);
foreach (var entry in parsed)
{
Console.WriteLine($"Key = '{entry.Key}', Value = '{entry.Value}'");
}
}
static readonly Regex regex = new Regex(#"(?<key>[A-Z]+): (?<value>[-\d,]+)");
static IDictionary<string, string> Parse(string input)
{
int openBrace = input.IndexOf('{');
if (openBrace == -1)
{
throw new ArgumentException("Expected input to contain a {");
}
if (!input.EndsWith("}"))
{
throw new ArgumentException("Expected input to end with }");
}
string inner = input.Substring(openBrace + 1, input.Length - openBrace - 2);
var matches = regex.Matches(inner);
return matches.Cast<Match>()
.ToDictionary(match => match.Groups["key"].Value,
match => match.Groups["value"].Value.TrimEnd(','));
}
}
Output:
Key = 'W', Value = '0,623413'
Key = 'X', Value = '0,015374'
Key = 'Y', Value = '0,005306'
Key = 'Z', Value = '-0,781723'
Converting those values to integers may be as simple as removing the commas, trimming leading zeroes, and then using int.Parse - but it really depends on what you want the results to be.

To answer your question, this method will do:
int GetIntValue(string input, char prefix)
{
return int.Parse(input.Substring(input.IndexOf($"{prefix}: ") + 3, 1));
}
This will, however, return 0 for all of your sample input. The reason why we only parse the zero is that they'll be ignored by the int parser anyway.
If, as I suspect, you don't want an integer but instead the full number, use something like this:
decimal GetValue(string input, char prefix)
{
return decimal.Parse(input.Substring(input.IndexOf($"{prefix}: ") + 3).Split(new[] { ", ", "}" }, StringSplitOptions.None).First());
}
Feel free to replace decimal with whatever you would like.
Call it like this:
var input = "FB:77:CB:0B:EC:09{W: 0,623413, X: 0,015374, Y: 0,005306, Z: -0,781723}";
var W = GetValue(input, 'W'); // 0.623413
var X = GetValue(input, 'X'); // 0.015374
var Y = GetValue(input, 'Y'); // 0.005306
var Z = GetValue(input, 'Z'); // -0.781723
What this does is to identify the location of the prefix, and then parse a substring from the beginning of the following number until the delimiter (, or }) has been reached.

static void Main(string[] args) {
string str = "FB:77:CB:0B:EC:09{W: 0,623413, X: 0,015374, Y: 0,005306, Z: -0,781723}";
char[] delims = { ':', ' ' };
var parsed = Parse(str, delims);
foreach (var p in parsed) {
Console.WriteLine($"{p.Key} : {p.Value}");
}
}
static Dictionary<string, double> Parse(string input, char[] delims) {
int first = input.IndexOf('{') + 1;
int second = input.IndexOf('}');
string str2 = input.Substring(first, second - first);
string[] strArray = str2.Split(delims, StringSplitOptions.RemoveEmptyEntries);
Dictionary<string, double> pairs = new Dictionary<string, double>();
for (int i = 1; i < strArray.Length; i++) {
if (double.TryParse(strArray[i].TrimEnd(','), out double result)) {
pairs.Add(strArray[i - 1], result);
}
i++;
}
return pairs;
}

Here is another Regex example that parses number to decimal and includes plus and negative sign.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Globalization;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string input = "FB:77:CB:0B:EC:09{W: 0,623413, X: 0,015374, Y: 0,005306, Z: -0,781723}";
string pattern = #"W:\s*(?'W'[-+]?\d+,\d+),\s*X:\s*(?'X'[-+]?\d+,\d+),\s*Y:\s*(?'Y'[-+]?\d+,\d+),\s*Z:\s*(?'Z'[-+]?\d+,\d+)";
CultureInfo info = new CultureInfo("en");
info.NumberFormat.NumberDecimalSeparator = ",";
Match match = Regex.Match(input, pattern);
decimal W = decimal.Parse(match.Groups["W"].Value, info);
decimal X = decimal.Parse(match.Groups["X"].Value, info);
decimal Y = decimal.Parse(match.Groups["Y"].Value, info);
decimal Z = decimal.Parse(match.Groups["Z"].Value, info);
}
}
}

Related

Method that takes a message and index, creates a substring using the index

Problem: I want to write a method that takes a message/index pair like this:
("Hello, I am *Name1, how are you doing *Name2?", 2)
The index refers to the asterisk delimited name in the message. So if the index is 1, it should refer to *Name1, if it's 2 it should refer to *Name2.
The method should return just the name with the asterisk (*Name2).
I have attempted to play around with substrings, taking the first delimited * and ending when we reach a character that isn't a letter, number, underscore or hyphen, but the logic just isn't setting in.
I know this is similar to a few problems on SO but I can't find anything this specific. Any help is appreciated.
This is what's left of my very vague attempt so far. Based on this thread:
public string GetIndexedNames(string message, int index)
{
int strStart = message.IndexOf("#") + "#".Length;
int strEnd = message.LastIndexOf(" ");
String result = message.Substring(strStart, strEnd - strStart);
}
If you want to do it the old school way, then something like:
public static void Main(string[] args)
{
string message = "Hello, I am *Name1, how are you doing *Name2?";
string name1 = GetIndexedNames(message, "*", 1);
string name2 = GetIndexedNames(message, "*", 2);
Console.WriteLine(message);
Console.WriteLine(name1);
Console.WriteLine(name2);
Console.ReadLine();
}
public static string GetIndexedNames(string message, string singleCharDelimiter, int index)
{
string valid = "abcdefghijlmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-";
string[] parts = message.Split(singleCharDelimiter.ToArray());
if (parts.Length >= index)
{
StringBuilder sb = new StringBuilder();
for(int i = 0; i < parts[index].Length; i++)
{
string character = parts[index].Substring(i, 1);
if (valid.Contains(character))
{
sb.Append(character);
}
else
{
return sb.ToString();
}
}
return sb.ToString();
}
return "";
}
You can try using regular expressions to match the names. Assuming that name is a sequence of word characters (letters or digits):
using System.Linq;
using System.Text.RegularExpressions;
...
// Either name with asterisk *Name or null
// index is 1-based
private static ObtainName(string source, int index) => Regex
.Matches(source, #"\*\w+")
.Cast<Match>()
.Select(match => match.Value)
.Distinct() // in case the same name repeats several times
.ElementAtOrDefault(index - 1);
Demo:
string name = ObtainName(
"Hello, I am *Name1, how are you doing *Name2?", 2);
Console.Write(name);
Outcome:
*Name2
Perhaps not the most elegant solution, but if you want to use IndexOf, use a loop:
public static string GetIndexedNames(string message, int index, char marker='*')
{
int lastFound = 0;
for (int i = 0; i < index; i++) {
lastFound = message.IndexOf(marker, lastFound+1);
if (lastFound == -1) return null;
}
var space = message.IndexOf(' ', lastFound);
return space == -1 ? message.Substring(lastFound) : message.Substring(lastFound, space - lastFound);
}

Breaking Format and String provided into a list of string array

I have a format( data Type - string ) say of the form {1}.{0}.{2}#xyz.com. Also, I have a string following the same format say bht.aay.ccch#xyz.com. How can I break the string into the format provided into a list or a string array or any other data structure ( Number provided in the format is the index where the corresponding strings should be stored). Delimiter can be anything like , or /
Example :-
1) Format - {0}.{1}#xyz.com
String - name0.name1#xyz.com
String array[]= { "name0", "name1"}
2) Format - {1}.{0}.{2}#xyz.com
String - pos0.pos1.pos2#xyz.com
String array[]= { "pos1", "pos0", "pos2"}
3) Format - {0}.{1}
String - name0.name1
String array[] = { "pos0", "pos1"}
using System;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace ConAppCore
{
class Program
{
static void Main(string[] args)
{
Test("{0}.{1}#xyz.com", "name0.name1#xyz.com");
Test("{1}.{0}.{2}#xyz.com", "pos0.pos1.pos2#xyz.com");
Test("{0}.{1}", "name0.name1");
}
static void Test(string format, string input)
{
string pattern = CreatePattern(format);
var match = Regex.Match(input, pattern);
var array = match.Groups
.OfType<Group>()
.Skip(1)
.OrderBy(g => g.Name, StringComparer.Ordinal)
.Select(g => g.Value)
.ToArray();
Console.WriteLine(string.Join(", ", array));
}
static string CreatePattern(string format)
{
var sb = new StringBuilder();
var match = Regex.Match(format, #"(.*?)\{(\d+)\}");
int index;
int length;
do
{
sb.Append(Regex.Escape(match.Groups[1].Value));
sb.Append("(?'group").Append(match.Groups[2].Value).Append(#"'\w+)");
index = match.Index;
length = match.Length;
match = match.NextMatch();
} while (match.Success);
sb.Append(Regex.Escape(format.Substring(index + length)));
return sb.ToString();
}
}
}
Try following :
string[] inputs = { "name0.name1#xyz.com", "name0.name1#xyz.com", "name0.name1" };
string pattern = "(?'path'.*)#(?'site'.*)";
foreach(string input in inputs)
{
Match match = Regex.Match(input,pattern);
string[] paths = match.Groups["path"].Value.Split(new char[] {'.'}).ToArray();
string site = match.Groups["site"].Value;
Console.WriteLine("Path : '{0}', Site : '{1}'", string.Join("-", paths), site);
}
Console.ReadLine();

Extract number from string value

I have a string that always come into this format:
"TM" + multiple Leading 0 + Number + Non-Number Character + Alphanumeric.
For example: TM000013452S20548, PB000013452S3DVSF.
How do I parse (in C# code) the varchar value to get the "Number" (13452) in this case?
You can use RegualarExpressions:
(?:TM|PB)0{0,}(\d+)
Like this:
string input = "For example: TM000013452S20548, PB000013452S3DVSF.";
var matches = Regex.Matches(input, #"(?:TM|PB)0{0,}(\d+)");
foreach(Match m in matches)
Console.WriteLine(int.Parse(m.Groups[1].Value));
Live Demo
You can use Linq:
var number = new String(
yourString.Skip(2)
.SkipWhile(s => s == '0')
.TakeWhile(s => Char.IsDigit(s))
.ToArray()
);
If all the fields are fixed width, and all you care about is the first integer, then it's pretty easy; just use string.Substring to extract the part you care about and then parse it.
Here's how to do the extract and parse (note that I use int.TryParse - you are parsing a possibly corrupted string):
private bool TryExtractFirstNumber(string input, out int result)
{
var resultString = input.Substring(2, 9);
return int.TryParse(resultString, out result);
}
You can call this like:
var inputs = new[]
{
"TM000013452S20548",
"PB000013452S3DVSF",
};
foreach (var inp in inputs)
{
if (TryExtractFirstNumber(inp, out var result))
{
Debug.WriteLine(result);
}
}
The output from that is:
13452
13452
If the position of the "Non-Number Character" that you describe is not known, go looking for it:
private int FindIndexOfFirstNonNumeric(string toScan, int startIndex = 0)
{
for (var index = startIndex; index < toScan.Length; ++index)
{
if (!char.IsNumber(toScan[index]))
{
return index;
}
}
return toScan.Length;
}
and then modify the TryExtractFirstNumber function to look for it:
private bool TryExtractFirstNumber(string input, out int result)
{
var length = FindIndexOfFirstNonNumeric(input, 2) - 2;
var resultString = input.Substring(2, length);
return int.TryParse(resultString, out result);
}
It gives the same results.

How to convert a map point string to double?

I have a string such as 45,5235234096284 or 112,013574120648. I want to convert it to double. I have tried following code but i got error 'System.Globalization.CultureInfo' does not contain a definition for 'GetCultureInfo
' because of framework version. I'm not able to change framework option.I have looked some another solution but i couldn't understand clearly.What is the best way converting these string to double with their commas.
var convertedMapPoint = double.Parse(mapPoint, CultureInfo.GetCultureInfo(1053));
First, you should split your input on spaces, since that is the separator:
string input = "45,5235234096284 112,013574120648";
string[] splitted = input.Split(' ');
Then you can iterate over the result of the splitting, and convert each to a double:
List<double> doubles = new List<double>();
foreach (string s in splitted)
{
doubles.Add(double.Parse(s, new CultureInfo("fr-FR")));
}
I used fr-fr, since it has a comma as decimal separator, but you can use any culture that does that.
If that doesn't work for you, and you are 100% sure there is no . as thousand separator, you can use this:
doubles.Add(double.Parse(s.Replace(",", ".")));
Then you might want to add the invariant culture to be platform independent:
doubles.Add(double.Parse(s.Replace(",", ".", CultureInfo.InvariantCulture)));
You should split the string into array delimited by Space, and than convert each part.
string mapPoint = "45,5235234096284 112,013574120648";
var mapPoints = mapPoint.Split(' ');
var convertedMapPointX = double.Parse(mapPoints[0], CultureInfo.GetCultureInfo(1053));
var convertedMapPointY = double.Parse(mapPoints[1], CultureInfo.GetCultureInfo(1053));
Console.WriteLine("X: {0}\tY: {1}", convertedMapPointX, convertedMapPointY);
Output:
X: 45.5235234096284 Y: 112.013574120648
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StringToMapPoint
{
class Program
{
static void Main(string[] args)
{
string mapPointStr = "45,5235234096284 112,013574120648";
MapPoint mp = mapPointStr.ToMapPoint();
Console.WriteLine(mp.ToString());
}
}
public class MapPoint
{
public double X { get; set; }
public double Y { get; set; }
public override string ToString()
{
return "X: " + X + " Y: " + Y;
}
}
public static class MapPointHelper
{
public static MapPoint ToMapPoint(this string str)
{
var split = str.Replace(".", ",").Split(' ');
return new MapPoint() { X = Double.Parse(split[0]), Y = Double.Parse(split[1]) };
}
}
}

How can I search through a string in C# and replace areas bounded by a pattern?

We tried a few solutions now that try and use XML parsers. All fail because the strings are not always 100% valid XML. Here's our problem.
We have strings that look like this:
var a = "this is a testxxx of my data yxxx and of these xxx parts yxxx";
var b = "hello testxxx world yxxx ";
"this is a testxxx3yxxx and of these xxx1yxxx";
"hello testxxx1yxxx ";
The key here is that we want to do something to the data between xxx and yxxx. In the example above I would need a function that counts words and replaces the strings with a word count.
Is there a way we can process the string a and apply a function to change the data that's between the xxx and yxxx? Any function right now as we're just trying to get an idea of how to code this.
You can use Split method:
var parts = a.Split(new[] {"xxx", "yxxx"}, StringSplitOptions.None)
.Select((s, index) =>
{
string s1 = index%2 == 1 ? string.Format("{0}{2}{1}", "xxx", "yxxx", s + "1") : s;
return s1;
});
var result = string.Join("", parts);
If it always going to xxx and yxxx, you can use regex as suggested.
var stringBuilder = new StringBuilder();
Regex regex = new Regex("xxx(.*?)yxxx");
var splitGroups = Regex.Match(a);
foreach(var group in splitGroups)
{
var value = splitGroupsCopy[i];
// do something to value and then append it to string builder
stringBuilder.Append(string.Format("{0}{1}{2}", "xxx", value, "yxxx"));
}
I suppose this is as basic as it gets.
Using Regex.Replace will replace all the matches with your choice of text, something like this:
Regex rgx = new Regex("xxx.+yxxx");
string cleaned = rgx.Replace(a, "replacementtext");
This code will process each of the parts delimited by "xxx". It preserves the "xxx" separators. If you do not want to preserve the "xxx" separators, remove the two lines that say "result.Append(separator);".
Given:
"this is a testxxx of my data yxxx and there are many of these xxx parts yxxx"
It prints:
"this is a testxxx>> of my data y<<xxx and there are many of these xxx>> parts y<<xxx"
I'm assuming that's the kind of thing you want. Add your own processing to "processPart()".
using System;
using System.Text;
namespace ConsoleApplication1
{
internal class Program
{
private static void Main(string[] args)
{
string text = "this is a testxxx of my data yxxx and there are many of these xxx parts yxxx";
string separator = "xxx";
var result = new StringBuilder();
int index = 0;
while (true)
{
int start = text.IndexOf(separator, index);
if (start < 0)
{
result.Append(text.Substring(index));
break;
}
result.Append(text.Substring(index, start - index));
int end = text.IndexOf(separator, start + separator.Length);
if (end < 0)
{
throw new InvalidOperationException("Unbalanced separators.");
}
start += separator.Length;
result.Append(separator);
result.Append(processPart(text.Substring(start, end-start)));
result.Append(separator);
index = end + separator.Length;
}
Console.WriteLine(result);
}
private static string processPart(string part)
{
return ">>" + part + "<<";
}
}
}
[EDIT] Here's the code amended to work with two different separators:
using System;
using System.Text;
namespace ConsoleApplication1
{
internal class Program
{
private static void Main(string[] args)
{
string text = "this is a test<pre> of my data y</pre> and there are many of these <pre> parts y</pre>";
string separator1 = "<pre>";
string separator2 = "</pre>";
var result = new StringBuilder();
int index = 0;
while (true)
{
int start = text.IndexOf(separator1, index);
if (start < 0)
{
result.Append(text.Substring(index));
break;
}
result.Append(text.Substring(index, start - index));
int end = text.IndexOf(separator2, start + separator1.Length);
if (end < 0)
{
throw new InvalidOperationException("Unbalanced separators.");
}
start += separator1.Length;
result.Append(separator1);
result.Append(processPart(text.Substring(start, end-start)));
result.Append(separator2);
index = end + separator2.Length;
}
Console.WriteLine(result);
}
private static string processPart(string part)
{
return "|" + part + "|";
}
}
}
The indexOf() function will return to you the index of the first occurrence of a given substring.
(My indices might be a bit off, but) I would suggest doing something like this:
var searchme = "this is a testxxx of my data yxxx and there are many of these xxx parts yxxx";
var startindex= searchme.indexOf("xxx");
var endindex = searchme.indexOf("yxxx") + 3; //added 3 to find the index of the last 'x' instead of the index of the 'y' character
var stringpiece = searchme.substring(startindex, endindex - startindex);
and you can repeat that while startindex != -1
Like I said, the indices might be slightly off, you might have to add a +1 or -1 somewhere, but this will get you along nicely (I think).
Here is a little sample program that counts chars instead of words. But you should just need to change the processor function.
var a = "this is a testxxx of my data yxxx and there are many of these xxx parts yxxx";
a = ProcessString(a, CountChars);
string CountChars(string a)
{
return a.Length.ToString();
}
string ProcessString(string a, Func<string, string> processor)
{
int idx_start, idx_end = -4;
while ((idx_start = a.IndexOf("xxx", idx_end + 4)) >= 0)
{
idx_end = a.IndexOf("yxxx", idx_start + 3);
if (idx_end < 0)
break;
var string_in_between = a.Substring(idx_start + 3, idx_end - idx_start - 3);
var newString = processor(string_in_between);
a = a.Substring(0, idx_start + 3) + newString + a.Substring(idx_end, a.Length - idx_end);
idx_end -= string_in_between.Length - newString.Length;
}
return a;
}
I would use Regex Groups:
Here my solution to get the parts in the string:
private static IEnumerable<string> GetParts( string searchFor, string begin, string end ) {
string exp = string.Format("({0}(?<searchedPart>.+?){1})+", begin, end);
Regex regex = new Regex(exp);
MatchCollection matchCollection = regex.Matches(searchFor);
foreach (Match match in matchCollection) {
Group #group = match.Groups["searchedPart"];
yield return #group.ToString();
}
}
you can use it like to get the parts:
string a = "this is a testxxx of my data yxxx and there are many of these xxx parts yxxx";
IEnumerable<string> parts = GetParts(a, "xxx", "yxxx");
To replace the parts in the original String you can use the Regex Group to determine Length and StartPosition (#group.Index, #group.Length).

Categories

Resources