IPv6 Abbreviation(zero blocks compression) logic. I'm using c# - c#

This is a complete un compressed IP address 2001:0008:0000:CD30:0000:0000:0000:0101
I need to compress it like this
2001:8:0:CD30::101
But i was only able to compress the zeroes in blocks like this
2001:8:0:CD30:0:0:0:101
using this code
string output = "";
string a = textBox1.Text;
if (a.Length != 39 )
MessageBox.Show("Invalid IP please enter the IPv6 IP in this format 6cd9:a87a:ad46:0005:ad40:0000:5698:8ab8");
else
{
for (int i = 0; i < a.Length; i++)
{
if ((a[i] >= '1' && a[i] <= '9') || (Char.ToLower(a[i]) >= 'a' && Char.ToLower(a[i]) <= 'f') || ((i + 1) % 5 == 0 && a[i] == ':'))
{
output = output + a[i];
}
else if ((a[i]=='0' && a[i-1]==':') || (a[i]=='0' && a[i-1]=='0' && a[i-2]==':') || (a[i]=='0' && a[i-1]=='0' && a[i-2]=='0' && a[i-3]==':'))
{
}
else if (a[i] == '0')
{
output = output + a[i];
}
else
{
MessageBox.Show("Invalid IP please enter the IPv6 IP in this format 6cd9:a87a:ad46:0005:ad40:0000:5698:8ab8");
}
}
textBox2.Text = output;
}
Im using c# but i only need the programming logic about how can whole blocks of zeroes be deleted the problem is there could be more then 1 group of blocks containing all zeros in an ip but only one should be abbreviated.

Was far more tricky than I expected, but here you got the way to do it with regular expressions:
private static string Compress(string ip)
{
var removedExtraZeros = ip.Replace("0000","*");
//2001:0008:*:CD30:*:*:*:0101
var blocks = ip.Split(':');
var regex = new Regex(":0+");
removedExtraZeros = regex.Replace(removedExtraZeros, ":");
//2001:8:*:CD30:*:*:*:101
var regex2 = new Regex(":\\*:\\*(:\\*)+:");
removedExtraZeros = regex2.Replace(removedExtraZeros, "::");
//2001:8:*:CD30::101
return removedExtraZeros.Replace("*", "0");
}

If you would like to achieve the same result without using Regex:
public string Compress(string value)
{
var values = value.Split(",");
var ints = values.Select(i => int.Parse(i, System.Globalization.NumberStyles.HexNumber));
var result = ints.Select(Conversion.Hex);
return string.Join(":", result);
}
Didn't spend time on micro-optimizations (stackalloc, spans, etc.) but this gives an idea. For reference on optimization, you can look at the implementation of the IPAddress.Parse in the .net core. Do mind that result of the IPAddress.Parse will give a different result than the example above:
Compress -> 2001:8:0:CD30:0:0:0:101
IPAddress.Parse -> 2001:8:0:cd30::101
This could be "cleaned" up by moving into some object and other ideas.
Edit:
After chatting with one of my colleagues, I spent some time writing an "optimized" version of this. I haven't spent time cleaning up the code, so maybe one of the future edits will be even cleaner.
public string Compress(string value)
{
Span<char> chars = stackalloc char[value.Length];
const char zero = '0';
const char colon = ':';
int index = 0;
int positionInSegment = 0;
bool startsWithZero;
while (index < _originalValue.Length)
{
startsWithZero = value[index] == zero && positionInSegment == 0;
positionInSegment++;
if (startsWithZero)
{
if (index == value.Length - 1)
{
chars[index] = zero;
break;
}
if (value[index + 1] == colon)
{
chars[index] = zero;
positionInSegment = 0;
index++;
continue;
}
positionInSegment = 0;
index++;
continue;
}
if (value[index] == colon)
{
positionInSegment = 0;
chars[index] = colon;
index++;
continue;
}
chars[index] = value[index];
index++;
}
return chars.ToString();
}
I have also created a public gist for future references:
https://gist.github.com/DeanMilojevic/7b4f1d060ce8cfa191592694b11234d7

Related

Having trouble understanding a solution to this c# algorithm

