Extract substrings from a string - c#

I have string variable that has value like this
string hello = "Endpoint: Efficacy, Intervene: Single Group, Mask: Open, Purpose: Treatment";
As I am extracting from XML so Endpoint , Intervene , Mask and Purpose remains
same but its value can change.
I want to store this Endpoint , intervene , mask and purpose separately in different variables any help will be much appreciated.

var result = hello.Split(',')
.Select(part => part.Split(':'))
.ToDictionary(split => split[0], split => split[1].Trim());

Try this way using Dictionary
string hello = "Endpoint: Efficacy, Intervene: Single Group, Mask: Open, Purpose: Treatment";
Dictionary<string, string> result = new Dictionary<string, string>();
var s1 = hello.Split(',');
foreach (var s in s1)
{
var v = s.Split(':');
result.Add(v[0].Trim(), v[1].Trim());
}
After that you can get result using key value of the Dictionary
foreach (var a in result)
{
Console.WriteLine(a.Key +" " + a.Value);
}

Split your string first by , and then by : and save the result in dictionary:
public class Program
{
public static void Main(string[] args)
{
string hello = "Endpoint: Efficacy, Intervene: Single Group, Mask: Open, Purpose: Treatment";
string[] arr = hello.Split(',');
Dictionary<string, string> result = new Dictionary<string, string>();
foreach(string s in arr)
{
string[] keyValuePair = s.Split(':');
result[keyValuePair[0].Replace(" ","")] = keyValuePair[1].Replace(" ","");
}
foreach(var v in result)
{
Console.WriteLine(v.Key + " : " + v.Value );
}
}
}

If the pattern is fixed, this can help you.
string hello = "Endpoint: Efficacy, Intervene: Single Group, Mask: Open, Purpose: Treatment";
string[] packs = hello.Split(',');
var data = packs.ToDictionary(k => k.Split(':').First().Trim(), v => v.Split(':').Last().Trim());

