P粉7648364482023-08-24 00:42:38
First try, just use 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 are some ugly repetitions in the loop. Fortunately, this is easy to fix - and it can also be more flexible:
Second attempt: a 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 only want to generate a, b, c, d, aa, ab, ac, ad, ba, etc., you can call GetExcelColumns("abcd")
.
Third Attempt (Further Revised) - Infinite Sequence
public static IEnumerable<string> GetExcelColumns(string alphabet) { int length = 0; char[] chars = null; int[] indexes = null; while (true) { int position = length-1; // 尝试递增最低有效值。 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 (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); } }
Maybe using recursion will lead to clearer code, but the efficiency will not be that high.
Please note that if you want to stop at a specific point, you can use LINQ:
var query = GetExcelColumns().TakeWhile(x => x != "zzz");
"Restart" iterator
To restart the iterator from a given point, you can use SkipWhile
as thesoftwarejedi suggested. Of course, this is quite inefficient. If you can maintain any state between calls, you can retain 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 might be able to structure your code to use foreach
, breaking when the first value is found that can actually be used.
P粉9204852852023-08-24 00:23:16
Edit: Make it exactly as per the latest edits to the original post
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; }