if i have this string:
12345 = true
123a45 = false
abcde = false
how to do it in C#?
Regex.IsMatch(sinput,#"\d+");
to match a string containing just digits.
If you forgot an optional digit in the question,
use this:
Regex.IsMatch("+12345", #"[+-]?\d+");
If you want to avoid RegEx, then you can use the built-in char methods:
bool allDigits = s.All(c => char.IsDigit(c));
This is the code for checking only letters in string in C#. You can modify it upto your need.
using System;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string MyString = "A #string & and 2.";
Console.WriteLine(MyString);
for (int charpos = 0; charpos < MyString.Length; charpos++)
{
Console.WriteLine(Char.IsLetter(MyString, charpos));
}
//Keep the console on screen
Console.WriteLine("Press any key to quit.");
Console.ReadKey();
}
}
}
private bool ContainsText(string input)
{
for (int i = 0; i < input.Length; i++)
{
if (((int) input[i] >= 65 && (int) input[i] <= 90) || ((int) input[i] >= 97 && (int) input[i] <= 177))
return true;
}
return false;
}
Running:
MessageBox.Show(ContainsText("abc").ToString());
MessageBox.Show(ContainsText("123").ToString());
MessageBox.Show(ContainsText("123b23").ToString());
returns True, False, True respectively
int myNumber;
if( int.TryParse(myString, out myNumber) == true )
{
// is a number and myNumber contains it
}
else
{
// isn't a number
}
if it's a BIG number, use long or double or.... instead of int.
int.TryParse or long.TryParse.
You can also use Regex for any length of number:
if (Regex.IsMatch(str, "^[0-9]+$"))
// ...
Related
I am making password validation method using console app and got stuck with comparing two chars. Is there a way to compare two chars without using toUpper() or toLower() method.
For example if i am comparing these two chars
char c1 = 'a', c2 = 'A';
bool result = c1.Equals(c2);
I want result to be true.
I've tried using toLower() method but i have a problem.
This is my password validation Method.
private static bool PasswordValidation(string input)
{
if(!(input.Length>=8&&input.Length<=15))
{
Console.WriteLine("min 8 characters max 15");
return false;
}
int specialCharacters = 0, uppLatter = 0, lowerLatter = 0;
char[] charArray = input.ToCharArray();
for (int i = 0; i < charArray.Length; i++)
{
char ch = charArray[i];
if (char.IsWhiteSpace(ch))
{
Console.WriteLine("can't use space in password");
return false;
}
if (!char.IsLetterOrDigit(ch))
specialCharacters++;
if (char.IsUpper(ch))
uppLatter++;
if (char.IsLower(ch))
lowerLatter++;
if (i < charArray.Length - 1)
{
if (char.ToLower(charArray[i])==char.ToLower(charArray[++i]))
{
Console.WriteLine("same characters");
return false;
}
if(char.IsDigit(charArray[i])&&char.IsDigit(charArray[++i]))
{
int sum = Convert.ToInt32(charArray[i]) + Convert.ToInt32(charArray[++i]);
if(sum==input.Length)
{
Console.WriteLine("sum is equal to length");
return false;
}
}
}
}
if (specialCharacters == 0)
{
Console.WriteLine("at lesast one special character is required");
return false;
}
if (uppLatter == 0)
{
Console.WriteLine("at lesast one upper latter is required");
return false;
}
if (lowerLatter == 0)
{
Console.WriteLine("at lesast one lower character is required");
return false;
}
var repeats = input.GroupBy(s1 => s1)
.Where(s1 => s1.Count() > 3)
.Select(s1 => s1).ToArray();
if (repeats.Length > 0)
{
Console.WriteLine("one character can't be repeted more than 3 times");
return false;
}
return true;
}
As you can see i've used integers to count the number of lower and upper letters, as well as special characters but this will only work for the first character of input string.
Soon as i hit this
if (char.ToLower(charArray[i])==char.ToLower(charArray[++i]))
line of code, all hell breaks loose.
Any suggestion is helpful.
Thank you for your time.
You can do this:
char c1 = 'a', c2 = 'A';
bool result = String.Equals(c1.ToString(), c2.ToString(), StringComparison.OrdinalIgnoreCase);
I need to write a method/function using C# that accepts two parameters: one string and one character. The function should return true if for every instance of the character in the string, there is another instance of the character immediately to the left or right. In other words, the function should return true only if the character appears in the string only as a pair. I can't seem to figure it out.
Examples:
Manipulation("abcdeefghi", 'e') -> true
Manipulation("abcdeeefghi", 'e') -> false
Currently I have:
public bool Manipulation(string strParam, char[] charParam)
{
if (strParam.Contains(charParam))
{
return true;
}
return false;
}
I already know this doesn't work but I was trying to at least get a return of true if I could find it at least once in the string.
Well one way of looking at it would be traversing the string once, just checking if a run of your char appears: Note that this answer requires the char to appear exactly twice, for example:
IsPairOccurence("abcdeefghi", 'e') -> true
IsPairOccurence("abcdeeefghi", 'e') -> false
Checking if it appears at least twice is easier, as shown by #aditya's answer.
private bool IsPairOccurence(string s, char c) {
int occurence = 0; // the number of consecutive occurences
for (int i = 0; i < s.Length; i++) {
if (s[i] == c) { // If you encounter the character
occurrence++; // Increase the counter
} else { // If another character occurs
if (occurence == 2) {
// Check if the number of consecutive occurences was exactly 2
return true;
}
// If not, reset the counter
occurence = 0;
}
}
return occurence == 2;
}
This answer is surprisingly easy to generalise for any case, e.g. if you want to count exactly occurrences of 3 consecutive or 24 consecutive chars. You can even generalise it for n consecutive characters:
private bool DoesOccurNTimes(string s, char c, int n) {
int occurence = 0; // the number of consecutive occurences
for (int i = 0; i < s.Length; i++) {
if (s[i] == c) { // If you encounter the character
occurrence++; // Increase the counter
} else { // If another character occurs
if (occurence == n) {
// Check if the number of consecutive occurences was exactly n
return true;
}
// If not, reset the counter
occurence = 0;
}
}
// Check for the end of the string
return occurence == n;
}
Now IsPairOccurence(s, c) is simply DoesOccurNTimes(s, c, 2).
This should work for you, but as #ThreeFx suggested in the comments you need to mention if it is only for pairs or for triplets and so on as well.
Nevertheless, the answer assumes the latter case:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StackQuestionAnswersCS
{
class Program
{
static void Main(string[] args)
{
if (checkContains("HelloWorld", 'o')) Console.WriteLine("Contains Character");
else Console.WriteLine("Does not contain character");
Console.ReadLine();
}
static bool checkContains(string input, char character)
{
for (int i = 0; i < input.Length - 1; i++)
{
if (input[i] == character && input[i+1] == character) return true;
}
return false;
}
}
}
private static bool Manipulation(string p, char c)
{
return p.Contains(c.ToString() + c.ToString()) && !p.Replace(c.ToString() + c.ToString(), "").Contains(c);
}
It tells me that it can't convert int to bool.
Tried TryParse but for some reason the argument list is invalid.
Code:
private void SetNumber(string n)
{
// if user input is a number then
if (int.Parse(n))
{
// if user input is negative
if (h < 0)
{
// assign absolute version of user input
number = Math.Abs(n);
}
else
{
// else assign user input
number = n;
}
}
else
{
number = 0; // if user input is not an int then set number to 0
}
}
You were probably very close using TryParse, but I'm guessing you forgot the out keyword on the parameter:
int value;
if (int.TryParse(n, out value))
{
}
Just use this:
int i;
bool success = int.TryParse(n, out i);
if the parse was successful, success is true.
If that case i contain the number.
You probably got the out argument modifier wrong before. It has the out modifier to indicate that it is a value that gets initialized within the method called.
You can try with some simple regular expression :
bool IsNumber(string text)
{
Regex regex = new Regex(#"^[-+]?[0-9]*\.?[0-9]+$");
return regex.IsMatch(text);
}
private void SetNumber(string n)
{
int nVal = 0;
if (int.TryParse(n, out nVal))
{
if (nVal < 0)
number = Math.Abs(nVal);
else
number = nVal;
}
else
number = 0;
}
There are a lot of problems with this code:
Using VB-style line comments (') instead of C# slashes
Parse for integer returns an int and not a bool
You should use TryParse with an out value
h does not seem to be valid at all. Is it a type for n?
There are variables that do not seem to be defined in function scope (number) are they defined at class scope?
But try this:
private void SetNumber(string n)
{
int myInt;
if (int.TryParse(n, out myInt)) //if user input is a number then
{
if (myInt < 0) //if user input is negative
number = Math.Abs(n); //assign absolute version of user input
else //else assign user input
number = n;
}
else number = 0; //if user input is not an int then set number to 0
}
You could try something like below using int.TryParse..
private void SetNumber(string n)
{
int parsed = -1;
if (int.TryParse(n, out parsed)) //if user input is a number then
...
The reason there are complaints that it cannot convert an int to a bool is because the return type of int.Parse() is an int and not a bool and in c# conditionals need to evaluate bool values.
int.Parse will give you back an integer rather than a boolean.
You could use int.TryParse as you suggested.
int parsedValue;
if(int.TryParse(n, out parsedValue))
{
}
Well for one thing the inner if statement has an 'h' instead of an 'n' if(h < 0). But TryParse should work there assuming that 'number' is a class variable.
private void SetNumber(string n)
{
int temp;
bool success = Int32.TryParse(n, out temp);
// If conversion successful
if (success)
{
// If user input is negative
if (temp < 0)
number = Math.Abs(temp); // Assign absolute version of user input
else // Assign user input
number = temp;
}
else
{
number = 0;
}
}
int.Parse will convert a string to an integer. Current you have it within an if statement, so its treating the returned value of int.Parse as a bool, which its not.
Something like this will work:
private void SetNumber(string n)
{
int num = 0;
try{
num = int.Parse(n);
number = Math.Abs(num);
}catch(Exception e){
number = 0;
}
}
I did this in the simplest way I knew how.
static void Main(string[] args)
{
string a, b;
int f1, f2, x, y;
Console.WriteLine("Enter two inputs");
a = Convert.ToString(Console.ReadLine());
b = Console.ReadLine();
f1 = find(a);
f2 = find(b);
if (f1 == 0 && f2 == 0)
{
x = Convert.ToInt32(a);
y = Convert.ToInt32(b);
Console.WriteLine("Two inputs r number \n so tha additon of these text box is= " + (x + y).ToString());
}
else
Console.WriteLine("One or tho inputs r string \n so tha concadination of these text box is = " + (a + b));
Console.ReadKey();
}
static int find(string s)
{
string s1 = "";
int f;
for (int i = 0; i < s.Length; i++)
for (int j = 0; j <= 9; j++)
{
string c = j.ToString();
if (c[0] == s[i])
{
s1 += c[0];
}
}
if (s==s1)
f= 0;
else
f= 1;
return f;
}
How can I compare 2 strings in C# ignoring the case, spaces and any line-breaks. I also need to check if both strings are null then they are marked as same.
Thanks!
You should normalize each string by removing the characters that you don't want to compare and then you can perform a String.Equals with a StringComparison that ignores case.
Something like this:
string s1 = "HeLLo wOrld!";
string s2 = "Hello\n WORLd!";
string normalized1 = Regex.Replace(s1, #"\s", "");
string normalized2 = Regex.Replace(s2, #"\s", "");
bool stringEquals = String.Equals(
normalized1,
normalized2,
StringComparison.OrdinalIgnoreCase);
Console.WriteLine(stringEquals);
Here Regex.Replace is used first to remove all whitespace characters. The special case of both strings being null is not treated here but you can easily handle that case before performing the string normalization.
This may also work.
String.Compare(s1, s2, CultureInfo.CurrentCulture, CompareOptions.IgnoreCase | CompareOptions.IgnoreSymbols) == 0
Edit:
IgnoreSymbols: Indicates that the string comparison must ignore symbols, such as
white-space characters, punctuation, currency symbols, the percent
sign, mathematical symbols, the ampersand, and so on.
Remove all the characters you don't want and then use the ToLower() method to ignore case.
edit: While the above works, it's better to use StringComparison.OrdinalIgnoreCase. Just pass it as the second argument to the Equals method.
First replace all whitespace via regular expression from both string and then use the String.Compare method with parameter ignoreCase = true.
string a = System.Text.RegularExpressions.Regex.Replace("void foo", #"\s", "");
string b = System.Text.RegularExpressions.Regex.Replace("voidFoo", #"\s", "");
bool isTheSame = String.Compare(a, b, true) == 0;
If you need performance, the Regex solutions on this page run too slow for you. Maybe you have a large list of strings you want to sort. (A Regex solution is more readable however)
I have a class that looks at each individual char in both strings and compares them while ignoring case and whitespace. It doesn't allocate any new strings. It uses the char.IsWhiteSpace(ch) to determine whitespace, and char.ToLowerInvariant(ch) for case-insensitivity (if required). In my testing, my solution runs about 5x - 8x faster than a Regex-based solution. My class also implements IEqualityComparer's GetHashCode(obj) method using this code in another SO answer. This GetHashCode(obj) also ignores whitespace and optionally ignores case.
Here's my class:
private class StringCompIgnoreWhiteSpace : IEqualityComparer<string>
{
public bool Equals(string strx, string stry)
{
if (strx == null) //stry may contain only whitespace
return string.IsNullOrWhiteSpace(stry);
else if (stry == null) //strx may contain only whitespace
return string.IsNullOrWhiteSpace(strx);
int ix = 0, iy = 0;
for (; ix < strx.Length && iy < stry.Length; ix++, iy++)
{
char chx = strx[ix];
char chy = stry[iy];
//ignore whitespace in strx
while (char.IsWhiteSpace(chx) && ix < strx.Length)
{
ix++;
chx = strx[ix];
}
//ignore whitespace in stry
while (char.IsWhiteSpace(chy) && iy < stry.Length)
{
iy++;
chy = stry[iy];
}
if (ix == strx.Length && iy != stry.Length)
{ //end of strx, so check if the rest of stry is whitespace
for (int iiy = iy + 1; iiy < stry.Length; iiy++)
{
if (!char.IsWhiteSpace(stry[iiy]))
return false;
}
return true;
}
if (ix != strx.Length && iy == stry.Length)
{ //end of stry, so check if the rest of strx is whitespace
for (int iix = ix + 1; iix < strx.Length; iix++)
{
if (!char.IsWhiteSpace(strx[iix]))
return false;
}
return true;
}
//The current chars are not whitespace, so check that they're equal (case-insensitive)
//Remove the following two lines to make the comparison case-sensitive.
chx = char.ToLowerInvariant(chx);
chy = char.ToLowerInvariant(chy);
if (chx != chy)
return false;
}
//If strx has more chars than stry
for (; ix < strx.Length; ix++)
{
if (!char.IsWhiteSpace(strx[ix]))
return false;
}
//If stry has more chars than strx
for (; iy < stry.Length; iy++)
{
if (!char.IsWhiteSpace(stry[iy]))
return false;
}
return true;
}
public int GetHashCode(string obj)
{
if (obj == null)
return 0;
int hash = 17;
unchecked // Overflow is fine, just wrap
{
for (int i = 0; i < obj.Length; i++)
{
char ch = obj[i];
if(!char.IsWhiteSpace(ch))
//use this line for case-insensitivity
hash = hash * 23 + char.ToLowerInvariant(ch).GetHashCode();
//use this line for case-sensitivity
//hash = hash * 23 + ch.GetHashCode();
}
}
return hash;
}
}
private static void TestComp()
{
var comp = new StringCompIgnoreWhiteSpace();
Console.WriteLine(comp.Equals("abcd", "abcd")); //true
Console.WriteLine(comp.Equals("abCd", "Abcd")); //true
Console.WriteLine(comp.Equals("ab Cd", "Ab\n\r\tcd ")); //true
Console.WriteLine(comp.Equals(" ab Cd", " A b" + Environment.NewLine + "cd ")); //true
Console.WriteLine(comp.Equals(null, " \t\n\r ")); //true
Console.WriteLine(comp.Equals(" \t\n\r ", null)); //true
Console.WriteLine(comp.Equals("abcd", "abcd h")); //false
Console.WriteLine(comp.GetHashCode(" a b c d")); //-699568861
//This is -699568861 if you #define StringCompIgnoreWhiteSpace_CASE_INSENSITIVE
// Otherwise it's -1555613149
Console.WriteLine(comp.GetHashCode("A B c \t d"));
}
Here's my testing code (with a Regex example):
private static void SpeedTest()
{
const int loop = 100000;
string first = "a bc d";
string second = "ABC D";
var compChar = new StringCompIgnoreWhiteSpace();
Stopwatch sw1 = Stopwatch.StartNew();
for (int i = 0; i < loop; i++)
{
bool equals = compChar.Equals(first, second);
}
sw1.Stop();
Console.WriteLine(string.Format("char time = {0}", sw1.Elapsed)); //char time = 00:00:00.0361159
var compRegex = new StringCompIgnoreWhiteSpaceRegex();
Stopwatch sw2 = Stopwatch.StartNew();
for (int i = 0; i < loop; i++)
{
bool equals = compRegex.Equals(first, second);
}
sw2.Stop();
Console.WriteLine(string.Format("regex time = {0}", sw2.Elapsed)); //regex time = 00:00:00.2773072
}
private class StringCompIgnoreWhiteSpaceRegex : IEqualityComparer<string>
{
public bool Equals(string strx, string stry)
{
if (strx == null)
return string.IsNullOrWhiteSpace(stry);
else if (stry == null)
return string.IsNullOrWhiteSpace(strx);
string a = System.Text.RegularExpressions.Regex.Replace(strx, #"\s", "");
string b = System.Text.RegularExpressions.Regex.Replace(stry, #"\s", "");
return String.Compare(a, b, true) == 0;
}
public int GetHashCode(string obj)
{
if (obj == null)
return 0;
string a = System.Text.RegularExpressions.Regex.Replace(obj, #"\s", "");
return a.GetHashCode();
}
}
I would probably start by removing the characters you don't want to compare from the string before comparing. If performance is a concern, you might look at storing a version of each string with the characters already removed.
Alternatively, you could write a compare routine that would skip over the characters you want to ignore. But that just seems like more work to me.
You can also use the following custom function
public static string ExceptChars(this string str, IEnumerable<char> toExclude)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < str.Length; i++)
{
char c = str[i];
if (!toExclude.Contains(c))
sb.Append(c);
}
return sb.ToString();
}
public static bool SpaceCaseInsenstiveComparision(this string stringa, string stringb)
{
return (stringa==null&&stringb==null)||stringa.ToLower().ExceptChars(new[] { ' ', '\t', '\n', '\r' }).Equals(stringb.ToLower().ExceptChars(new[] { ' ', '\t', '\n', '\r' }));
}
And then use it following way
"Te st".SpaceCaseInsenstiveComparision("Te st");
Another option is the LINQ SequenceEquals method which according to my tests is more than twice as fast as the Regex approach used in other answers and very easy to read and maintain.
public static bool Equals_Linq(string s1, string s2)
{
return Enumerable.SequenceEqual(
s1.Where(c => !char.IsWhiteSpace(c)).Select(char.ToUpperInvariant),
s2.Where(c => !char.IsWhiteSpace(c)).Select(char.ToUpperInvariant));
}
public static bool Equals_Regex(string s1, string s2)
{
return string.Equals(
Regex.Replace(s1, #"\s", ""),
Regex.Replace(s2, #"\s", ""),
StringComparison.OrdinalIgnoreCase);
}
Here the simple performance test code I used:
var s1 = "HeLLo wOrld!";
var s2 = "Hello\n WORLd!";
var watch = Stopwatch.StartNew();
for (var i = 0; i < 1000000; i++)
{
Equals_Linq(s1, s2);
}
Console.WriteLine(watch.Elapsed); // ~1.7 seconds
watch = Stopwatch.StartNew();
for (var i = 0; i < 1000000; i++)
{
Equals_Regex(s1, s2);
}
Console.WriteLine(watch.Elapsed); // ~4.6 seconds
An approach not optimized for performance, but for completeness.
normalizes null
normalizes unicode, combining characters, diacritics
normalizes new lines
normalizes white space
normalizes casing
code snippet:
public static class StringHelper
{
public static bool AreEquivalent(string source, string target)
{
if (source == null) return target == null;
if (target == null) return false;
var normForm1 = Normalize(source);
var normForm2 = Normalize(target);
return string.Equals(normForm1, normForm2);
}
private static string Normalize(string value)
{
Debug.Assert(value != null);
// normalize unicode, combining characters, diacritics
value = value.Normalize(NormalizationForm.FormC);
// normalize new lines to white space
value = value.Replace("\r\n", "\n").Replace("\r", "\n");
// normalize white space
value = Regex.Replace(value, #"\s", string.Empty);
// normalize casing
return value.ToLowerInvariant();
}
}
I would Trim the string using Trim() to remove all the
whitespace.
Use StringComparison.OrdinalIgnoreCase to ignore case sensitivity ex. stringA.Equals(stringB, StringComparison.OrdinalIgnoreCase)
The question is complicated but I will explain it in details.
The goal is to make a function which will return next "step" of the given string.
For example
String.Step("a"); // = "b"
String.Step("b"); // = "c"
String.Step("g"); // = "h"
String.Step("z"); // = "A"
String.Step("A"); // = "B"
String.Step("B"); // = "C"
String.Step("G"); // = "H"
Until here its quite easy, But taking in mind that input IS string it can contain more than 1 characters and the function must behave like this.
String.Step("Z"); // = "aa";
String.Step("aa"); // = "ab";
String.Step("ag"); // = "ah";
String.Step("az"); // = "aA";
String.Step("aA"); // = "aB";
String.Step("aZ"); // = "ba";
String.Step("ZZ"); // = "aaa";
and so on...
This doesn't exactly need to extend the base String class.
I tried to work it out by each characters ASCII values but got stuck with strings containing 2 characters.
I would really appreciate if someone can provide full code of the function.
Thanks in advance.
EDIT
*I'm sorry I forgot to mention earlier that the function "reparse" the self generated string when its length reaches n.
continuation of this function will be smth like this. for example n = 3
String.Step("aaa"); // = "aab";
String.Step("aaZ"); // = "aba";
String.Step("aba"); // = "abb";
String.Step("abb"); // = "abc";
String.Step("abZ"); // = "aca";
.....
String.Step("zzZ"); // = "zAa";
String.Step("zAa"); // = "zAb";
........
I'm sorry I didn't mention it earlier, after reading some answers I realised that the problem was in question.
Without this the function will always produce character "a" n times after the end of the step.
NOTE: This answer is incorrect, as "aa" should follow after "Z"... (see comments below)
Here is an algorithm that might work:
each "string" represents a number to a given base (here: twice the count of letters in the alphabet).
The next step can thus be computed by parsing the "number"-string back into a int, adding 1 and then formatting it back to the base.
Example:
"a" == 1 -> step("a") == step(1) == 1 + 1 == 2 == "b"
Now your problem is reduced to parsing the string as a number to a given base and reformatting it. A quick googling suggests this page: http://everything2.com/title/convert+any+number+to+decimal
How to implement this?
a lookup table for letters to their corresponding number: a=1, b=2, c=3, ... Y = ?, Z = 0
to parse a string to number, read the characters in reverse order, looking up the numbers and adding them up:
"ab" -> 2*BASE^0 + 1*BASE^1
with BASE being the number of "digits" (2 count of letters in alphabet, is that 48?)
EDIT: This link looks even more promising: http://www.citidel.org/bitstream/10117/20/12/convexp.html
Quite collection of approaches, here is mine:-
The Function:
private static string IncrementString(string s)
{
byte[] vals = System.Text.Encoding.ASCII.GetBytes(s);
for (var i = vals.Length - 1; i >= 0; i--)
{
if (vals[i] < 90)
{
vals[i] += 1;
break;
}
if (vals[i] == 90)
{
if (i != 0)
{
vals[i] = 97;
continue;
}
else
{
return new String('a', vals.Length + 1);
}
}
if (vals[i] < 122)
{
vals[i] += 1;
break;
}
vals[i] = 65;
break;
}
return System.Text.Encoding.ASCII.GetString(vals);
}
The Tests
Console.WriteLine(IncrementString("a") == "b");
Console.WriteLine(IncrementString("z") == "A");
Console.WriteLine(IncrementString("Z") == "aa");
Console.WriteLine(IncrementString("aa") == "ab");
Console.WriteLine(IncrementString("az") == "aA");
Console.WriteLine(IncrementString("aZ") == "ba");
Console.WriteLine(IncrementString("zZ") == "Aa");
Console.WriteLine(IncrementString("Za") == "Zb");
Console.WriteLine(IncrementString("ZZ") == "aaa");
public static class StringStep
{
public static string Next(string str)
{
string result = String.Empty;
int index = str.Length - 1;
bool carry;
do
{
result = Increment(str[index--], out carry) + result;
}
while (carry && index >= 0);
if (index >= 0) result = str.Substring(0, index+1) + result;
if (carry) result = "a" + result;
return result;
}
private static char Increment(char value, out bool carry)
{
carry = false;
if (value >= 'a' && value < 'z' || value >= 'A' && value < 'Z')
{
return (char)((int)value + 1);
}
if (value == 'z') return 'A';
if (value == 'Z')
{
carry = true;
return 'a';
}
throw new Exception(String.Format("Invalid character value: {0}", value));
}
}
Split the input string into columns and process each, right-to-left, like you would if it was basic arithmetic. Apply whatever code you've got that works with a single column to each column. When you get a Z, you 'increment' the next-left column using the same algorithm. If there's no next-left column, stick in an 'a'.
I'm sorry the question is stated partly.
I edited the question so that it meets the requirements, without the edit the function would end up with a n times by step by step increasing each word from lowercase a to uppercase z without "re-parsing" it.
Please consider re-reading the question, including the edited part
This is what I came up with. I'm not relying on ASCII int conversion, and am rather using an array of characters. This should do precisely what you're looking for.
public static string Step(this string s)
{
char[] stepChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray();
char[] str = s.ToCharArray();
int idx = s.Length - 1;
char lastChar = str[idx];
for (int i=0; i<stepChars.Length; i++)
{
if (stepChars[i] == lastChar)
{
if (i == stepChars.Length - 1)
{
str[idx] = stepChars[0];
if (str.Length > 1)
{
string tmp = Step(new string(str.Take(str.Length - 1).ToArray()));
str = (tmp + str[idx]).ToCharArray();
}
else
str = new char[] { stepChars[0], str[idx] };
}
else
str[idx] = stepChars[i + 1];
break;
}
}
return new string(str);
}
This is a special case of a numeral system. It has the base of 52. If you write some parser and output logic you can do any kind of arithmetics an obviously the +1 (++) here.
The digits are "a"-"z" and "A" to "Z" where "a" is zero and "Z" is 51
So you have to write a parser who takes the string and builds an int or long from it. This function is called StringToInt() and is implemented straight forward (transform char to number (0..51) multiply with 52 and take the next char)
And you need the reverse function IntToString which is also implementet straight forward (modulo the int with 52 and transform result to digit, divide the int by 52 and repeat this until int is null)
With this functions you can do stuff like this:
IntToString( StringToInt("ZZ") +1 ) // Will be "aaa"
You need to account for A) the fact that capital letters have a lower decimal value in the Ascii table than lower case ones. B) The table is not continuous A-Z-a-z - there are characters inbetween Z and a.
public static string stepChar(string str)
{
return stepChar(str, str.Length - 1);
}
public static string stepChar(string str, int charPos)
{
return stepChar(Encoding.ASCII.GetBytes(str), charPos);
}
public static string stepChar(byte[] strBytes, int charPos)
{
//Escape case
if (charPos < 0)
{
//just prepend with a and return
return "a" + Encoding.ASCII.GetString(strBytes);
}
else
{
strBytes[charPos]++;
if (strBytes[charPos] == 91)
{
//Z -> a plus increment previous char
strBytes[charPos] = 97;
return stepChar(strBytes, charPos - 1); }
else
{
if (strBytes[charPos] == 123)
{
//z -> A
strBytes[charPos] = 65;
}
return Encoding.ASCII.GetString(strBytes);
}
}
}
You'll probably want some checking in place to ensure that the input string only contains chars A-Za-z
Edit Tidied up code and added new overload to remove redundant byte[] -> string -> byte[] conversion
Proof http://geekcubed.org/random/strIncr.png
This is a lot like how Excel columns would work if they were unbounded. You could change 52 to reference chars.Length for easier modification.
static class AlphaInt {
private static string chars =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
public static string StepNext(string input) {
return IntToAlpha(AlphaToInt(input) + 1);
}
public static string IntToAlpha(int num) {
if(num-- <= 0) return "a";
if(num % 52 == num) return chars.Substring(num, 1);
return IntToAlpha(num / 52) + IntToAlpha(num % 52 + 1);
}
public static int AlphaToInt(string str) {
int num = 0;
for(int i = 0; i < str.Length; i++) {
num += (chars.IndexOf(str.Substring(i, 1)) + 1)
* (int)Math.Pow(52, str.Length - i - 1);
}
return num;
}
}
LetterToNum should be be a Function that maps "a" to 0 and "Z" to 51.
NumToLetter the inverse.
long x = "aazeiZa".Aggregate((x,y) => (x*52) + LetterToNum(y)) + 1;
string s = "";
do { // assertion: x > 0
var c = x % 52;
s = NumToLetter() + s;
x = (x - c) / 52;
} while (x > 0)
// s now should contain the result