Adding numbers from list of custom delimieters - c#

I am writing program to add numbers from string which will be seperated from delimeters
private static readonly char[] Separators = { ',', '\n', '/','#' };
public int Add(string numbers)
{
if (numbers.Equals(string.Empty))
{
return 0;
}
return numbers.Split(Separators).Select(int.Parse).Sum();
}
When i pass the following string to Add method //#\n2#3
Then i get below error Input string was not in a correct format.
I expect answer to be 5

By default, string.Split will create empty groups if two delimiters are right next to each other. For example "3,,4".Split(','); will produce an array with three elements ("3", empty string, and "4").
You can change this in one of two ways. The first (and probably simpler) is to have the Split ignore empty entries.
numbers.Split(Separators, StringSplitOptions.RemoveEmptyEntries)
Or you can use Where in Linq
numbers.Split(Separators).Where(x => x.Length > 0)
This will prevent elements with a blank string value reaching int.Parse. Of course, there are still other things you should do to validate your input before attempting to parse, but that's another topic.

Related

Trouble Converting string [] to int []

using System;
using System.IO;
namespace Test_Arrays_and_Files
{
class Program
{
static void Main(string[] args)
{
string tFile = #"C:\Programming\GLO\DC\dcw.txt";
string read = File.ReadAllText(tFile);
string[] test = read.Split(',');
int[] ints = Array.ConvertAll(test, int.Parse);
Console.WriteLine(ints[0]);
}
}
}
Input Data:
Text File Contents:(1 value per line,)
35,
35,
40,
40,
40,
getting System.FormatException: Input string was not in correct format
please help and sorry for bad post I'm new here
There are two issues in your code,
Your string is ending with , which is creating empty record after the split. This is the reason you are getting the error.
Your delimiter could be $",{Environment.NewLine}" not only ','.
So to convert given string to int array, first Trim() the input string by , and then split by $",{Environment.NewLine}".
Like,
using System.Linq;
...
var result = str.Trim(',') //Remove leading and trailing comma(s), You can use `TrimEnd()` as well
.Split($",{Environment.NewLine}", StringSplitOptions.RemoveEmptyEntries) //Split by given delimiter.
.Select(int.Parse)
.ToArray(); //Convert string[] to int[]
Try online
You're only getting it because the file has a comma at the end, which means Split ends up churning out an empty string in the very last position. int.Parse will choke on the empty string
Plenty of ways you could solve it, one is to tell Split not to return you empties, by changing the split line of your code to:
string[] test = read.Split(new[]{','}, StringSplitOptions.RemoveEmptyEntries);
You could instead trim the comma off the end, but using the above approach would mean your parsing would survive a blank line in the middle of the file too so overall it's more robust
Generally when parsing strings it's more robust to use TryParse than Parse. TryParse takes in the number variable to set the result to and returns you a Boolean telling if the parsing succeeded
int[] ints = Array.ConvertAll(test, GetIntOrMinusOne);
//put a method helper
private int GetIntOrMinusOne(string s){
if(int.TryParse(s, out var t)
return t;
return -1;
}
For this we need to get a bit more involved with the ConvertAll call. Instead of telling ConvertAll to call a "method that converts a string to an int" like int.Parse, we need to write our own mini method that tries to parse and if it fails return something like -1, then nominate that as the method to call to do the conversion, not int.Parse
It's important to note that this would introduce -1 into the resulting int[] array wherever there was bad data in the string array.. In your later processing you would then do some check to avoid them (such as skipping them)
You can shorten that code above by turning the method into a lambda:
int[] ints = Array.ConvertAll(test, s => int.TryParse(s, out var t) ? t : -1);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
a lambda; "just the important parts of a method"
But I'm not sure that you'll have come across lambdas yet, judging by the style of the rest of the code

Substring Issue: Substring converting to char

I am making a typing game and I need to make a list of each character in a string so I can define what input the code should be expecting.
I tried:
static List<char> chars = "This is my string".ToCharArray().ToList();
But because char does not contain capitalization information it throws this error:
ArgumentException: InputKey named: T is unknown.
I knew char was not going to work, I needed each letter to be a string, not a char. So, I created a method using Substring:
static List<string> ToStringArray(string input)
{
List<string> strings = new List<string>();
for (int i = 0; i < input.Length; i++)
{
strings.Add(input.Substring(i, 1));
}
return strings;
}
static List<string> strings = ToStringArray("This is my string");
But apparently Substring is converting to a char because my code is still throwing the same error, and if I change the length of the substring to 2 my code works again. How can I force Substring to not convert to char? Or should I be approaching this problem in a completely different way?
I think you may be approaching this from a more complex angle than it needs to be.
If you have a:
string testString = "This is my string";
Then you can already access each individual character by index, such as testString[1] (which would be 'h')
If you're worried about case, then you can reference the string with
testString.ToLower();

C# LINQ: How is string("[1, 2, 3]") parsed as an array?

I am trying to parse a string into array and find a very concise approach.
string line = "[1, 2, 3]";
string[] input = line.Substring(1, line.Length - 2).Split();
int[] num = input.Skip(2)
.Select(y => int.Parse(y))
.ToArray();
I tried remove Skip(2) and I cannot get the array because of non-int string. My question is that what is the execution order of those LINQ function. How many times is Skip called here?
Thanks in advance.
The order is the order that you specify. So input.Skip(2) skips the first two strings in the array, so only the last remains which is 3. That can be parsed to an int. If you remove the Skip(2) you are trying to parse all of them. That doesn't work because the commas are still there. You have splitted by white-spaces but not removed the commas.
You could use line.Trim('[', ']').Split(','); and int.TryParse:
string line = "[1, 2, 3]";
string[] input = line.Trim('[', ']').Split(',');
int i = 0;
int[] num = input.Where(s => int.TryParse(s, out i)) // you could use s.Trim but the spaces don't hurt
.Select(s => i)
.ToArray();
Just to clarify, i have used int.TryParse only to make sure that you don't get an exception if the input contains invalid data. It doesn't fix anything. It would also work with int.Parse.
Update: as has been proved by Eric Lippert in the comment section using int.TryParse in a LINQ query can be harmful. So it's better to use a helper method that encapsulates int.TryParse and returns a Nullable<int>. So an extension like this:
public static int? TryGetInt32(this string item)
{
int i;
bool success = int.TryParse(item, out i);
return success ? (int?)i : (int?)null;
}
Now you can use it in a LINQ query in this way:
string line = "[1, 2, 3]";
string[] input = line.Trim('[', ']').Split(',');
int[] num = input.Select(s => s.TryGetInt32())
.Where(n => n.HasValue)
.Select(n=> n.Value)
.ToArray();
The reason it does not work unless you skip the first two lines is that these lines have commas after ints. Your input looks like this:
"1," "2," "3"
Only the last entry can be parsed as an int; the initial two will produce an exception.
Passing comma and space as separators to Split will fix the problem:
string[] input = line
.Substring(1, line.Length - 2)
.Split(new[] {',', ' '}, StringSplitOptions.RemoveEmptyEntries);
Note the use of StringSplitOptions.RemoveEmptyEntries to remove empty strings caused by both comma and space being used between entries.
I think it would be better you do it this way:
JsonConvert.DeserializeObject(line, typeof(List<int>));
you might try
string line = "[1,2,3]";
IEnumerable<int> intValues = from i in line.Split(',')
select Convert.ToInt32(i.Trim('[', ' ', ']'));

Parsing similar named parameters out of a URL

I'm taking a URL that looks like this:
some_site.com/full/path/page.aspx?label[0]=a_value&label[1]=b_value&label[2]=c_value
The indexed number is generated, so there's a dynamic number of these 'label[x]' values every time.
What would the simplest way of parsing these all into a String[] named 'Label' in ASP/C#.NET 4.0?
You should use a NameValueCollection instead of array of Strings.
NameValueCollection queryParameters = new NameValueCollection();
string[] querySegments = queryString.Split('&');
foreach(string segment in querySegments)
{
string[] parts = segment.Split('=');
if (parts.Length > 0)
{
string key = parts[0].Trim(new char[] { '?', ' ' });
string val = parts[1].Trim();
queryParameters.Add(key, val);
}
}
To get the number of the label withing the square brackets, use Regular Expressions.
regxObj = new Regex(#"\[(.*?)\]");
Have you thought about enumerating the entries in the Request.Querystring collection?
You can start by taking the substring from the index of '?' to the end, then split by '&'.
Then you can either loop through that list and split by '=' and take the second element, or the substring of each of those starting after the index of '='.
If you do want it as just and array of strings for some reason, this one line will probably work.
String[] labels = (from substring in s.Substring(s.IndexOf('?') + 1).Split('&') select substring.Substring(substring.IndexOf('=') + 1)).ToArray();
edit: Do note that this disregards what the actual labels are, as well as their numbers; if there's something other than named, numbered label[n] tags, those will be added as to the array as well.
make a parameter called length
some_site.com/full/path/page.aspx?length=4&label[0]=a_value&label[1]=b_value&label[2]=c_value...
then that will be easy to parse on the other side already knowing the length
if you know the length , then you know how many times to iterate through a loop to read the values of querystring
-or-
don't have a variable amount of parametes , use one , and use any special character to seperate them, then split the value by the seperating char on the other side

Failed to use SUBSTRING in TryParse

I found an error in my code, where the subtring is not work, it says "startIndex cannot be larger than the length of string"
static int MyIntegerParse(string possibleInt)
{
int i;
return int.TryParse(possibleInt.Substring(2), out i) ? i : 0;
}
I used the procedure here:
var parsed = File.ReadLines(filename)
.Select(line => line.Split(' ')
.Select(MyIntegerParse)
.ToArray())
.ToArray();
But I don't understand why it's error because I already used the substring before and it's work, can I ask for a help here? thnaks.
sample string:
10192 20351 30473 40499 50449 60234
10192 20207 30206 40203 50205 60226
10192 20252 30312 40376 50334 60252
Substring will fail when possibleInt contains fewer than two characters, so you should add that test to your code as well. I suspect that you Split call produces an empty string during some circumstances. This empty string is passed into your int-parser which then fails on the Substring call. So, you should probably do two things:
Get rid of empty strings in the splitting
Handle short or empty strings deliberately in your parsing code
Getting rid of empty strings is quite easy:
var parsed = File.ReadLines(filename)
.Select(line => line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)
.Select(MyIntegerParse)
.ToArray())
.ToArray();
Adding deliberate handling of empty strings can be done like so:
static int MyIntegerParse(string possibleInt)
{
if (string.IsNullOrEmpty(possibleInt) || possibleInt.Length < 2)
{
return 0;
}
int i;
return int.TryParse(possibleInt.Substring(2), out i) ? i : 0;
}
...or if you are a fan of compact and hard-to-read constructs:
static int MyIntegerParse(string possibleInt)
{
int i;
return (!string.IsNullOrEmpty(possibleInt)
&& possibleInt.Length >= 2
&& int.TryParse(possibleInt.Substring(2), out i)) ? i : 0;
}
No, I have chosen to return 0 when I get strings that are too short. In your case it might make more sense to return some other value, throw an exception or use a Debug.Assert statement.
The possibleInt string needs to be at least two characters long. When it isn't then you'll see the error that you've described.
Add this before your return statement and see if that helps you figure out what's going on:
Debug.Assert(!string.IsNullOrEmpty(possibleInt) && possibleInt.Length > 2);
When running in Debug mode this will throw an exception if the two cases above are not met.
You could also use a Code Contract like this:
Contract.Assert(!string.IsNullOrEmpty(possibleInt) && possibleInt.Length > 2);
You are getting this exception because you are trying to get the substring of a string starting at an index that is greater than the length of the string.
someString.Substring(x) will give you the substring of someString starting at position x in the string, and it is zero based. You are getting this exception because in this case 2 is outside the range of the particular strings length.
Stick a try catch around it, or a breakpoint and you will see the string that is causing this exception has a length less than 3.
The line you are attempting to parse is not that long. From the C# Specification on Substring:
The zero-based starting character position of a substring in this instance.
The string you are passing in either has 0 or 1 characters in it. You need to modify your code to handle such a situation.
EDIT: Additionally, you should be removing empty elements from your file using an overload of split:
.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntires)

Categories