I was doing a LeetCode exercise for c# and made my own solution for the following prompt:
"Given a binary array nums, return the maximum number of consecutive 1's in the array."
"Input: nums = [1,1,0,1,1,1]
Output: 3
Explanation: The first two digits or the last three digits are consecutive 1s. The maximum number of consecutive 1s is 3."
My solution had a hardcoded array value, and i found a solution online that worked and ran fine. However, im having a really hard time understanding what the isStart and isEnd bools do. The code is below:
public class Solution {
public int FindMaxConsecutiveOnes(int[] nums)
{
if(nums == null || nums.Length == 0)
{
return 0;
}
var start = 0;
var length = nums.Length;
var maxLength = 0;
for(int i = 0; i < length; i++)
{
var current = nums[i];
bool isStart = current == 1 && (i == 0 || nums[i - 1] == 0);
bool isEnd = current == 1 && (i == length - 1 || nums[i + 1] == 0);
if (isStart)
{
start = i;
}
if(isEnd)
{
var currentOnes = i - start + 1;
maxLength = currentOnes > maxLength ? currentOnes : maxLength;
}
}
return maxLength;
}
I assume isStart and isEnd determine if we are at the Start or End of the array? I dont really understand what the operators do either. Any help could be appreciated. Thanks :)

My int 'LastTwoChars' is resulting in a "StartIndex cannot be less than zero." Exception. I understand the exception, but am unable to resolve it

