Hello Please could you suggest better ways of writing this C# code.
Basically when NumberList has missing values between '-' I am trying to rebuild the String with default Values.
The final result should be "123-10-45-9-09"
As you can see value of "second-10" is replaced as the second item in the string.
10, 9 and 09 are filled in from the value string values.
This is the bad string which is missing some values.
string NumberList = "123--45--";
I have stored this string value in my app.config file.
string valuestring = "first-12,second-10,third-99,fourth-9,fifth-09";
protected string MissingNumberString(string Number)
{
string NumberList = "123--45--";
string valuestring = "first-12,second-10,third-99,fourth-9,fifth-09";
var companyAccountList = valuestring.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
var result = NumberList.Split('-');
int counter = 0;
var builder = new System.Text.StringBuilder();
foreach (string s in companyAccountList)
{
string t = s.Substring(s.IndexOf('-') + 1);
if (string.IsNullOrEmpty(result[counter]))
builder.Append(t).Append("-");
else
{
if (companyAccountList.Length == counter)
builder.Append(result[counter]);
else
builder.Append(result[counter]).Append("-");
}
counter++;
}
return builder.ToString();
}
One way (assuming valuestring is in order and do not miss any defaults) to achieve this would be
string MissingNumber(string Number)
{
string valuestring = "first-12,second-10,third-99,fourth-9,fifth-09";
var regex = Regex.Matches(valuestring,#"(?<=-)(\d*)(?<=,)?");
var defaults = regex.Cast<Match>().Select(x=>x.Value).ToList();
var newArray = Number.Split('-').Select((x,index)=>string.IsNullOrEmpty(x)?defaults[index]:x);
return string.Join("-",newArray);
}
The code uses Regular Expression to break the ValueString and read the default values.
Regex.Matches(valuestring,#"(?<=-)(\d*)(?<=,)?");
The regular expression uses non-capturing groups to capture a number which is prefixed as by an optional "-" character and suffixed by an optional "," character.
Once the defaults are parsed into a List (assuming that the positions are in order and do not miss any values), we loop through the input string (which has been split based on delimiter), check if it is Empty, and if so, use the value from the Defaults (based on our assumption, it should have same index).
Update
Based on the comments, it looks like you other data in the original string, and hence the concerned sub-string has to be captured first.
We could update the Missing Number method as
static string MissingNumber(string Number)
{
string valuestring = "first-12,second-10,third-99,fourth-9,fifth-09";
var regexDefaultValues = Regex.Matches(valuestring,#"(?<=-)(\d*)(?<=,)?");
var defaults = regexDefaultValues.Cast<Match>().Select(x=>x.Value).ToList();
var regexNumberToParse = new Regex(#"(\d)*-(\d)*-(\d)*-(\d)*-(\d)*");
var capturedNumberFormat = regexNumberToParse.Match(Number).Value;
var newArray = capturedNumberFormat.Split('-').Select((x,index)=>string.IsNullOrEmpty(x)?defaults[index]:x);
var ValueWithDefaults = string.Join("-",newArray);
return regexNumberToParse.Replace(Number,ValueWithDefaults);
}
Demo Code
Related
I have string COO70-123456789-12345-1. I need to parse based on the "-" not the length of the substrings and use the parsed values. I have tried using Regular expressions but having issues.Please suggest.
Also after I have split the values I need to use each values: string A = COO70, int B = 123456789, int C = 12345, short D = 1 . How do I get it in different variables A,B,C,D.
string[] results = UniqueId.Split('-');
string A = results[0];
string B = results[1];
string C = results[2];
int k_id = Convert.ToInt32(k_id);
string D = results[3];
short seq = Convert.ToInt16(seq);
string s = "COO70-123456789-12345-1";
string[] split = s.Split('-'); //=> {"COO70", "123456789", "12345", "1"}
Use indexOf
To find everything before the first hyphen use:
string original= "COO70-123456789-12345-1";
string toFirstHyphen=original.Substring(0,original.IndexOf("-"));
Or if you want every section use split like the above example.
You can verify whether the input is formatted as you want and then split to get the parts.
using System;
using System.Text.RegularExpressions;
class Program
{
static void Main()
{
string pattern = #"(?x)^(\w+-\w+-\w+-\w+)$";
Regex reg = new Regex(pattern);
string test = "word-6798-3401-001";
if((reg.Match(test).Success))
foreach (var x in test.Split(new char[] {'-'}))
Console.WriteLine(x);
}
}
So it sounds like you first want to split it, but then store it into your values.
I would do something like this:
var myString = "COO70-123456789-12345-1";
var stringSet = myString.Split("-"); // This returns an array of values.
Now we need to verify that we receive only 4 sub strings:
if (stringSet.Count != 4)
throw Exception e; // Throw a real exception, not this
From here we need to know what order our strings should be in and assign them:
var A = stringSet[0];
var B = stringSet[1];
var C = stringSet[2];
var D = stringSet[3];
While this should answer your question as posed, I would recommend you work with stringSet differently personally.
Suppose I have written "5 and 6" or "5+6". How can I assign 5 and 6 to two different variables in c# ?
P.S. I also want to do certain work if certain chars are found in string. Suppose I have written 5+5. Will this code do that ?
if(string.Contains("+"))
{
sum=x+y;
}
string input="5+5";
var numbers = Regex.Matches(input, #"\d+")
.Cast<Match>()
.Select(m => m.Value)
.ToList();
Personally, I would vote against doing some splitting and regular expression stuff.
Instead I would (and did in the past) use one of the many Expression Evaluation libraries, like e.g. this one over at Code Project (and the updated version over at CodePlex).
Using the parser/tool above, you could do things like:
A simple expression evaluation then could look like:
Expression e = new Expression("5 + 6");
Debug.Assert(11 == e.Evaluate());
To me this is much more error-proof than doing the parsing all by myself, including regular expressions and the like.
You should use another name for your string than string
var numbers = yourString.Split("+");
var sum = Convert.ToInt32(numbers[0]) + Convert.ToInt32(numbers[1]);
Note: Thats an implementation without any error checking or error handling...
If you want to assign numbers from string to variables, you will have to parse string and make conversion.
Simple example, if you have text with only one number
string text = "500";
int num = int.Parse(text);
Now, if you want to parse something more complicated, you can use split() and/or regex to get all numbers and operators between them. Than you just iterate array and assign numbers to variables.
string text = "500+400";
if (text.Contains("+"))
{
String[] data = text.Split("+");
int a = int.Parse(data[0]);
int b = int.Parse(data[1]);
int res = a + b;
}
Basicly, if you have just 2 numbers and operazor between them, its ok. If you want to make "calculator" you will need something more, like Binary Trees or Stack.
Use the String.Split method. It splits your string rom the given character and returns a string array containing the value that is broken down into multiple pieces depending on the character to break, in this case, its "+".
int x = 0;
int y = 0;
int z = 0;
string value = "5+6";
if (value.Contains("+"))
{
string[] returnedArray = value.Split('+');
x = Convert.ToInt32(returnedArray[0]);
y = Convert.ToInt32(returnedArray[1]);
z = x + y;
}
Something like this may helpful
string strMy = "5&6";
char[] arr = strMy.ToCharArray();
List<int> list = new List<int>();
foreach (char item in arr)
{
int value;
if (int.TryParse(item.ToString(), out value))
{
list.Add(item);
}
}
list will contains all the integer values
You can use String.Split method like;
string s = "5 and 6";
string[] a = s.Split(new string[] { "and", "+" }, StringSplitOptions.RemoveEmptyEntries);
Console.WriteLine(a[0].Trim());
Console.WriteLine(a[1].Trim());
Here is a DEMO.
Use regex to get those value and then switch on the operand to do the calculation
string str = "51 + 6";
str = str.Replace(" ", "");
Regex regex = new Regex(#"(?<rightHand>\d+)(?<operand>\+|and)(?<leftHand>\d+)");
var match = regex.Match(str);
int rightHand = int.Parse(match.Groups["rightHand"].Value);
int leftHand = int.Parse(match.Groups["leftHand"].Value);
string op = match.Groups["operand"].Value;
switch (op)
{
case "+":
.
.
.
}
Split function maybe is comfortable in use but it is space inefficient
because it needs array of strings
Maybe Trim(), IndexOf(), Substring() can replace Split() function
I have a large string, where there can be specific words (text followed by a single colon, like "test:") occurring more than once. For example, like this:
word:
TEST:
word:
TEST:
TEST: // random text
"word" occurs twice and "TEST" occurs thrice, but the amount can be variable. Also, these words don't have to be in the same order and there can be more text in the same line as the word (as shown in the last example of "TEST"). What I need to do is append the occurrence number to each word, for example the output string needs to be this:
word_ONE:
TEST_ONE:
word_TWO:
TEST_TWO:
TEST_THREE: // random text
The RegEx for getting these words which I've written is ^\b[A-Za-z0-9_]{4,}\b:. However, I don't know how to accomplish the above in a fast way. Any ideas?
Regex is perfect for this job - using Replace with a match evaluator:
This example is not tested nor compiled:
public class Fix
{
public static String Execute(string largeText)
{
return Regex.Replace(largeText, "^(\w{4,}):", new Fix().Evaluator);
}
private Dictionary<String, int> counters = new Dictionary<String, int>();
private static String[] numbers = {"ONE", "TWO", "THREE",...};
public String Evaluator(Match m)
{
String word = m.Groups[1].Value;
int count;
if (!counters.TryGetValue(word, out count))
count = 0;
count++;
counters[word] = count;
return word + "_" + numbers[count-1] + ":";
}
}
This should return what you requested when calling:
result = Fix.Execute(largeText);
i think you can do this with Regax.Replace(string, string, MatchEvaluator) and a dictionary.
Dictionary<string, int> wordCount=new Dictionary<string,int>();
string AppendIndex(Match m)
{
string matchedString = m.ToString();
if(wordCount.Contains(matchedString))
wordCount[matchedString]=wordCount[matchedString]+1;
else
wordCount.Add(matchedString, 1);
return matchedString + "_"+ wordCount.ToString();// in the format: word_1, word_2
}
string inputText = "....";
string regexText = #"";
static void Main()
{
string text = "....";
string result = Regex.Replace(text, #"^\b[A-Za-z0-9_]{4,}\b:",
new MatchEvaluator(AppendIndex));
}
see this:
http://msdn.microsoft.com/en-US/library/cft8645c(v=VS.80).aspx
If I understand you correctly, regex is not necessary here.
You can split your large string by the ':' character. Maybe you also need to read line by line (split by '\n'). After that you just create a dictionary (IDictionary<string, int>), which counts the occurrences of certain words. Every time you find word x, you increase the counter in the dictionary.
EDIT
Read your file line by line OR split the string by '\n'
Check if your delimiter is present. Either by splitting by ':' OR using regex.
Get the first item from the split array OR the first match of your regex.
Use a dictionary to count your occurrences.
if (dictionary.Contains(key)) dictionary[key]++;
else dictionary.Add(key, 1);
If you need words instead of numbers, then create another dictionary for these. So that dictionary[key] equals one if key equals 1. Mabye there is another solution for that.
Look at this example (I know it's not perfect and not so nice)
lets leave the exact argument for the Split function, I think it can help
static void Main(string[] args)
{
string a = "word:word:test:-1+234=567:test:test:";
string[] tks = a.Split(':');
Regex re = new Regex(#"^\b[A-Za-z0-9_]{4,}\b");
var res = from x in tks
where re.Matches(x).Count > 0
select x + DecodeNO(tks.Count(y=>y.Equals(x)));
foreach (var item in res)
{
Console.WriteLine(item);
}
Console.ReadLine();
}
private static string DecodeNO(int n)
{
switch (n)
{
case 1:
return "_one";
case 2:
return "_two";
case 3:
return "_three";
}
return "";
}
I have a string that I am reading from another system. It's basically a long string that represents a list of key value pairs that are separated by a space in between. It looks like this:
key:value[space]key:value[space]key:value[space]
So I wrote this code to parse it:
string myString = ReadinString();
string[] tokens = myString.split(' ');
foreach (string token in tokens) {
string key = token.split(':')[0];
string value = token.split(':')[1];
. . . .
}
The issue now is that some of the values have spaces in them so my "simplistic" split at the top no longer works. I wanted to see how I could still parse out the list of key value pairs (given space as a separator character) now that I know there also could be spaces in the value field as split doesn't seem like it's going to be able to work anymore.
NOTE: I now confirmed that KEYs will NOT have spaces in them so I only have to worry about the values. Apologies for the confusion.
Use this regular expression:
\w+:[\w\s]+(?![\w+:])
I tested it on
test:testvalue test2:test value test3:testvalue3
It returns three matches:
test:testvalue
test2:test value
test3:testvalue3
You can change \w to any character set that can occur in your input.
Code for testing this:
var regex = new Regex(#"\w+:[\w\s]+(?![\w+:])");
var test = "test:testvalue test2:test value test3:testvalue3";
foreach (Match match in regex.Matches(test))
{
var key = match.Value.Split(':')[0];
var value = match.Value.Split(':')[1];
Console.WriteLine("{0}:{1}", key, value);
}
Console.ReadLine();
As Wonko the Sane pointed out, this regular expression will fail on values with :. If you predict such situation, use \w+:[\w: ]+?(?![\w+:]) as the regular expression. This will still fail when a colon in value is preceded by space though... I'll think about solution to this.
This cannot work without changing your split from a space to something else such as a "|".
Consider this:
Alfred Bester:Alfred Bester Alfred:Alfred Bester
Is this Key "Alfred Bester" & value Alfred" or Key "Alfred" & value "Bester Alfred"?
string input = "foo:Foobarius Maximus Tiberius Kirk bar:Barforama zap:Zip Brannigan";
foreach (Match match in Regex.Matches(input, #"(\w+):([^:]+)(?![\w+:])"))
{
Console.WriteLine("{0} = {1}",
match.Groups[1].Value,
match.Groups[2].Value
);
}
Gives you:
foo = Foobarius Maximus Tiberius Kirk
bar = Barforama
zap = Zip Brannigan
You could try to Url encode the content between the space (The keys and the values not the : symbol) but this would require that you have control over the Input Method.
Or you could simply use another format (Like XML or JSON), but again you will need control over the Input Format.
If you can't control the input format you could always use a Regular expression and that searches for single spaces where a word plus : follows.
Update (Thanks Jon Grant)
It appears that you can have spaces in the key and the value. If this is the case you will need to seriously rethink your strategy as even Regex won't help.
string input = "key1:value key2:value key3:value";
Dictionary<string, string> dic = input.Split(' ').Select(x => x.Split(':')).ToDictionary(x => x[0], x => x[1]);
The first will produce an array:
"key:value", "key:value"
Then an array of arrays:
{ "key", "value" }, { "key", "value" }
And then a dictionary:
"key" => "value", "key" => "value"
Note, that Dictionary<K,V> doesn't allow duplicated keys, it will raise an exception in such a case. If such a scenario is possible, use ToLookup().
Using a regular expression can solve your problem:
private void DoSplit(string str)
{
str += str.Trim() + " ";
string patterns = #"\w+:([\w+\s*])+[^!\w+:]";
var r = new System.Text.RegularExpressions.Regex(patterns);
var ms = r.Matches(str);
foreach (System.Text.RegularExpressions.Match item in ms)
{
string[] s = item.Value.Split(new char[] { ':' });
//Do something
}
}
This code will do it (given the rules below). It parses the keys and values and returns them in a Dictonary<string, string> data structure. I have added some code at the end that assumes given your example that the last value of the entire string/stream will be appended with a [space]:
private Dictionary<string, string> ParseKeyValues(string input)
{
Dictionary<string, string> items = new Dictionary<string, string>();
string[] parts = input.Split(':');
string key = parts[0];
string value;
int currentIndex = 1;
while (currentIndex < parts.Length-1)
{
int indexOfLastSpace=parts[currentIndex].LastIndexOf(' ');
value = parts[currentIndex].Substring(0, indexOfLastSpace);
items.Add(key, value);
key = parts[currentIndex].Substring(indexOfLastSpace + 1);
currentIndex++;
}
value = parts[parts.Length - 1].Substring(0,parts[parts.Length - 1].Length-1);
items.Add(key, parts[parts.Length-1]);
return items;
}
Note: this algorithm assumes the following rules:
No spaces in the values
No colons in the keys
No colons in the values
Without any Regex nor string concat, and as an enumerable (it supposes keys don't have spaces, but values can):
public static IEnumerable<KeyValuePair<string, string>> Split(string text)
{
if (text == null)
yield break;
int keyStart = 0;
int keyEnd = -1;
int lastSpace = -1;
for(int i = 0; i < text.Length; i++)
{
if (text[i] == ' ')
{
lastSpace = i;
continue;
}
if (text[i] == ':')
{
if (lastSpace >= 0)
{
yield return new KeyValuePair<string, string>(text.Substring(keyStart, keyEnd - keyStart), text.Substring(keyEnd + 1, lastSpace - keyEnd - 1));
keyStart = lastSpace + 1;
}
keyEnd = i;
continue;
}
}
if (keyEnd >= 0)
yield return new KeyValuePair<string, string>(text.Substring(keyStart, keyEnd - keyStart), text.Substring(keyEnd + 1));
}
I guess you could take your method and expand upon it slightly to deal with this stuff...
Kind of pseudocode:
List<string> parsedTokens = new List<String>();
string[] tokens = myString.split(' ');
for(int i = 0; i < tokens.Length; i++)
{
// We need to deal with the special case of the last item,
// or if the following item does not contain a colon.
if(i == tokens.Length - 1 || tokens[i+1].IndexOf(':' > -1)
{
parsedTokens.Add(tokens[i]);
}
else
{
// This bit needs to be refined to deal with values with multiple spaces...
parsedTokens.Add(tokens[i] + " " + tokens[i+1]);
}
}
Another approach would be to split on the colon... That way, your first array item would be the name of the first key, second item would be the value of the first key and then name of the second key (can use LastIndexOf to split it out), and so on. This would obviously get very messy if the values can include colons, or the keys can contain spaces, but in that case you'd be pretty much out of luck...
How can I remove duplicate substrings within a string? so for instance if I have a string like smith:rodgers:someone:smith:white then how can I get a new string that has the extra smith removed like smith:rodgers:someone:white. Also I'd like to keep the colons even though they are duplicated.
many thanks
string input = "smith:rodgers:someone:smith:white";
string output = string.Join(":", input.Split(':').Distinct().ToArray());
Of course this code assumes that you're only looking for duplicate "field" values. That won't remove "smithsmith" in the following string:
"smith:rodgers:someone:smithsmith:white"
It would be possible to write an algorithm to do that, but quite difficult to make it efficient...
Something like this:
string withoutDuplicates = String.Join(":", myString.Split(':').Distinct().ToArray());
Assuming the format of that string:
var theString = "smith:rodgers:someone:smith:white";
var subStrings = theString.Split(new char[] { ':' });
var uniqueEntries = new List<string>();
foreach(var item in subStrings)
{
if (!uniqueEntries.Contains(item))
{
uniqueEntries.Add(item);
}
}
var uniquifiedStringBuilder = new StringBuilder();
foreach(var item in uniqueEntries)
{
uniquifiedStringBuilder.AppendFormat("{0}:", item);
}
var uniqueString = uniquifiedStringBuilder.ToString().Substring(0, uniquifiedStringBuilder.Length - 1);
Is rather long-winded but shows the process to get from one to the other.
not sure why you want to keep the duplicate colons. if you are expecting the output to be "smith:rodgers:someone::white" try this code:
public static string RemoveDuplicates(string input)
{
string output = string.Empty;
System.Collections.Specialized.StringCollection unique = new System.Collections.Specialized.StringCollection();
string[] parts = input.Split(':');
foreach (string part in parts)
{
output += ":";
if (!unique.Contains(part))
{
unique.Add(part);
output += part;
}
}
output = output.Substring(1);
return output;
}
ofcourse i've not checked for null input, but i'm sure u'll do it ;)