I came up with something more dynamic just in case someone have to deal with variable keywords to match.
This first extracts the keywords and builds a pattern to match. Matches are then captured by name and each capture group is named after a keyword so that we can access them by keyword:
var input = "Endpoint: Efficacy, Intervene: Single Group, Mask: Open, Purpose: Treatment";
//extract keywords: (word followed by ':' )
var keywords = Regex.Matches( input, #"(\w*):" ).Cast<Match>()
.Select( m => m.Groups[ 1 ].Value );
//build the pattern to match (create capture groups named as keywords)
var pattern = String.Join( ", ", keywords.Select(
k => k + $#": (?<{k}>(\w*\s*)*)" ) );
var matches = Regex.Matches( input, pattern );
//access groups by keyword
foreach( var keyword in keywords )
Console.WriteLine( matches[ 0 ].Groups[ keyword ].Value );

Related

C# Use Regex to split on Words

This is a stripped down version of code I am working on. The purpose of the code is to take a string of information, break it down, and parse it into key value pairs.
Using the info in the example below, a string might look like:
"DIVIDE = KE48 CLACOS = 4556D DIV = 3466 INT = 4567"
One further point about the above example, at least three of the features we have to parse out will occasionally include additional values. Here is an updated fake example string.
"DIVIDE = KE48, KE49, KE50 CLACOS = 4566D DIV = 3466 INT = 4567 & 4568"
The problem with this is that the code refuses to split out DIVIDE and DIV information separately. Instead, it keeps splitting at DIV and then assigning the rest of the information as the value.
Is there a way to tell my code that DIVIDE and DIV need to be parsed out as two separate values, and to not turn DIVIDE into DIV?
public List<string> FeatureFilterStrings
{
// All possible feature types from the EWSD switch.
get
{
return new List<string>() { "DIVIDE", "DIV", "CLACOS", "INT"};
}
}
public void Parse(string input){
Func<string, bool> queryFilter = delegate(string line) { return FeatureFilterStrings.Any(s => line.Contains(s)); };
Regex regex = new Regex(#"(?=\\bDIVIDE|DIV|CLACOS|INT)");
string[] ms = regex.Split(updatedInput);
List<string> queryLines = new List<string>();
// takes the parsed out data and assigns it to the queryLines List<string>
foreach (string m in ms)
{
queryLines.Add(m);
}
var features = queryLines.Where(queryFilter);
foreach (string feature in features)
{
foreach (Match m in Regex.Matches(workLine, valueExpression))
{
string key = m.Groups["key"].Value.Trim();
string value = String.Empty;
value = Regex.Replace(m.Groups["value"].Value.Trim(), #"s", String.Empty);
AddKeyValue(key, value);
}
}
private void AddKeyValue(string key, string value)
{
try
{
// Check if key already exists. If it does, remove the key and add the new key with updated value.
// Value information appends to what is already there so no data is lost.
if (this.ContainsKey(key))
{
this.Remove(key);
this.Add(key, value.Split('&'));
}
else
{
this.Add(key, value.Split('&'));
}
}
catch (ArgumentException)
{
// Already added to the dictionary.
}
}
}
Further information, the string information does not have a set number of spaces between each key/value, each string may not include all of the values, and the features aren't always in the same order. Welcome to parsing old telephone switch information.
I would create a dictionary from your input string
string input = "DIVIDE = KE48 CLACOS = 4556D DIV = 3466 INT = 4567";
var dict = Regex.Matches(input, #"(\w+?) = (.+?)( |$)").Cast<Match>()
.ToDictionary(m => m.Groups[1].Value, m => m.Groups[2].Value);
Test the code:
foreach(var kv in dict)
{
Console.WriteLine(kv.Key + "=" + kv.Value);
}
This might be a simple alternative for you.
Try this code:
var input = "DIVIDE = KE48 CLACOS = 4556D DIV = 3466 INT = 4567";
var parts = input.Split(new [] { '=', ' ' }, StringSplitOptions.RemoveEmptyEntries);
var dictionary =
parts.Select((x, n) => new { x, n })
.GroupBy(xn => xn.n / 2, xn => xn.x)
.Select(xs => xs.ToArray())
.ToDictionary(xs => xs[0], xs => xs[1]);
I then get the following dictionary:
Based on your updated input, things get more complicated, but this works:
var input = "DIVIDE = KE48, KE49, KE50 CLACOS = 4566D DIV = 3466 INT = 4567 & 4568";
Func<string, char, string> tighten =
(i, c) => String.Join(c.ToString(), i.Split(c).Select(x => x.Trim()));
var parts =
tighten(tighten(input, '&'), ',')
.Split(new[] { '=', ' ' }, StringSplitOptions.RemoveEmptyEntries);
var dictionary =
parts
.Select((x, n) => new { x, n })
.GroupBy(xn => xn.n / 2, xn => xn.x)
.Select(xs => xs.ToArray())
.ToDictionary(
xs => xs[0],
xs => xs
.Skip(1)
.SelectMany(x => x.Split(','))
.SelectMany(x => x.Split('&'))
.ToArray());
I get this dictionary:

Regex replace multiple groups with wildcards

I found an answer when we do not use wildcard characters. So question is - how to perform multiple replaces by regex. This code shows what i want to do
internal class Program
{
private static void Main(string[] args)
{
var rules = new Dictionary<string, string>
{
{#"F\S+", "Replace 1"},
{#"\S+z", "Replace 2"},
};
string s = "Foo bar baz";
string result = ProcessText(s, rules);
Console.WriteLine(result);
}
private static string ProcessText(string input, Dictionary<string, string> rules)
{
string[] patterns = rules.Keys.ToArray();
string pattern = string.Join("|", patterns);
return Regex.Replace(input, pattern, match =>
{
int index = GetMatchIndex(match);
return rules[patterns[index]];
});
}
private static int GetMatchIndex(Match match)
{
int i = 0;
foreach (Match g in match.Groups)
{
if (g.Success)
return i;
i++;
}
throw new Exception("Never throws");
}
}
but match.Groups.Count is always 1.
I'm looking for the fastest alternative. Perhaps, it shouldn't use regexes.
I don't understand why are you concatenating patterns and then performin so many searches in an array.
Can't you just apply each pattern individually like this?
var rules = new Dictionary<string, string>
{
{#"F\S+", "Replace 1"},
{#"\S+z", "Replace 2"},
};
string s = "Foo bar baz";
var result = rules.Aggregate(s, (seed, rule) => Regex.Replace(seed, rule.Key, m => rule.Value));
EDIT
Your match.Groups.Count is always one because there are no groups defined in your matches and the values is the entire matched string as described on MSDN. In other words, your GetMatchIndex method does nothing.
You could try transforming your patterns into named groups like this:
var patterns = rules.Select((kvp, index) => new
{
Key = String.Format("(?<{0}>{1})", index, kvp.Key),
Value = kvp.Value
};
Having this array, in GetMatchIndex method you'd just parse the group name as being the index of the matched pattern:
private static int GetMatchIndex(Regex regex, Match match)
{
foreach(var name in regex.GetGroupNames())
{
var group = match.Groups[name];
if(group.Success)
return int.Parse(name); //group name is a number
}
return -1;
}
Now, you can use it like this:
var pattern = String.Join("|", patterns.Select(x => x.Key));
var regex = new Regex(pattern);
return regex.Replace(input, pattern, m =>
{
var index = GetMatchIndex(regex, m);
return patterns[index].Value;
});
To make it (probably way) faster extract
var keyArray = rules.Keys.ToArray()
before you use it in:
return Regex.Replace(input, pattern, match =>
{
int index = GetMatchIndex(match);
return rules[keyArray[index]];
});

C#: Loop over Textfile, split it and Print a new Textfile

I get many lines of String as an Input that look like this. The Input is a String that comes from
theObjects.Runstate;
each #VAR;****;#ENDVAR; represents one Line and one step in the loop.
#VAR;Variable=Speed;Value=Fast;Op==;#ENDVAR;#VAR;Variable=Fabricator;Value=Freescale;Op==;#ENDVAR;
I split it, to remove the unwanted fields, like #VAR,#ENDVAR and Op==.
The optimal Output would be:
Speed = Fast;
Fabricator = Freescale; and so on.
I am able to cut out the #VAR and the#ENDVAR. Cutting out the "Op==" wont be that hard, so thats now not the main focus of the question. My biggest concern right now is,thatI want to print the Output as a Text-File. To print an Array I would have to loop over it. But in every iteration, when I get a new line, I overwrite the Array with the current splitted string. I think the last line of the Inputfile is an empty String, so the Output I get is just an empty Text-File. It would be nice if someone could help me.
string[] w;
Textwriter tw2;
foreach (EA.Element theObjects in myPackageObject.Elements)
{
theObjects.Type = "Object";
foreach (EA.Element theElements in PackageHW.Elements)
{
if (theObjects.ClassfierID == theElements.ElementID)
{
t = theObjects.RunState;
w = t.Replace("#ENDVAR;", "#VAR;").Replace("#VAR;", ";").Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries);
foreach (string s in w)
{
tw2.WriteLine(s);
}
}
}
}
This linq-query gives the exptected result:
var keyValuePairLines = File.ReadLines(pathInputFile)
.Select(l =>
{
l = l.Replace("#VAR;", "").Replace("#ENDVAR;", "").Replace("Op==;", "");
IEnumerable<string[]> tokens = l.Split(new[]{';'}, StringSplitOptions.RemoveEmptyEntries)
.Select(t => t.Split('='));
return tokens.Select(t => {
return new KeyValuePair<string, string>(t.First(), t.Last());
});
});
foreach(var keyValLine in keyValuePairLines)
foreach(var keyVal in keyValLine)
Console.WriteLine("Key:{0} Value:{1}", keyVal.Key, keyVal.Value);
Output:
Key:Variable Value:Speed
Key:Value Value:Fast
Key:Variable Value:Fabricator
Key:Value Value:Freescale
If you want to output it to another text-file with one key-value pair on each line:
File.WriteAllLines(pathOutputFile, keyValuePairLines.SelectMany(l =>
l.Select(kv => string.Format("{0}:{1}", kv.Key, kv.Value))));
Edit according to your question in the comment:
"What would I have to change/add so that the Output is like this. I
need AttributeValuePairs, for example: Speed = Fast; or Fabricator =
Freescale ?"
Now i understand the logic, you have key-value pairs but you are interested only in the values. So every two key-values belong together, the first value of a pair specifies the attibute and the second value the value of that attribute(f.e. Speed=Fast).
Then it's a little bit more complicated:
var keyValuePairLines = File.ReadLines(pathInputFile)
.Select(l =>
{
l = l.Replace("#VAR;", "").Replace("#ENDVAR;", "").Replace("Op==;", "");
string[] tokens = l.Split(new[]{';'}, StringSplitOptions.RemoveEmptyEntries);
var lineValues = new List<KeyValuePair<string, string>>();
for(int i = 0; i < tokens.Length; i += 2)
{
// Value to a variable can be found on the next index, therefore i += 2
string[] pair = tokens[i].Split('=');
string key = pair.Last();
string value = null;
string nextToken = tokens.ElementAtOrDefault(i + 1);
if (nextToken != null)
{
pair = nextToken.Split('=');
value = pair.Last();
}
var keyVal = new KeyValuePair<string, string>(key, value);
lineValues.Add(keyVal);
}
return lineValues;
});
File.WriteAllLines(pathOutputFile, keyValuePairLines.SelectMany(l =>
l.Select(kv=>string.Format("{0} = {1}", kv.Key, kv.Value))));
Output in the file with your single sample-line:
Speed = Fast
Fabricator = Freescale

Extracting parts of a string c#

In C# what would be the best way of splitting this sort of string?
%%x%%a,b,c,d
So that I end up with the value between the %% AND another variable containing everything right of the second %%
i.e. var x = "x"; var y = "a,b,c,d"
Where a,b,c.. could be an infinite comma seperated list. I need to extract the list and the value between the two double-percentage signs.
(To combat the infinite part, I thought perhaps seperating the string out to: %%x%% and a,b,c,d. At this point I can just use something like this to get X.
var tag = "%%";
var startTag = tag;
int startIndex = s.IndexOf(startTag) + startTag.Length;
int endIndex = s.IndexOf(tag, startIndex);
return s.Substring(startIndex, endIndex - startIndex);
Would the best approach be to use regex or use lots of indexOf and substring to do the extracting based on te static %% characters?
Given that what you want is "x,a,b,c,d" the Split() function is actually pretty powerful and regex would be overkill for this.
Here's an example:
string test = "%%x%%a,b,c,d";
string[] result = test.Split(new char[] { '%', ',' }, StringSplitOptions.RemoveEmptyEntries);
foreach (string s in result) {
Console.WriteLine(s);
}
Basicly we ask it to split by both '%' and ',' and ignore empty results (eg. the result between "%%"). Here's the result:
x
a
b
c
d
To Extract X:
If %% is always at the start then;
string s = "%%x%%a,b,c,d,h";
s = s.Substring(2,s.LastIndexOf("%%")-2);
//Console.WriteLine(s);
Else;
string s = "v,u,m,n,%%x%%a,b,c,d,h";
s = s.Substring(s.IndexOf("%%")+2,s.LastIndexOf("%%")-s.IndexOf("%%")-2);
//Console.WriteLine(s);
If you need to get them all at once then use this;
string s = "m,n,%%x%%a,b,c,d";
var myList = s.ToArray()
.Where(c=> (c != '%' && c!=','))
.Select(c=>c).ToList();
This'll let you do it all in one go:
string pattern = "^%%(.+?)%%(?:(.+?)(?:,|$))*$";
string input = "%%x%%a,b,c,d";
Match match = Regex.Match(input, pattern);
if (match.Success)
{
// "x"
string first = match.Groups[1].Value;
// { "a", "b", "c", "d" }
string[] repeated = match.Groups[2].Captures.Cast<Capture>()
.Select(c => c.Value).ToArray();
}
You can use the char.IsLetter to get all the list of letter
string test = "%%x%%a,b,c,d";
var l = test.Where(c => char.IsLetter(c)).ToArray();
var output = string.Join(", ", l.OrderBy(c => c));
Since you want the value between the %% and everything after in separate variables and you don't need to parse the CSV, I think a RegEx solution would be your best choice.
var inputString = #"%%x%%a,b,c,d";
var regExPattern = #"^%%(?<x>.+)%%(?<csv>.+)$";
var match = Regex.Match(inputString, regExPattern);
foreach (var item in match.Groups)
{
Console.WriteLine(item);
}
The pattern has 2 named groups called x and csv, so rather than just looping, you can easily reference them by name and assign them to values:
var x = match.Groups["x"];
var y = match.Groups["csv"];

how to split the line in the text file

Text File:
$3.00,0.00,0.00,1.00,L55894M8,$3.00,0.00,0.00,2.00,L55894M9
How do I split the line and get the serial number like L55894M8 and L55894M9?
To get the data that appears after the 4th comma and 9th comma, you would want to do:
var pieces = line.Split(',');
var serial1 = line[3];
var serial2 = line[8];
Edit: Upon further reflection, it appears your file has records that begin with $ and end with the next record. If you want these records, along with the serial number (which appears to be the last field) you can do:
var records = line.TrimStart('$').Split('$');
var recordObjects = records.Select(r => new { Line = r, Serial = r.TrimEnd(',').Split(',').Last() });
In your sample you means want to get the words in index of ( 4 , 9 , 14 .... )
And the five words as a party .
So you can try this way.....
static void Main(string[] args)
{
string strSample = "$3.00,0.00,0.00,1.00,L55894M8,$3.00,0.00,0.00,2.00,L55894M9";
var result = from p in strSample.Split(',').Select((v, i) => new { Index = i, Value = v })
where p.Index % 5 == 4
select p.Value;
foreach (var r in result)
{
Console.WriteLine(r);
}
Console.ReadKey();
}
if the file is in a string you can use the string's .split(',') method then check each element of the resulting array. Or grab every 5th element if that pattern of data is seen throughout.
var str = "$3.00,0.00,0.00,1.00,L55894M8,$3.00,0.00,0.00,2.00,L55894M9";
var fieldArray = str.Split(new[] { ',' });
var serial1 = fieldArray[4]; // "L55894M8"
var serial2 = fieldArray[9]; // "L55894M9"
Try regular expression.
string str = "$3.00,0.00,0.00,1.00,L55894M8,$3.00,0.00,0.00,2.00,L55894M9";
string pat = #"L[\w]+";
MatchCollection ar= Regex.Matches(str, pat);
foreach (var t in ar)
Console.WriteLine(t);

Categories