System.ArgumentOutOfRangeException Message=Length cannot be less than zero. (Parameter 'length') - c#

Why is this code giving me a "length cannot be less than zero" error message?
class Fraction
{
private double Numerator = 0;
private double Denominator= 1;
public static Fraction Parse(string str)
{
Fraction newFrac = new Fraction();
int indexSlash = str.IndexOf("/");
newFrac.Numerator = int.Parse(str.Substring(0, indexSlash));
newFrac.Denominator = int.Parse(str.Substring(indexSlash + 1));
return newFrac;
}
}

string.IndexOf() returns -1 when the string does not contain the expected sequence (a slash in this case). So when you call Parse() with a string that does not contain a slash ("/"), the next line calls str.Substring(0, -1), which is obviously illegal.

The cause of the message is that str doesn't contain /:
// when str doesn't contain "/", the result of IndexOf is -1
// So indexSlash == -1
int indexSlash = str.IndexOf("/");
// str.Substring(0, indexSlash)
// is now
// str.Substring(0, -1)
// which throws the exception
newFrac.Numerator = int.Parse(str.Substring(0, indexSlash));
I suggest starting implementation from TryParse:
class Fraction {
...
public static bool TryParse(string value, out Fraction result) {
result = null;
if (value is null)
return false;
int index = value.IndexOf('/');
if (index <= 0)
return false;
if (!int.TryParse(str.Substring(0, index), out var num) ||
!int.TryParse(str.Substring(index + 1), out var den))
return false;
result = new Fraction() {
Numerator = num,
Denominator = den
};
return true;
}
And based on it Parse which is very simple now:
public static Fraction Parse(string value) => TryParse(value, out var result)
? result
: throw new FormatException("Not a valid format for fraction");

Related

function complex_decode( string str) that takes a non-simple repeated encoded string, and returns the original un-encoded string

Im trying to write a function complex_decode( string str) in c sharp that takes a non-simple repeated encoded string, and returns the original un-encoded string.
for example, "t11h12e14" would return "ttttttttttthhhhhhhhhhhheeeeeeeeeeeeee". I have been successful in decoding strings where the length is less than 10, but unable to work with length for than 10. I am not allowed to use regex, libraries or loops. Only recursions.
This is my code for simple decode which decodes when length less than 10.
public string decode(string str)
{
if (str.Length < 1)
return "";
if(str.Length==2)
return repeat_char(str[0], char_to_int(str[1]));
else
return repeat_char(str[0], char_to_int(str[1]))+decode(str.Substring(2));
}
public int char_to_int(char c)
{
return (int)(c-48);
}
public string repeat_char(char c, int n)
{
if (n < 1)
return "";
if (n == 1)
return ""+c;
else
return c + repeat_char(c, n - 1);
}
This works as intended, for example input "a5" returns "aaaaa", "t1h1e1" returns "the"
Any help is appreciated.
Here is another way of doing this, assuming the repeating string is always one character long and using only recursion (and a StringBuilder object):
private static string decode(string value)
{
var position = 0;
var result = decode_char(value, ref position);
return result;
}
private static string decode_char(string value, ref int position)
{
var next = value[position++];
var countBuilder = new StringBuilder();
get_number(value, ref position, countBuilder);
var result = new string(next, Convert.ToInt32(countBuilder.ToString()));
if (position < value.Length)
result += decode_char(value, ref position);
return result;
}
private static void get_number(string value, ref int position, StringBuilder countBuilder)
{
if (position < value.Length && char.IsNumber(value[position]))
{
countBuilder.Append(value[position++]);
get_number(value, ref position, countBuilder);
}
}
I've refactored your code a bit. I've removed 2 unnecessary methods that you don't actually need. So, the logic is simple and it works like this;
Example input: t3h2e4
Get the first digit. (Which is 2 and has index of 1)
Get the first letter comes after that index, which is our next letter. (Which is "h" and has index of 2)
Slice the string. Start from index 1 and end the slicing on index 2 to get repeat count. (Which is 3)
Repeat the first letter of string for repeat count times and combine it with the result you got from step 5.
Slice the starting from the next letter index we got in second step, to the very end of the string and pass this to recursive method.
public static string Decode(string input)
{
// If the string is empty or has only 1 character, return the string itself to not proceed.
if (input.Length <= 1)
{
return input;
}
// Convert string into character list.
var characters = new List<char>();
characters.AddRange(input);
var firstDigitIndex = characters.FindIndex(c => char.IsDigit(c)); // Get first digit
var nextLetterIndex = characters.FindIndex(firstDigitIndex, c => char.IsLetter(c)); // Get the next letter after that digit
if (nextLetterIndex == -1)
{
// This has only one reason. Let's say you are in the last recursion and you have c2
// There is no letter after the digit, so the index will -1, which means "not found"
// So, it will raise an exception, since we try to use the -1 in slicing part
// Instead, if it's not found, we set the next letter index to length of the string
// With doing that, you either get repeatCount correctly (since remaining part is only digits)
// or you will get empty string in the next recursion, which will stop the recursion.
nextLetterIndex = input.Length;
}
// Let's say first digit's index is 1 and the next letter's index is 2
// str[2..3] will start to slice the string from index 2 and will stop in index 3
// So, it will basically return us the repeat count.
var repeatCount = int.Parse(input[firstDigitIndex..nextLetterIndex]);
// string(character, repeatCount) constructor will repeat the "character" you passed to it for "repeatCount" times
return new string(input[0], repeatCount) + Decode(input[nextLetterIndex..]);
}
Examples;
Console.WriteLine(Decode("t1h1e1")); // the
Console.WriteLine(Decode("t2h3e4")); // tthhheeee
Console.WriteLine(Decode("t3h3e3")); // ttthhheee
Console.WriteLine(Decode("t2h10e2")); // tthhhhhhhhhhee
Console.WriteLine(Decode("t2h10e10")); // tthhhhhhhhhheeeeeeeeee
First you can simplify your repeat_char function, you have to have a clear stop condition:
public static string repeat_char(char c, int resultLength)
{
if(resultLength < 1) throw new ArgumentOutOfRangeException("resultLength");
if(resultLength == 1) return c.ToString();
return c + repeat_char(c, resultLength - 1);
}
See the use of the parameter as equivalent of a counter on a loop.
So you can have something similar on the main function, a parameter that tells when your substring is not an int anymore.
public static string decode(string str, int repeatNumberLength = 1)
{
if(repeatNumberLength < 1) throw new ArgumentOutOfRangeException("length");
//stop condition
if(string.IsNullOrWhiteSpace(str)) return str;
if(repeatNumberLength >= str.Length) repeatNumberLength = str.Length; //Some validation, just to be safe
//keep going until str[1...repeatNumberLength] is not an int
int charLength;
if(repeatNumberLength < str.Length && int.TryParse(str.Substring(1, repeatNumberLength), out charLength))
{
return decode(str, repeatNumberLength + 1);
}
repeatNumberLength--;
//Get the repeated Char.
charLength = int.Parse(str.Substring(1, repeatNumberLength));
var repeatedChar = repeat_char(str[0], charLength);
//decode the substring
var decodedSubstring = decode(str.Substring(repeatNumberLength + 1));
return repeatedChar + decodedSubstring;
}
I used a default parameter, but you can easily change it for a more traditonal style.
This also assumes that the original str is in a correct format.
An excellent exercise is to change the function so that you can have a word, instead of a char before the number. Then you could, for example, have "the3" as the parameter (resulting in "thethethe").
I took more of a Lisp-style head and tail approach (car and cdr if you speak Lisp) and created a State class to carry around the current state of the parsing.
First the State class:
internal class State
{
public State()
{
LastLetter = string.Empty;
CurrentCount = 0;
HasStarted = false;
CurrentValue = string.Empty;
}
public string LastLetter { get; private set; }
public int CurrentCount { get; private set; }
public bool HasStarted { get; private set; }
public string CurrentValue { get; private set; }
public override string ToString()
{
return $"LastLetter: {LastLetter}, CurrentCount: {CurrentCount}, HasStarted: {HasStarted}, CurrentValue: {CurrentValue}";
}
public void AddLetter(string letter)
{
CurrentCount = 0;
LastLetter = letter;
HasStarted = true;
}
public int AddDigit(string digit)
{
if (!HasStarted)
{
throw new InvalidOperationException($"The input must start with a letter, not a digit");
}
if (!int.TryParse(digit, out var num))
{
throw new InvalidOperationException($"Digit passed to {nameof(AddDigit)} ({digit}) is not a number");
}
CurrentCount = CurrentCount * 10 + num;
return CurrentCount;
}
public string GetValue()
{
if (string.IsNullOrEmpty(LastLetter))
{
return string.Empty;
}
CurrentValue = new string(LastLetter[0], CurrentCount);
return CurrentValue;
}
}
You'll notice it's got some stuff in there for debugging (example, the ToString override and the CurrentValue property)
Once you have that, the decoder is easy, it just recurses over the string it's given (along with (initially) a freshly constructed State instance):
private string Decode(string input, State state)
{
if (input.Length == 0)
{
_buffer.Append(state.GetValue());
return _buffer.ToString();
}
var head = input[0];
var tail = input.Substring(1);
var headString = head.ToString();
if (char.IsDigit(head))
{
state.AddDigit(headString);
}
else // it's a character
{
_buffer.Append(state.GetValue());
state.AddLetter(headString);
}
Decode(tail, state);
return _buffer.ToString();
}
I did this in a simple Windows Forms app, with a text box for input, a label for output and a button to crank her up:
const string NotAllowedPattern = #"[^a-zA-Z0-9]";
private static Regex NotAllowedRegex = new Regex(NotAllowedPattern);
private StringBuilder _buffer = new StringBuilder();
private void button1_Click(object sender, EventArgs e)
{
if (textBox1.Text.Length == 0 || NotAllowedRegex.IsMatch(textBox1.Text))
{
MessageBox.Show(this, "Only Letters and Digits Allowed", "Bad Input", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
label1.Text = string.Empty;
_buffer.Clear();
var result = Decode(textBox1.Text, new State());
label1.Text = result;
}
Yeah, there's a Regex there, but it's just to make sure that the input is valid; it's not involved in calculating the output.

C# foreach with string - ignore last character

I have a methode who add numbers together parsed from a string.
If user type : "52+7+1", it will result in (60)
"52++2+3", "52+c+2", "+52+2", "52+2++", ... won't work.
My code works almost perfectly, expect... it ignores the last character. It works when, in the code I add "+0" to the string but of course it brokes the condition who prevent the user to type "+" as a last character.
public int addFromString(string str)
{
bool valid_str = true;
bool current_char_numeric = false;
string unparsedNumber = "";
int parsedNumber = 0;
List<int> parsedNumbers = new List<int>();
if (string.IsNullOrEmpty(chaine))
chaine_valide = false;
else
{
if (!int.TryParse(str[0].ToString(), out parsedNumber))
valid_str = false;
if (!int.TryParse(str[str.Length - 1].ToString(), out parsedNumber))
valid_str = false;
}
foreach (char c in str)
{
current_char_numeric = int.TryParse(c.ToString(), out parsedNumber);
if (current_char_numeric)
unparsedNumber += c;
else if(c == '+')
{
int.TryParse(unparsedNumber, out parsedNumber);
parsedNumbers.Add(parsedNumber);
if (str.IndexOf(c) < str.Length && str.ElementAt(str.IndexOf(c) + 1) == '+')
valid_str = false;
//Just in case :
unparsedNumber = "";
current_char_numeric = int.TryParse(c.ToString(), out parsedNumber);
}
else valid_str = false;
}
int result = 0;
if(valid_str) { foreach(int n in parsedNumbers) { result += n; } }
return result;
}
So if I type : "52+2" I get 52
If I type : "52+2+6" I get 54
It misses the last value because you only add numbers if you find a +. So for the last unparsedNumber you never enter the else if (c == '+') block.
Let me suggest an more compact solution:
public int addFromString(string str)
{
string trimmed = str.Trim();
if (str.StartsWith("+") || str.EndsWith("+")) return 0; // invalid -> return immediatly
// split string at "+" and trim parts
string[] numbers = str.Split('+').Select(s => s.Trim()).ToArray();
int result = 0;
foreach(string number in numbers)
{
int n;
if (!int.TryParse(number, out n)) return 0; // invalid -> return
result += n;
}
return result;
}
You only add a number to the numbers collection when you encounter a +.
What about the last number? There's no '+' after that.
You should add unparsedNumber to parsedNumbers for the last number too.
You can do a more elegant version:
public int addFromString(string str)
{
int parsedNumber = 0;
int result = 0;
if (string.IsNullOrEmpty(chaine))
{
return result;
}
else
{
if (!int.TryParse(str[0].ToString(), out parsedNumber)
|| !int.TryParse(str[str.Length - 1].ToString(), out parsedNumber))
{
return result;
}
}
try
{
result = str.Split(new char[] { '+' }).Select(s => Convert.ToInt32(s)).Sum();
}
finally
{
return result;
}
}
It seems that all you want is Split:
string source = "52+7+1";
int sum = 0; // initial sum is 0
bool chaine_valide = true; // the chain is valid (we don't have any counter examples)
// Split on terms: 52, 7, 1
foreach (string term in source.Split('+')) {
int value;
// No need in Trim() etc. - TryParse is smart enough
if (int.TryParse(term, out value))
sum += value; // valid term: add it up
else {
chaine_valide = false; // counter example: term is not a valid integer
break;
}
}
...
Console.Write(chaine_valide ? sum.ToString() : "Invalid formula");
In case of C# 7.0 you can (with a help of out var) simplify the code into
int sum = 0; // initial sum is 0
bool chaine_valide = true; // the chain is valid (we don't have any counter examples)
// Split on terms: 52, 7, 1
foreach (string term in source.Split('+'))
if (int.TryParse(term, out var value))
sum += value; // valid term: add it up
else {
chaine_valide = false; // counter example: term is not a valid integer
break;
}

I want to write a function that takes a string and returns the string with at least 3 digits at the end

If the string passed in already has 3 digits at the end then return unchanged. If the string passed in does not have 3 digits at the end then need to insert zeros before any digits at the end to have 3 digits.
I have done coding where i had put some logic in private static string stringCleaner(string inputString) to implement but its giving this error:
Test:'A12' Expected:'A012' Exception:Index was outside the bounds of the array.
Test:'A12345' Expected:'A12345' Exception:Index was outside the bounds of the array.
Test:'A1B3' Expected:'A1B003' Exception:Index was outside the bounds of the array.
Test:'' Expected:'000' Exception:Object reference not set to an instance of an object.
Test:'' Expected:'000' Actual:'000' Result:Pass
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConvertToCamelCaseCS
{
class Program
{
static void Main(string[] args)
{
List<string[]> testValues = new List<string[]>()
{
new string[]{"A12","A012"},
new string[]{"A12345","A12345"},
new string[]{"A1B3","A1B003"},
new string[]{null, "000"},
new string[]{"", "000"}
};
foreach (string[] testValue in testValues)
{
testStringCleaner(testValue[0], testValue[1]);
}
Console.ReadLine();
}
private static void testStringCleaner(string inputString, string expectedString)
{
try
{
String actualString = stringCleaner(inputString);
String passOrFail = (actualString == expectedString) ? "Pass" : "Fail";
Console.WriteLine("Test:'{0}' Expected:'{1}' Actual:'{2}' Result:{3}", inputString, expectedString, actualString, passOrFail);
}
catch (Exception ex)
{
Console.WriteLine("Test:'{0}' Expected:'{1}' Exception:{2}", inputString, expectedString, ex.Message);
}
}
private static string stringCleaner(string inputString)
{
string result = inputString;
int lengthOfString = result.Length;
int changeIndex = 0;
if (lengthOfString == 0)
{
result = "000";
}
else
{
for (int i = lengthOfString; i >= lengthOfString - 2; i--)
{
char StrTOChar = (char)result[i];
int CharToInt = (int)StrTOChar;
if (CharToInt >= 65 && CharToInt <= 122)
{
changeIndex = i;
break;
}
}
if (lengthOfString == changeIndex + 3)
{
return result;
}
else
{
if (changeIndex == lengthOfString)
{
return result = result + "000";
}
else if (changeIndex + 1 == lengthOfString)
{
return result = result.Substring(0, changeIndex) + "00" + result.Substring(changeIndex + 1, lengthOfString);
}
else if(changeIndex+2==lengthOfString)
{
return result = result.Substring(0, changeIndex) + "0" + result.Substring(changeIndex + 1, lengthOfString);
}
}
}
return result;
}
}
}
You are overcomplicating this a lot from what I can tell among this somewhat confusing question and code.
I would use substring to extract the last 3 characters, and then check that string from the back whether it is a digit using Char.IsDigit. Depending on when you run into a non-digit you add a certain amount of zero using simple string concatenation.
Perhaps try to rewrite your code from scratch now that you probably have a better idea of how to do this.
char StrTOChar = (char)result[i];
Your problem is in this line. (Line: 60)
You used i that starts from result.Length. And, results[result.Length] is outside of the bounds of the array. You must use it lower than the length of the array.
Let's implement:
private static string stringCleaner(string value) {
// let's not hardcode magic values: 3, "000" etc. but a have a constant
const int digits_at_least = 3;
// special case: null or empty string
if (string.IsNullOrEmpty(value))
return new string('0', digits_at_least);
int digits = 0;
// let's count digits starting from the end
// && digits < digits_at_least - do not loop if we have enough digits
// (value[i] >= '0' && value[i] <= '9') - we want 0..9 digits only,
// not unicode digits (e.g. Persian ones) - char.IsDigit
for (int i = value.Length - 1; i >= 0 && digits < digits_at_least; --i)
if (value[i] >= '0' && value[i] <= '9')
digits += 1;
else
break;
if (digits >= digits_at_least) // we have enough digits, return as it is
return value;
else
return value.Substring(0, value.Length - digits) +
new string('0', digits_at_least - digits) + // inserting zeros
value.Substring(value.Length - digits);
}
Tests:
using System.Linq;
...
var testValues = new string[][] {
new string[]{"A12","A012"},
new string[]{"A12345","A12345"},
new string[]{"A1B3","A1B003"},
new string[]{null, "000"},
new string[]{"", "000"}
};
// Failed tests
var failed = testValues
.Where(test => test[1] != stringCleaner(test[0]))
.Select(test =>
$"stringCleaner ({test[0]}) == {stringCleaner(test[0])} expected {test[1]}");
string failedReport = string.Join(Environment.NewLine, failed);
// All failed tests
Console.WriteLine(failedReport);
// All tests and their results
var allTests = testValues
.Select(test => new {
argument = test[0],
expected = test[1],
actual = stringCleaner(test[0]),
})
.Select(test => $"{(test.expected == test.actual ? "passed" : $"failed: f({test.argument}) = {test.actual} expected {test.expected}")}");
string allReport = string.Join(Environment.NewLine, allTests);
Console.WriteLine(allReport);
Outcome (no failedReport and all tests passed):
passed
passed
passed
passed
passed

Convert to double failed if the value is null or empty

I write a method to convert the string into double,here is the code
public double convertToDouble(string number)
{
string temp = number;
if (number.Contains("x"))
{
int locationE = number.IndexOf("x");
string exponent = number.Substring(locationE + 5, number.Length - (locationE + 5));
temp = number.Substring(0, locationE - 1) + "E" + exponent;
}
return Convert.ToDouble(temp);
}
But if the temp variable passed in as null or empty string, the conversion will fail. How could i write this part.
Why do you want to write a new method for this purpose, while you could use the more safest one, double.TryParse.
double number;
// The numberStr is the string you want to parse
if(double.TryParse(numberStr, out number))
{
// The parsing succeeded.
}
If you don't like the above approach and you want to stick with your method, then the only option I see is to throw an exception.
public double convertToDouble(string number)
{
if(string.IsNullOrWhiteSpace(number))
{
throw new ArgumentException("The input cannot be null, empty string or consisted only of of white space characters", "number");
}
string temp = number;
if (number.Contains("x"))
{
int locationE = number.IndexOf("x");
string exponent = number.Substring(locationE + 5, number.Length - (locationE + 5));
temp = number.Substring(0, locationE - 1) + "E" + exponent;
}
return Convert.ToDouble(temp);
}
Depends on what do you want to happen when the number can't be converted.
You could try this:
public double convertToDouble(string number)
{
string temp = number;
if (number.Contains("x"))
{
int locationE = number.IndexOf("x");
string exponent = number.Substring(locationE + 5, number.Length - (locationE + 5));
temp = number.Substring(0, locationE - 1) + "E" + exponent;
}
double returnDouble;
if(double.TryParse(temp, out returnDouble))
return returnDouble;
// Return whatever or throw an exception, etc.
return 0;
}
As a further tip, it looks like you are converting something like [number] x 10^[exponent] to [number]E[exponent], if so, this could be easily converted as:
public double convertToDouble(string number)
{
if(String.IsNullOrWhiteSpace(number))
return 0; // or throw exception, or whatever
// Instead of all those "IndexOf" and "Substrings"
var temp = number.Replace("x 10^", "E");
double returnDouble;
if(double.TryParse(temp, out returnDouble))
return returnDouble;
// Return whatever or throw an exception, etc.
return 0;
}
This could be further prettified without hurting readability, but I'll leave that to you

Function to return middle letter or letters of a palindrome

I'm writing a function to test if a string is a palindrome or not and I am wondering how to return the middle letter or letters if the string is indeed a palindrome?
Here's what i have so far:
My bool to check if the string is a palindrome:
public static bool IsPalindrome(string input)
{
int i = 0;
int j = input.Length - 1;
while (true)
{
if (i > j)
{
return true;
}
char a = input[i];
char b = input[j];
if (!a.Equals(b))
{
return false;
}
i++;
j--;
}
}
And here is where I'd like to be able to print out the middle letter(s):
while (true)
{
Console.Clear();
Regex myRegex = new Regex("[ ;:,.-?'!\"]");
string userInput = String.Empty;
Console.WriteLine("Please enter sentence or phrase");
userInput = Console.ReadLine();
Console.WriteLine();
if (IsPalindrome(myRegex.Replace(userInput, string.Empty).ToLower()))
{
Console.WriteLine("True");
Console.WriteLine("Press any key to continue");
}
else
{
Console.WriteLine("False");
Console.WriteLine("Press any key to continue");
}
Console.ReadLine();
}
Here's another implementation to return the middle letter(s):
public string MiddleLettersOf(string s)
{
if (s.Length == 0)
return "";
if ((s.Length & 1) == 1) // Odd length?
return s.Substring(s.Length/2, 1);
return s.Substring(s.Length/2-1, 2);
}
(This assumes that passing a null string is an error and therefore I allow it to throw a NullReferenceException.)
By the way, a simple (but not the most efficient) way of checking a string to see if it's a palindrome is:
public static bool IsPalindrome(string s)
{
return s.SequenceEqual(s.Reverse());
}
You can generalise that test to any IEnumerable:
public static bool IsPalindrome<T>(IEnumerable<T> s)
{
return s.SequenceEqual(s.Reverse());
}
but the flaw with that code is that s is enumerated twice, which might be A Bad Thing.
Try this:
private static string GetMiddleLetters(string input)
{
//Find the middle point
var mid = input.Length / 2.0;
//If it's odd, we take 1 letter, if it's even, we take 2
var numToTake = (mid == (int)mid) ? 2 : 1;
//Round up from the middle, and subtract one (as Substring is 0-indexed)
var startIndex = (int)Math.Ceiling(mid) - 1;
return input.Substring((int)Math.Ceiling(mid) - 1, numToTake);
}
And use it like this:
var fixedString = myRegex.Replace(userInput, string.Empty).ToLower();
if (IsPalindrome(fixedString))
{
Console.WriteLine(GetMiddleLetters(fixedString));
//Rest of the code here...
}
Something like that:
public static String MiddleLetters(string value) {
if (String.IsNullOrEmpty(value))
return value; // middle of the "null" is supposed to be null
return value.Length % 2 == 0 ?
value.Substring(value.Length / 2 - 1, 2)
: value.Substring(value.Length / 2, 1);
}
To test for being palindrome:
public static bool IsPalindrome(string value) {
if (String.IsNullOrEmpty(value))
return true; // or false, or throw an exception
//TODO: are you looking for case sensitive or case insensitive palindromes?
for (int i = 0; i < value.Length / 2; ++i)
if (value[i] != value[value.Length - i - 1])
return false;
return true;
}

Categories