I want to start by saying I am very new (1 week) into learning C#, so I sincerely apologize if this question is obvious. I do understand the reason for the exception, diverse.Length has to be 0 or greater. However, I am required to have the formula in my code so as to get the numbered-positions of the last 2 characters of an ever changing string (diverse).
Below, are 3 sets of code....
Firstly my working method.
static void Main(string[] args)
{
try
{
// Sample data - inputs 3 ints.
Console.WriteLine(Solution1(6, 1, 1));
Console.WriteLine(Solution1(1, 3, 1));
Console.WriteLine(Solution1(0, 1, 8));
Console.WriteLine(Solution1(5, 2, 4));
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
static string Solution1(int A, int B, int C)
{
string a = "a"; // a/b/c are added to string diverse when needed.
string b = "b";
string c = "c";
string diverse = "";
int totalLength = A + B + C; // Length of all 3 arrays
for(int i = 1; i <= totalLength; i++)
{
if (A >= B && A >= C && A > 0) { diverse = diverse + a; A = A - 1; }
if (B >= A && B >= C && B > 0) { diverse = diverse + b; B = B - 1; }
if (C >= A && C >= B && C > 0) { diverse = diverse + c; C = C - 1; }
}
return diverse;
}
What I am trying to do is add an additional check to my code. This check will take the printed letters and check to see if 2 of the same letter has previously been printed. If so, it will not print a 3rd. To do this I made a solution that would find the last 2 characters of the string (as I mentioned above) and compare it to the conditional check in the if statement.
Below is the code with this additional check, that I need to get working...
static string Solution1(int A, int B, int C)
{
string a = "a"; // a/b/c are added to string diverse when needed.
string b = "b";
string c = "c";
string diverse = "";
int totalLength = A + B + C; // Length of all 3 arrays
for (int i = 1; i <= totalLength; i++)
{
// Finds the last 2 characters in the diverse string.
int LastTwoChars = diverse.Length - 2;
string twoCharCheck = diverse.Substring(LastTwoChars, 2);
if (A > 0 && B < 2 && C < 2 && twoCharCheck != "aa")
{
diverse = diverse + a; A = A - 1;
}
if (B > 0 && A < 2 && C < 2 && twoCharCheck != "bb")
{
diverse = diverse + b; B = B - 1;
}
if (C > 0 && B < 2 && A < 2 && twoCharCheck != "cc")
{
diverse = diverse + c; C = C - 1;
}
}
return diverse;
}
You can have the string check only after you have at least 2 characters:
int LastTwoChars = diverse.Length - 2;
string twoCharCheck = LastTwoChars >= 0 ? diverse.Substring(LastTwoChars, 2) : string.Empty; // identical with if (LastTwoChars >= 0) twoCharCheck = diverse.Substring(LastTwoChars, 2); else twoCharCheck = string.Empty
if (A > 0 && B < 2 && C < 2 && (LastTwoChars < 0 || twoCharCheck != "aa")) // check the end only if you have at least 2 chars;
{
...
}
....
As you can see the code gets ugly very fast, so I would propose using a helper method:
// returns the last two chars in a string format, or a default user string
private static string LastTwoCharsOrDefault(string input, string default)
{
var lastTwoCharsIdx = input.Length - 2;
if (lastTwoCharsIdx > 0 )
{
return input.Substring(lastTwoCharsIdx, 2);
}
// we don't have at least 2 chars, so lets just return the default
return default;
}
Then you can change your code like this:
if (A > 0 && B < 2 && C < 2 && string.Equals("aa", LastTwoCharsOrDefault(diverse, "aa"), StringComparison.OrdinalIgnoreCase))
...
You can find more about string comparison here.
If I read your code right, you initialize Diverse as: string diverse = "";
and then execute string twoCharCheck = diverse.Substring(LastTwoChars, 2);
LastTwoChars is going to be -2 the first time through the loop.

Decoding Bitcoin Base58 address to byte array

I'm trying to decode bitcoin address from Base58 string into byte array, and to do that I rewrited original function from Satoshi repository (https://github.com/bitcoin/bitcoin/blob/master/src/base58.cpp), written in c++, to c# (which I'm using).
Original code
static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
bool DecodeBase58(const char *psz, std::vector<unsigned char>& vch) {
// Skip leading spaces.
while (*psz && isspace(*psz))
psz++;
// Skip and count leading '1's.
int zeroes = 0;
while (*psz == '1') {
zeroes++;
psz++;
}
// Allocate enough space in big-endian base256 representation.
std::vector<unsigned char> b256(strlen(psz) * 733 / 1000 + 1); // log(58) / log(256), rounded up.
// Process the characters.
while (*psz && !isspace(*psz)) {
// Decode base58 character
const char *ch = strchr(pszBase58, *psz);
if (ch == NULL)
return false;
// Apply "b256 = b256 * 58 + ch".
int carry = ch - pszBase58;
for (std::vector<unsigned char>::reverse_iterator it = b256.rbegin(); it != b256.rend(); it++) {
carry += 58 * (*it);
*it = carry % 256;
carry /= 256;
}
assert(carry == 0);
psz++;
}
// Skip trailing spaces.
while (isspace(*psz))
psz++;
if (*psz != 0)
return false;
// Skip leading zeroes in b256.
std::vector<unsigned char>::iterator it = b256.begin();
while (it != b256.end() && *it == 0)
it++;
// Copy result into output vector.
vch.reserve(zeroes + (b256.end() - it));
vch.assign(zeroes, 0x00);
while (it != b256.end())
vch.push_back(*(it++));
return true;
}
Mine rewrited c# version
private static string Base58characters = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
public static bool Decode(string source, ref byte[] destination)
{
int i = 0;
while (i < source.Length)
{
if (source[i] == 0 || !Char.IsWhiteSpace(source[i]))
{
break;
}
i++;
}
int zeros = 0;
while (source[i] == '1')
{
zeros++;
i++;
}
byte[] b256 = new byte[(source.Length - i) * 733 / 1000 + 1];
while (i < source.Length && !Char.IsWhiteSpace(source[i]))
{
int ch = Base58characters.IndexOf(source[i]);
if (ch == -1) //null
{
return false;
}
int carry = Base58characters.IndexOf(source[i]);
for (int k = b256.Length - 1; k > 0; k--)
{
carry += 58 * b256[k];
b256[k] = (byte)(carry % 256);
carry /= 256;
}
i++;
}
while (i < source.Length && Char.IsWhiteSpace(source[i]))
{
i++;
}
if (i != source.Length)
{
return false;
}
int j = 0;
while (j < b256.Length && b256[j] == 0)
{
j++;
}
destination = new byte[zeros + (b256.Length - j)];
for (int kk = 0; kk < destination.Length; kk++)
{
if (kk < zeros)
{
destination[kk] = 0x00;
}
else
{
destination[kk] = b256[j++];
}
}
return true;
}
Function that I'm using for converting from byte-array to HexString
public static string ByteArrayToHexString(byte[] source)
{
return BitConverter.ToString(source).Replace("-", "");
}
To test if everything is working correctly I've used test cases found online here (https://github.com/ThePiachu/Bitcoin-Unit-Tests/blob/master/Address/Address%20Generation%20Test%201.txt). Good thing is that 97% of this test are passed correctly but for 3 there is a little error and I do not know where it is coming from. So my ask to you is to point me what could for these test go wrong or where in rewriting I've made an error. Thank you in advance.
The test cases where errors occures are 1, 21 and 25.
1.
Input:
16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM
Output:
000966776006953D5567439E5E39F86A0D273BEED61967F6
Should be:
00010966776006953D5567439E5E39F86A0D273BEED61967F6
21.
Input:
1v3VUYGogXD7S1E8kipahj7QXgC568dz1
Output:
0008201462985DF5255E4A6C9D493C932FAC98EF791E2F22
Should be:
000A08201462985DF5255E4A6C9D493C932FAC98EF791E2F22
25.
Input:
1axVFjCkMWDFCHjQHf99AsszXTuzxLxxg
Output:
006C0B8995C7464E89F6760900EA6978DF18157388421561
Should be:
00066C0B8995C7464E89F6760900EA6978DF18157388421561
In your for-loop:
for (int k = b256.Length - 1; k > 0; k--)
The loop condition should be k >= 0 so that you don't skip the first byte in b256.

Generating URL-friendly unique string with embedded datetime

I'm personally tired of reimplementing "a ticket" mechanism for my web projects for some one-time operations like account activation or password reset. I know it's simple but it requires me to keep (and persist!) two pieces of data: a ticket itself and an expiration date.
So I have this idea, to generate a unique string with embedded datetime (expiration date) in it, which we can check upon receiving as a part of url-request.
I've started with this:
var clearTicket = Convert.ToInt64(expiration.ToString("yyyyMMddhhmm")).ToString("x12") + key.ToString("N");
But I want it to be more compact. Something BaseXX-ish I suppose.
Any ideas how to implement this encoding/decoding efficiently (considering url-safe charset)?
It took me some time, so I hope it helps:
First of all, you should substract 200000000000 from "yyyyMMddhhmm", because you actually don't need the first two digits for the next 88 years.
Here is the implementation for encoding and decoding Base64, using only URL-safe characters.
If you have any questions, feel free to ask.
public string Base64Encode (Int64 Number)
{
string HelpString = "";
if (Number >= 64)
{
HelpString = Base64Encode(Number / 64);
}
return (HelpString += Base64EncodeHelper(Number % 64));
}
public string Base64EncodeHelper(Int64 Number)
{
string HelpString = "";
Number += 65;
if ((Number >= 65 && Number <= 90) || (Number >= 97 && Number <= 122)) // 0 - 25 and 32 - 57
{
HelpString = Convert.ToString((char)Number);
}
else if (Number >= 91 && Number <= 96) // 26 - 31
{
HelpString = Convert.ToString((char)(Number - 43));
}
else if (Number >= 123 && Number <= 126) // 58 - 61
{
HelpString = Convert.ToString((char)(Number - 69));
}
else if (Number == 127) // 62
{
HelpString = "-";
}
else // 63
{
HelpString = "_";
}
return (HelpString);
}
public Int64 Base64Decode(string Encoded)
{
Int64 Result = 0, HelpInt = 0;
int i = Encoded.Length - 1;
foreach (char Character in Encoded)
{
int CharInInt = (int)Character;
if (Character == '_')
{
HelpInt = 63;
}
else if (Character == '-')
{
HelpInt = 62;
}
else if (((CharInInt + 69) >= 123) && ((CharInInt + 69) <= 126))
{
HelpInt = CharInInt + 4;
}
else if (((CharInInt + 43) >= 91) && ((CharInInt + 43) <= 96))
{
HelpInt = CharInInt - 22;
}
else
{
HelpInt = CharInInt - 65;
}
Result += Convert.ToInt64((Math.Pow(64, Convert.ToDouble(i))) * HelpInt);
i--;
}
return Result;
}
You can extend string like this:
public static string ToURLParameter(this string text)
{
if (String.IsNullOrEmpty(text)) return "";
// to lowercase, trim extra spaces
text = text.Trim();
var len = text.Length;
var sb = new StringBuilder(len);
bool prevdash = false;
char c;
//
text = text.Replace('Å', 'A');
text = text.Replace('å', 'a');
text = text.Replace('Ä', 'A');
text = text.Replace('ä', 'a');
text = text.Replace('Ö', 'O');
text = text.Replace('ö', 'o');
for (int i = 0; i < text.Length; i++)
{
c = text[i];
if (c == ' ' || c == ',' || c == '.' || c == '/' || c == '\\' || c == '-')
{
if (!prevdash)
{
sb.Append('-');
prevdash = true;
}
}
else if ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z'))
{
sb.Append(c);
prevdash = false;
}
if (i == 80) break;
}
text = sb.ToString();
// remove trailing dash, if there is one
if (text.EndsWith("-"))
text = text.Substring(0, text.Length - 1);
return text;
}
and then you can use it on any variable that is string like:
string something = "asdfljasdklf";
<%= something.ToUrlParameter() %>
I've finally created a solution using techniques described in these answers:
Compressing big number (or string) to small value
How do you convert Byte Array to Hexadecimal String, and vice versa?
Works great!
Thanks you all for your help!

Iterating through the Alphabet - C# a-caz

I have a question about iterate through the Alphabet.
I would like to have a loop that begins with "a" and ends with "z". After that, the loop begins "aa" and count to "az". after that begins with "ba" up to "bz" and so on...
Anybody know some solution?
Thanks
EDIT: I forgot that I give a char "a" to the function then the function must return b. if u give "bnc" then the function must return "bnd"
First effort, with just a-z then aa-zz
public static IEnumerable<string> GetExcelColumns()
{
for (char c = 'a'; c <= 'z'; c++)
{
yield return c.ToString();
}
char[] chars = new char[2];
for (char high = 'a'; high <= 'z'; high++)
{
chars[0] = high;
for (char low = 'a'; low <= 'z'; low++)
{
chars[1] = low;
yield return new string(chars);
}
}
}
Note that this will stop at 'zz'. Of course, there's some ugly duplication here in terms of the loops. Fortunately, that's easy to fix - and it can be even more flexible, too:
Second attempt: more flexible alphabet
private const string Alphabet = "abcdefghijklmnopqrstuvwxyz";
public static IEnumerable<string> GetExcelColumns()
{
return GetExcelColumns(Alphabet);
}
public static IEnumerable<string> GetExcelColumns(string alphabet)
{
foreach(char c in alphabet)
{
yield return c.ToString();
}
char[] chars = new char[2];
foreach(char high in alphabet)
{
chars[0] = high;
foreach(char low in alphabet)
{
chars[1] = low;
yield return new string(chars);
}
}
}
Now if you want to generate just a, b, c, d, aa, ab, ac, ad, ba, ... you'd call GetExcelColumns("abcd").
Third attempt (revised further) - infinite sequence
public static IEnumerable<string> GetExcelColumns(string alphabet)
{
int length = 0;
char[] chars = null;
int[] indexes = null;
while (true)
{
int position = length-1;
// Try to increment the least significant
// value.
while (position >= 0)
{
indexes[position]++;
if (indexes[position] == alphabet.Length)
{
for (int i=position; i < length; i++)
{
indexes[i] = 0;
chars[i] = alphabet[0];
}
position--;
}
else
{
chars[position] = alphabet[indexes[position]];
break;
}
}
// If we got all the way to the start of the array,
// we need an extra value
if (position == -1)
{
length++;
chars = new char[length];
indexes = new int[length];
for (int i=0; i < length; i++)
{
chars[i] = alphabet[0];
}
}
yield return new string(chars);
}
}
It's possible that it would be cleaner code using recursion, but it wouldn't be as efficient.
Note that if you want to stop at a certain point, you can just use LINQ:
var query = GetExcelColumns().TakeWhile(x => x != "zzz");
"Restarting" the iterator
To restart the iterator from a given point, you could indeed use SkipWhile as suggested by thesoftwarejedi. That's fairly inefficient, of course. If you're able to keep any state between call, you can just keep the iterator (for either solution):
using (IEnumerator<string> iterator = GetExcelColumns())
{
iterator.MoveNext();
string firstAttempt = iterator.Current;
if (someCondition)
{
iterator.MoveNext();
string secondAttempt = iterator.Current;
// etc
}
}
Alternatively, you may well be able to structure your code to use a foreach anyway, just breaking out on the first value you can actually use.
Edit: Made it do exactly as the OP's latest edit wants
This is the simplest solution, and tested:
static void Main(string[] args)
{
Console.WriteLine(GetNextBase26("a"));
Console.WriteLine(GetNextBase26("bnc"));
}
private static string GetNextBase26(string a)
{
return Base26Sequence().SkipWhile(x => x != a).Skip(1).First();
}
private static IEnumerable<string> Base26Sequence()
{
long i = 0L;
while (true)
yield return Base26Encode(i++);
}
private static char[] base26Chars = "abcdefghijklmnopqrstuvwxyz".ToCharArray();
private static string Base26Encode(Int64 value)
{
string returnValue = null;
do
{
returnValue = base26Chars[value % 26] + returnValue;
value /= 26;
} while (value-- != 0);
return returnValue;
}
The following populates a list with the required strings:
List<string> result = new List<string>();
for (char ch = 'a'; ch <= 'z'; ch++){
result.Add (ch.ToString());
}
for (char i = 'a'; i <= 'z'; i++)
{
for (char j = 'a'; j <= 'z'; j++)
{
result.Add (i.ToString() + j.ToString());
}
}
I know there are plenty of answers here, and one's been accepted, but IMO they all make it harder than it needs to be. I think the following is simpler and cleaner:
static string NextColumn(string column){
char[] c = column.ToCharArray();
for(int i = c.Length - 1; i >= 0; i--){
if(char.ToUpper(c[i]++) < 'Z')
break;
c[i] -= (char)26;
if(i == 0)
return "A" + new string(c);
}
return new string(c);
}
Note that this doesn't do any input validation. If you don't trust your callers, you should add an IsNullOrEmpty check at the beginning, and a c[i] >= 'A' && c[i] <= 'Z' || c[i] >= 'a' && c[i] <= 'z' check at the top of the loop. Or just leave it be and let it be GIGO.
You may also find use for these companion functions:
static string GetColumnName(int index){
StringBuilder txt = new StringBuilder();
txt.Append((char)('A' + index % 26));
//txt.Append((char)('A' + --index % 26));
while((index /= 26) > 0)
txt.Insert(0, (char)('A' + --index % 26));
return txt.ToString();
}
static int GetColumnIndex(string name){
int rtn = 0;
foreach(char c in name)
rtn = rtn * 26 + (char.ToUpper(c) - '#');
return rtn - 1;
//return rtn;
}
These two functions are zero-based. That is, "A" = 0, "Z" = 25, "AA" = 26, etc. To make them one-based (like Excel's COM interface), remove the line above the commented line in each function, and uncomment those lines.
As with the NextColumn function, these functions don't validate their inputs. Both with give you garbage if that's what they get.
Here’s what I came up with.
/// <summary>
/// Return an incremented alphabtical string
/// </summary>
/// <param name="letter">The string to be incremented</param>
/// <returns>the incremented string</returns>
public static string NextLetter(string letter)
{
const string alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
if (!string.IsNullOrEmpty(letter))
{
char lastLetterInString = letter[letter.Length - 1];
// if the last letter in the string is the last letter of the alphabet
if (alphabet.IndexOf(lastLetterInString) == alphabet.Length - 1)
{
//replace the last letter in the string with the first leter of the alphbat and get the next letter for the rest of the string
return NextLetter(letter.Substring(0, letter.Length - 1)) + alphabet[0];
}
else
{
// replace the last letter in the string with the proceeding letter of the alphabet
return letter.Remove(letter.Length-1).Insert(letter.Length-1, (alphabet[alphabet.IndexOf(letter[letter.Length-1])+1]).ToString() );
}
}
//return the first letter of the alphabet
return alphabet[0].ToString();
}
just curious , why not just
private string alphRecursive(int c) {
var alphabet = "abcdefghijklmnopqrstuvwxyz".ToCharArray();
if (c >= alphabet.Length) {
return alphRecursive(c/alphabet.Length) + alphabet[c%alphabet.Length];
} else {
return "" + alphabet[c%alphabet.Length];
}
}
This is like displaying an int, only using base 26 in stead of base 10. Try the following algorithm to find the nth entry of the array
q = n div 26;
r = n mod 26;
s = '';
while (q > 0 || r > 0) {
s = alphabet[r] + s;
q = q div 26;
r = q mod 26;
}
Of course, if you want the first n entries, this is not the most efficient solution. In this case, try something like daniel's solution.
I gave this a go and came up with this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Alphabetty
{
class Program
{
const string alphabet = "abcdefghijklmnopqrstuvwxyz";
static int cursor = 0;
static int prefixCursor;
static string prefix = string.Empty;
static bool done = false;
static void Main(string[] args)
{
string s = string.Empty;
while (s != "Done")
{
s = GetNextString();
Console.WriteLine(s);
}
Console.ReadKey();
}
static string GetNextString()
{
if (done) return "Done";
char? nextLetter = GetNextLetter(ref cursor);
if (nextLetter == null)
{
char? nextPrefixLetter = GetNextLetter(ref prefixCursor);
if(nextPrefixLetter == null)
{
done = true;
return "Done";
}
prefix = nextPrefixLetter.Value.ToString();
nextLetter = GetNextLetter(ref cursor);
}
return prefix + nextLetter;
}
static char? GetNextLetter(ref int letterCursor)
{
if (letterCursor == alphabet.Length)
{
letterCursor = 0;
return null;
}
char c = alphabet[letterCursor];
letterCursor++;
return c;
}
}
}
Here is something I had cooked up that may be similar. I was experimenting with iteration counts in order to design a numbering schema that was as small as possible, yet gave me enough uniqueness.
I knew that each time a added an Alpha character, it would increase the possibilities 26x but I wasn't sure how many letters, numbers, or the pattern I wanted to use.
That lead me to the code below. Basically you pass it an AlphaNumber string, and every position that has a Letter, would eventually increment to "z\Z" and every position that had a Number, would eventually increment to "9".
So you can call it 1 of two ways..
//This would give you the next Itteration... (H3reIsaStup4dExamplf)
string myNextValue = IncrementAlphaNumericValue("H3reIsaStup4dExample")
//Or Loop it resulting eventually as "Z9zzZzzZzzz9zZzzzzzz"
string myNextValue = "H3reIsaStup4dExample"
while (myNextValue != null)
{
myNextValue = IncrementAlphaNumericValue(myNextValue)
//And of course do something with this like write it out
}
(For me, I was doing something like "1AA000")
public string IncrementAlphaNumericValue(string Value)
{
//We only allow Characters a-b, A-Z, 0-9
if (System.Text.RegularExpressions.Regex.IsMatch(Value, "^[a-zA-Z0-9]+$") == false)
{
throw new Exception("Invalid Character: Must be a-Z or 0-9");
}
//We work with each Character so it's best to convert the string to a char array for incrementing
char[] myCharacterArray = Value.ToCharArray();
//So what we do here is step backwards through the Characters and increment the first one we can.
for (Int32 myCharIndex = myCharacterArray.Length - 1; myCharIndex >= 0; myCharIndex--)
{
//Converts the Character to it's ASCII value
Int32 myCharValue = Convert.ToInt32(myCharacterArray[myCharIndex]);
//We only Increment this Character Position, if it is not already at it's Max value (Z = 90, z = 122, 57 = 9)
if (myCharValue != 57 && myCharValue != 90 && myCharValue != 122)
{
myCharacterArray[myCharIndex]++;
//Now that we have Incremented the Character, we "reset" all the values to the right of it
for (Int32 myResetIndex = myCharIndex + 1; myResetIndex < myCharacterArray.Length; myResetIndex++)
{
myCharValue = Convert.ToInt32(myCharacterArray[myResetIndex]);
if (myCharValue >= 65 && myCharValue <= 90)
{
myCharacterArray[myResetIndex] = 'A';
}
else if (myCharValue >= 97 && myCharValue <= 122)
{
myCharacterArray[myResetIndex] = 'a';
}
else if (myCharValue >= 48 && myCharValue <= 57)
{
myCharacterArray[myResetIndex] = '0';
}
}
//Now we just return an new Value
return new string(myCharacterArray);
}
}
//If we got through the Character Loop and were not able to increment anything, we retun a NULL.
return null;
}
Here's my attempt using recursion:
public static void PrintAlphabet(string alphabet, string prefix)
{
for (int i = 0; i < alphabet.Length; i++) {
Console.WriteLine(prefix + alphabet[i].ToString());
}
if (prefix.Length < alphabet.Length - 1) {
for (int i = 0; i < alphabet.Length; i++) {
PrintAlphabet(alphabet, prefix + alphabet[i]);
}
}
}
Then simply call PrintAlphabet("abcd", "");

Categories

Resources