I'm trying to compare the ascii value of each character in the input and then I want to shift it with a certain distance and reconvert it to valid character. (using Caesar Ciphering Algorithm)
public void Caesar_Cipher_Optimal(string input, int shift)
{
res = "";
int indx;
byte[] asciiInput = Encoding.ASCII.GetBytes(input);
foreach (byte element in asciiInput)
{
//compare if the current char is between[A-Z]
if(asciiInput[element] >= 65 && asciiInput[element] <= 90)
{
//convert the current value of element to int and add the shift value then mod 90
indx=((Convert.ToInt32(asciiInput[element])) + shift) % 90;
res += Convert.ToChar(indx).ToString();
}
}
}
When I'm testing the code, it's giving me an OutOfRange exception, is it the right way to compare the current ASCII value with what I want?
It's your array access using the value from the foreach that gives the out of range exception, just as SLaks showed.
You don't need to convert the characters to bytes, as you are only dealing with characters that are in the range A to Z. Characters are 16 bit values, and convert easily into their character codes as integers.
You would use modulo 26 rather than modulo 90, otherwise you would end up with characters with character codes from 0 to 64. You can calculate 26 as the difference between 'A' and 'Z' to avoid the magic number.
Doing calculations on the character code directly means that you have to do a check afterwards to clean up out of range values. Instead convert the 65-90 range to 0-25, do the calculation, and convert back. Subtract 65 ('A') from the character code, add the shift, apply the modulo, and add 65 back.
public static string Caesar_Cipher_Optimal(string input, int shift) {
return new String(
input.Where(c => c >= 'A' && c <= 'Z')
.Select(c => (char)((c - 'A' + shift) % ('Z' - 'A' + 1) + 'A'))
.ToArray()
);
}
Here, I fixed some errors in your code so it works now!
public void CaesarCipherOptimal(string input, int shift)
{
var res = "";
byte[] asciiInput = Encoding.ASCII.GetBytes(input);
// Loop for every character in the string, set the value to the element variable
foreach (byte element in asciiInput)
{
if (element >= 65 && element <= 90)
{
var indx = (element + shift - 65) % 26 + 65;
res += (char)indx;
}
}
return res;
}
Here's how you can use it: (probably in your static void Main())
CaesarCipherOptimal("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 10);
Related
I have a task and I have to check how many odd numbers there are. For example:
cw(string[54]); //37 42 44 61 62
From this I need to get how many odd numbers are in this string. The only way I figured out was to cut the string into 5 ints so int 1 is 37, 2 is 42 and so on. But that is a really long and slow process even with methods.
Any help, or shall I stick with the "cutting" which looks something like this:
for (int y = 0; y < all_number.Length; y++)
{
for (int x = 0; x < 5; x++)
{
cutter = all_number[y];
placeholder = cutter.IndexOf(" ");
final[x] = Convert.ToInt32(cutter.Remove(placeholder));
}
}
This one is for the first numbers, so at 37 42 44 61 62 final would be 37.
I'd start by using an outer foreach loop rather than referring array values by index, unless the index is important (which it doesn't look like it is here).
I'd then use string.Split to split each string by spaces, and then LINQ to sum the odd numbers.
For example:
foreach (string line in lines)
{
var oddSum = line.Split(' ')
.Select(int.Parse) // Parse each chunk
.Where(number => (number & 1) == 1) // Filter out even values
.Sum(); // Sum all the odd values
// Do whatever you want with the sum of the odd values for this line
}
If you actually only want to count the odd numbers, you can use the overload of Count that accepts a predicate:
foreach (string line in lines)
{
var oddCount = line.Split(' ')
.Select(int.Parse) // Parse each chunk
.Count(number => (number & 1) == 1) // Count the odd values
// Do whatever you want with the count of the odd values for this line
}
Note that this will throw an exception (in int.Parse) at the first non-integer value encountered. That may well be fine, but you can use int.TryParse to avoid the exception. That's harder to use with LINQ though; please specify how you want them handled if you need this functionality.
First of all, use the built in tools you have available.
To split a string by a predefined character, use Split:
var numbers = allNumbersString.Split(' ');
Now you have an array of strings, each holding a string representation of what we hope is a number.
Now we need to extract numbers out of each string. The safest way to do this is using int.TryParse:
foreach (var n in numbers)
{
if (int.TryParse(out var number)
{
//ok we got a number
}
else
{
//we don't. Do whatever is appropriate:
//ignore invalid number, log parse failure, throw, etc.
}
}
And now, simply return those that are odd: number % 2 != 0;
Putting it all together:
public static IEnumerable<int> ExtractOddNumbers(
string s
char separator)
{
if (s == null)
throw new ArgumentNullException(name(s));
foreach (var n in s.Split(separator))
{
if (int.TryParse(out var number)
{
if (number % 2 != 0)
yield return number;
}
}
}
So, if you want to know how many odd numbers there are in a given string, you would do:
var countOfOddNumbers = ExtractOddNumbers(s, ' ').Count();
The good thing about this approach is that now, its easily extensible. A small modification to our current method makes it a whole lot more powerful:
public static IEnumerable<int> ExtractNumbers(
string s
char separator
Func<int, bool> predicate)
{
if (s == null)
throw new ArgumentNullException(name(s));
foreach (var n in s.Split(separator))
{
if (int.TryParse(out var number)
{
if (predicate(number))
yield return number;
}
}
}
See what we've done? We've made the filtering criteria one more argument of the method call; now you can extract numbers based on any condition. Odd numbers? ExtractNumbers(s, ' ', n => n % 2 != 0). Multiples of 7? ExtractNumbers(s, ' ', n => n % 7 == 0). Greater than 100? ExtractNumbers(s, ' ', n => n > 100), etc.
As mentioned by others, the Split method is what you're after.
if you want the count of odd numbers then you can accomplish the task like so:
var oddCount = lines.SelectMany(line => line.Split(' ')) // flatten
.Select(int.Parse) // parse the strings
.Count(n => n % 2 != 0); // count the odd numbers
or if you want the summation you can do:
var oddSum = lines.SelectMany(line => line.Split(' '))// flatten
.Select(int.Parse) // parse the strings
.Where(n => n % 2 != 0)// retain the odd numbers
.Sum();// sum them
This assumes there will be no invalid characters in the string, otherwise, you'll need to perform checks with the Where clause prior to proceeding.
An alternative would be to loop over the string's characters and, if the current character is a space or the end of the string and the previous character is '1', '3', '5', '7' or '9' (odd numbers end with an odd figure), increase the count.
This allows the string to contain numbers that are much bigger than ints, does not allocate new memory (as with String.Split) and doesn't require the parsing of ints. It does assume a valid string with valid numbers:
var count = 0;
for(var i = 1; i < cw.Length; i++)
{
int numberIndex = -1;
if(i == cw.Length - 1) numberIndex = i;
if(cw[i] == ' ') numberIndex = i - 1;
if(numberIndex != -1)
{
if(cw[numberIndex] == '1' || cw[numberIndex] == '3' ||
cw[numberIndex] == '5' || cw[numberIndex] == '7' ||
cw[numberIndex] == '9')
{
count++;
}
}
}
I want to split my string called: invoerstring after a variable amount of characters (n is the number of characters when the string needs to be split)..
If the string length is shorter then the variable n, spaces need to be added until the string length = n. The result needs to be shown in a textfield called uitvoer.
This is what so far:
string invoerstring = invoer.Text;
if (invoerstring.Length < n)
{
invoerstring += "";
char[] myStrChars = invoerstring.ToCharArray();
}
if (invoerstring.Length == n)
{
string [] blok = invoerstring.Split();
foreach (string word in blok)
{
uitvoer.Text = word;
}
}
EDIT:
The solutions given above aren't completely doing the job for me, maybe it helps when I post the exercise:
|| crypt n m d text || text is padded with spaces until its length is
a multiple of n || the characters in text are circulary shifted in the
alphabet by the displacement d || example: if d = 1 then 'a' -> 'b' ,
'b' -> 'c' .... etc... 'z' -> 'a' || text is divided in blocks of
length n characters || inside every block of n the characters are
circulary shifted m times to the left || the shifted groups are
concatenated
I already solved the m and d only have to solve the n.
The solutions given above aren't completely doing the job for me, maybe it helps when I post the exercise:
|| crypt n m d text
|| text is padded with spaces until its length is a multiple of n
|| the characters in text are circulary shifted in the alphabet by the displacement d
|| example: if d = 1 then 'a' -> 'b' , 'b' -> 'c' .... etc... 'z' -> 'a'
|| text is divided in blocks of length n characters
|| inside every block of n the characters are circulary shifted m times to the left
|| the shifted groups are concatenated
I already solved the m and d only have to solve the n.
Here's the code you want, no need to go through a character array:
public static string EnsureExactLength(this string s, int length)
{
if (s == null)
throw new ArgumentNullException("null");
return s.PadRight(length).Substring(0, length);
}
You call it like this:
string s = "Test string".EnsureExactLength(4); // will return "Test"
string s = "Te".EnsureExactLength(4); // will return "Te "
You can find an example LINQPad program here.
Okay, I'm honestly not sure what the code you have above is doing because I see calls like Split() without any parameters, and stuff. But to meet the requirements, this one line should do:
string invoerstring = invoer.Text.PadRight(n, ' ').Substring(0, n);
the PadRight will make sure it's as long as n and the Substring will then return the portion of the string up to n.
If you then wanted that string in an array, because I see you have one at the end, you could do this:
invoerstring.ToArray();
Here is a LinqPad script:
void Main()
{
const string TEXT = "12345ABCDE12345ABCDE1234";
const int LENGTH = 5;
const char PAD = '#';
Enumerable.Range(0, TEXT.Length / LENGTH)
.Union(TEXT.Length < LENGTH ? new int[] { 1 } : new int[] {})
.Select((index, item) => TEXT.Length < LENGTH
? TEXT.PadRight(LENGTH, PAD)
: TEXT.Substring(index * LENGTH, LENGTH))
.Concat(TEXT.Length % LENGTH != 0
? new string[] { TEXT.Substring(TEXT.Length - (TEXT.Length % LENGTH)).PadRight(LENGTH, PAD) }
: new string[] { })
.Dump();
}
numbers[i] = numbers[i] * 2;
if (numbers[i] >= 10)
{
string t = numbers[i].ToString();
Console.WriteLine(t[0] + " plus " + t[1]+" = "+quersumme(t).ToString());
numbers[i] = Convert.ToInt32(t[0]) + Convert.ToInt32(t[1]);
}
public int quersumme(string n)
{
return n[0] + n[1];
}
The function returns 101 when I enter 7. But 7 * 2 = 14 and quersumme should do 1+4 = 5
t[0] is the character '1', and t[1] is the character '4', which is translated to 49 + 52, hence 101. Check out an ASCII chart to see what I'm talking about.
You could try using the Char.GetNumericValue() function:
return (int)Char.GetNumericValue(n[0]) + (int)Char.GetNumericValue(n[1]);
You're currently summing the Unicode code points - '1' is 49, and '4' is 52, hence the 101. You want to take the digit value of each character.
If you know that the digits will be in the range '0'-'9', the simplest way of doing that is just to subtract '0' and to use the LINQ Sum method to sum each value:
public int SumDigits(string n)
{
return n.Sum(c => c - '0');
}
Or you could use Char.GetNumericValue(), but that returns double because it also copes with characters such as U+00BD: ½.
Try converting n[0] and n[1] to separate int32's in your quersomme function
You are doing string concatenation in quesumme method.
Should be:
public int quersumme(string n)
{
return (int)Char.GetNumericValue(n[0]) + (int)Char.GetNumericValue(n[1]);
}
It looks to me like you are trying to enumerate the digits in an int.
Try this to avoid slow and cumbersome parsing and conversion. (Its all relative, I haven't tested performance.)
static IEnumerable<int> EnumerateDigits(int value, int baseValue = 10)
{
while (value > 0)
{
yield return value % baseValue;
value = value / baseValue
}
}
Then, if you want to switch the order into an array
var t = EnumerateDigits(numbers[i]).Reverse().ToArray();
But, if you just want to sum the digits.
var checksum = EnumerateDigits(numbers[i]).Sum()
I would like to generate a code like goo.gl and jsfiddle websites (http://jsfiddle.net/XzKvP/).
I tried different things that give me too large of a guid, a repeating alphanumeric code, etc.
I'm thinking I should be able to generate an alphanumeric code based on the Primary Key in my database table. This way it will be non-repeating? The PK is an auto-incremented integer by 1. But not sure that's how it should be done.
I want the code to look random, but it does NOT have to be.
For example, I do NOT want item 1234 in my database to be BCDE and the 1235 item to be BCDF.
Examples:
Notice how the url http://jsfiddle.net/XzKvP/ has a unique 5 character code XzKvP associated to the page. I want to be able to generate the same type of code.
goo.gl does it too: http://goo.gl/UEhtg has UEhtg
How is this done?
The solutions based on a random substring are no good because the outputs will collide. It may happen prematurely (with bad luck), and it will eventually happen when the list of generated values grows large. It doesn't even have to be that large for the probability of collisions to become high (see birthday attack).
What's good for this problem is a pseudo random permutation between the incrementing ID and its counterpart that will be shown in the URL. This technique guarantees that a collision is impossible, while still generating into an output space that is as small as the input space.
Implementation
I suggest this C# version of a Feistel cipher with 32 bits blocks, 3 rounds and a round function that is inspired by pseudo-random generators.
private static double RoundFunction(uint input)
{
// Must be a function in the mathematical sense (x=y implies f(x)=f(y))
// but it doesn't have to be reversible.
// Must return a value between 0 and 1
return ((1369 * input + 150889) % 714025) / 714025.0;
}
private static uint PermuteId(uint id)
{
uint l1=(id>>16)&65535;
uint r1=id&65535;
uint l2, r2;
for (int i = 0; i < 3; i++)
{
l2 = r1;
r2 = l1 ^ (uint)(RoundFunction(r1) * 65535);
l1 = l2;
r1 = r2;
}
return ((r1 << 16) + l1);
}
To express the permuted ID in a base62 string:
private static string GenerateCode(uint id)
{
return ToBase62(PermuteId(id));
}
The Base62 function is the same as the previous answer except that is takes uint instead of int (otherwise these functions would have to be rewritten to deal with negative values).
Customizing the algorithm
RoundFunction is the secret sauce of the algorithm. You may change it to a non-public version, possibly including a secret key. The Feistel network has two very nice properties:
even if the supplied RoundFunction is not reversible, the algorithm guarantees that PermuteId() will be a permutation in the mathematical sense (wich implies zero collision).
changing the expression inside the round function even lightly will change drastically the list of final output values.
Beware that putting something too trivial in the round expression would ruin the pseudo-random effect, although it would still work in terms of uniqueness of each PermuteId output. Also, an expression that wouldn't be a function in the mathematical sense would be incompatible with the algorithm, so for instance anything involving random() is not allowed.
Reversability
In its current form, the PermuteId function is its own inverse, which means that:
PermuteId(PermuteId(id))==id
So given a short string produced by the program, if you convert it back to uint with a FromBase62 function, and give that as input to PermuteId(), that will return the corresponding initial ID. That's pretty cool if you don't have a database to store the [internal-ID / shortstring] relationships: they don't actually need to be stored!
Producing even shorter strings
The range of the above function is 32 bits, that is about 4 billion values from 0 to 2^32-1. To express that range in base62, 6 characters are needed.
With only 5 characters, we could hope to represent at most 62^5 values, which is a bit under 1 billion. Should the output string be limited to 5 characters, the code should be tweaked as follows:
find N such that N is even and 2^N is as high as possible but lower than 62^5. That's 28, so our real output range that fits in 62^5 is going to be 2^28 or about 268 million values.
in PermuteId, use 28/2=14 bits values for l1 and r1 instead of 16 bits, while being careful to not ignore a single bit of the input (which must be less than 2^28).
multiply the result of RoundFunction by 16383 instead of 65535, to stay within the 14 bits range.
at the end of PermuteId, recombine r1 and l1 to form a 14+14=28 bits value instead of 32.
The same method could be applied for 4 characters, with an output range of 2^22, or about 4 million values.
What does it look like
In the version above, the first 10 produced strings starting with id=1 are:
cZ6ahF
3t5mM
xGNPN
dxwUdS
ej9SyV
cmbVG3
cOlRkc
bfCPOX
JDr8Q
eg7iuA
If I make a trivial change in the round function, that becomes:
ey0LlY
ddy0ak
dDw3wm
bVuNbg
bKGX22
c0s5GZ
dfNMSp
ZySqE
cxKH4b
dNqMDA
You can think of the five-letter code as a number in base-62 notation: your "digits" are 26 lowercase and 26 uppercase letters, and digits from 0 to 9. (26+26+10) digits in total. Given a number from 0 to 62^5 (which equals 916132832) (say, your primary key) you can do the conversion to a five-digit base-62 as follows:
private static char Base62Digit(int d) {
if (d < 26) {
return (char)('a'+d);
} else if (d < 52) {
return (char)('A'+d-26);
} else if (d < 62) {
return (char)('0'+d-52);
} else {
throw new ArgumentException("d");
}
}
static string ToBase62(int n) {
var res = "";
while (n != 0) {
res = Base62Digit(n%62) + res;
n /= 62;
}
return res;
}
private static int Base62Decode(char c) {
if (c >= '0' && c <= '9') {
return 52 + c - '0';
} else if (c >= 'A' && c <= 'Z') {
return 26 + c - 'A';
} else if (c >= 'a' && c <= 'z') {
return c - 'a';
} else {
throw new ArgumentException("c");
}
}
static int FromBase62(string s) {
return s.Aggregate(0, (current, c) => current*62 + Base62Decode(c));
}
Here is how to generate cryptographically strong random numbers (you need to add a reference to System.Security):
private static readonly RNGCryptoServiceProvider crypto =
new RNGCryptoServiceProvider();
private static int NextRandom() {
var buf = new byte[4];
crypto.GetBytes(buf);
return buf.Aggregate(0, (p, v) => (p << 8) + v) & 0x3FFFFFFF;
}
This is what I ended up doing
(Updated since Daniel Vérité's answer):
class Program
{
private static double RoundFunction(uint input)
{
// Must be a function in the mathematical sense (x=y implies f(x)=f(y))
// but it doesn't have to be reversible.
// Must return a value between 0 and 1
return ((1369 * input + 150889) % 714025) / 714025.0;
}
private static char Base62Digit(uint d)
{
if (d < 26)
{
return (char)('a' + d);
}
else if (d < 52)
{
return (char)('A' + d - 26);
}
else if (d < 62)
{
return (char)('0' + d - 52);
}
else
{
throw new ArgumentException("d");
}
}
private static string ToBase62(uint n)
{
var res = "";
while (n != 0)
{
res = Base62Digit(n % 62) + res;
n /= 62;
}
return res;
}
private static uint PermuteId(uint id)
{
uint l1 = (id >> 16) & 65535;
uint r1 = id & 65535;
uint l2, r2;
for (int i = 0; i < 3; i++)
{
l2 = r1;
r2 = l1 ^ (uint)(RoundFunction(r1) * 65535);
l1 = l2;
r1 = r2;
}
return ((r1 << 16) + l1);
}
private static string GenerateCode(uint id)
{
return ToBase62(PermuteId(id));
}
static void Main(string[] args)
{
Console.WriteLine("testing...");
try
{
for (uint x = 1; x < 1000000; x += 1)
{
Console.Write(GenerateCode(x) + ",");
}
}
catch (Exception err)
{
Console.WriteLine("error: " + err.Message);
}
Console.WriteLine("");
Console.WriteLine("Press 'Enter' to continue...");
Console.Read();
}
}
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