第一次尝试,只使用a-z然后是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); } } }
请注意,这将在'zz'处停止。当然,循环中存在一些丑陋的重复。幸运的是,这很容易解决 - 并且它还可以更加灵活:
第二次尝试:更灵活的字母表
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); } } }
现在,如果你只想生成a、b、c、d、aa、ab、ac、ad、ba等,你可以调用GetExcelColumns("abcd")。
GetExcelColumns("abcd")
第三次尝试(进一步修订)- 无限序列
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); } }
可能使用递归会有更清晰的代码,但效率不会那么高。
请注意,如果你想在某个特定点停止,你可以使用LINQ:
var query = GetExcelColumns().TakeWhile(x => x != "zzz");
“重新启动”迭代器
要从给定点重新启动迭代器,你可以像thesoftwarejedi建议的那样使用SkipWhile。当然,这相当低效。如果你能在调用之间保持任何状态,你可以保留迭代器(对于任一解决方案):
SkipWhile
using (IEnumerator<string> iterator = GetExcelColumns()) { iterator.MoveNext(); string firstAttempt = iterator.Current; if (someCondition) { iterator.MoveNext(); string secondAttempt = iterator.Current; // etc } }
或者,你可能能够构建你的代码以使用foreach,在找到第一个可以实际使用的值时中断。
foreach
编辑:使其完全按照原始帖子的最新编辑要求执行
这是最简单的解决方案,并经过测试:
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; }
第一次尝试,只使用a-z然后是aa-zz
请注意,这将在'zz'处停止。当然,循环中存在一些丑陋的重复。幸运的是,这很容易解决 - 并且它还可以更加灵活:
第二次尝试:更灵活的字母表
现在,如果你只想生成a、b、c、d、aa、ab、ac、ad、ba等,你可以调用
GetExcelColumns("abcd")
。第三次尝试(进一步修订)- 无限序列
可能使用递归会有更清晰的代码,但效率不会那么高。
请注意,如果你想在某个特定点停止,你可以使用LINQ:
“重新启动”迭代器
要从给定点重新启动迭代器,你可以像thesoftwarejedi建议的那样使用
SkipWhile
。当然,这相当低效。如果你能在调用之间保持任何状态,你可以保留迭代器(对于任一解决方案):或者,你可能能够构建你的代码以使用
foreach
,在找到第一个可以实际使用的值时中断。编辑:使其完全按照原始帖子的最新编辑要求执行
这是最简单的解决方案,并经过测试: