【问题标题】:Iterating through the Alphabet - C# a-caz遍历字母表 - C# a-caz
【发布时间】:2009-06-18 09:29:59
【问题描述】:

我有一个关于遍历字母表的问题。 我想要一个以“a”开头并以“z”结尾的循环。之后,循环开始“aa”并计数到“az”。之后从“ba”开始到“bz”等等......

有人知道解决办法吗?

谢谢

编辑:我忘了我给函数一个字符“a”,然后函数必须返回 b。如果你给“bnc”那么函数必须返回“bnd”

【问题讨论】:

  • 这是作业吗?听起来像在家工作。 . .
  • 更好的描述不会有什么坏处。你的这个循环是如何工作的?你有什么资源可以分享吗?
  • 你只需要从 a-zz 还是多个不确定级别?
  • @subprime 更新了我的答案,向您展示了如何做到这一点。您可以将其用作序列,或使用提供的函数完全按照您的说明进行操作。

标签: c# count loops iteration alphabet


【解决方案1】:

第一次尝试,只有 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")

第三次尝试(进一步修改)- 无限序列

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);
    }
}

使用递归可能会使代码更简洁,但效率不会那么高。

请注意,如果您想在某个点停止,您可以使用 LINQ:

var query = GetExcelColumns().TakeWhile(x => x != "zzz");

“重启”迭代器

要从给定点重新启动迭代器,您确实可以按照 thesoftwarejedi 的建议使用 SkipWhile。当然,这是相当低效的。如果您能够在调用之间保持任何状态,则可以只保留迭代器(对于任一解决方案):

using (IEnumerator<string> iterator = GetExcelColumns())
{
    iterator.MoveNext();
    string firstAttempt = iterator.Current;

    if (someCondition)
    {
        iterator.MoveNext();
        string secondAttempt = iterator.Current;
        // etc
    }
}

或者,您也可以将代码构造为使用foreach,只需突破您实际可以使用的第一个值。

【讨论】:

  • 该死的我也有同样的想法但为时已晚:)
  • 好的,谢谢!我必须重新编程以检查下一个字符,如果没有指定,那么它应该以 a 开头。非常感谢:)
  • 注意。这确实假设 ASCII(或 ASCII 的超集,包括 Unicode)。这种方法不适用于 EBCDIC 和其他此类编码(字母不连续)。但是,由于 .NET 在内部是 Unicode,所以这个特定的实现是可以的。
  • TheSoftwareJedi 的解决方案更好而且非常干净。这是要使用的通用模式。可能是您被否决的原因。
  • @Richard:它以什么方式假设?你给它任何你想要的字母。如果你愿意,你可以给它“agx”,它会产生“a”、“g”、“x”、“aa”、“ag”、“ax”、“ga”等。
【解决方案2】:

编辑:完全按照 OP 的最新编辑要求进行操作

这是最简单的解决方案,并且经过测试:

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;
}

【讨论】:

  • 最好的解决方案是应该被标记为答案的解决方案。
  • 现在它可以工作了,当然没有理由拒绝它。 (即使它有点坏,我也没有投反对票。)就个人而言,我会放弃“直到”部分——将构建无限序列的关注点与截断它的关注点分开——LINQ 可以做得很好。但是品种很好:)
  • @Jon 我同意。我只是跟风,因为他似乎喜欢那样。现在他的最新编辑开始解决真正的问题。仍然不完全存在......我认为他正在尝试使用此序列从某个 Set 中找到第一个未使用的密钥。但我不能说。
  • 抱歉 TheSoftwareJedi 为我的描述,但这是答案 :) 感谢所有其他人!
  • @TSJ:鉴于您的迭代器使用的相当简单的表示形式(即 long),从 base26 转换回 long 应该相当容易,然后创建一个从正确位置开始的迭代器。
【解决方案3】:

以下内容使用所需的字符串填充列表:

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());
    }
}

【讨论】:

  • 我喜欢我们的答案如此相似——只是他们是建立一个列表还是懒惰地产生不同:)
  • 我来自 VB,所以通常不记得产量,在这种情况下,我是在发布后才记的,但是当我尝试它时,您已经发布了产量解决方案 :)
【解决方案4】:

我知道这里有很多答案,而且一个已被接受,但 IMO 都让答案变得比需要的更难。我认为以下更简单,更干净:

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);
}

请注意,这不会进行任何输入验证。如果你不信任你的调用者,你应该在开头添加一个IsNullOrEmpty 检查,并在循环顶部添加一个c[i] &gt;= 'A' &amp;&amp; c[i] &lt;= 'Z' || c[i] &gt;= 'a' &amp;&amp; c[i] &lt;= 'z' 检查。或者就这样吧,让它成为GIGO

您还可以使用这些配套功能:

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;
}

这两个函数都是从零开始的。也就是说,"A" = 0、"Z" = 25、"AA" = 26 等。要使它们从一开始(如 Excel 的 COM 接口),请删除每个函数中注释行上方的行,并取消注释那些行。

NextColumn 函数一样,这些函数不会验证它们的输入。如果这就是他们得到的,两者都会给你垃圾。

【讨论】:

    【解决方案5】:

    这就是我想出的。

    /// <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();
    }
    

    【讨论】:

      【解决方案6】:

      只是好奇,为什么不只是

          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];
               }
          }
      

      【讨论】:

      • 当 c = 26 时,返回 a。您可以将 if/else 修改为以下内容: if (c > alphabet.Length - 1) { return alphRecursive(c / alphabet.Length) + alphabet[c % alphabet.Length]; } else { 返回 "" + 字母[c]; }
      • @BrentBarbata 是的,应该是 if (c &gt;= alphabet.Length) {...
      • 仍然很棒的解决方案。我只是用它来将谷歌地图上的字母大头针与数字计算的表格行相关联。谢谢!
      【解决方案7】:

      这就像显示一个 int,只使用基数 26 而不是基数 10。尝试以下算法找到数组的第 n 个条目

      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;
      }
      

      当然,如果您想要前 n 个条目,这不是最有效的解决方案。在这种情况下,请尝试类似 daniel 的解决方案。

      【讨论】:

        【解决方案8】:

        我试了一下,想出了这个:

        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;
                }
            }
        }
        

        【讨论】:

        • 仅从 a 到 zz 编程。没有回答对不起:(
        【解决方案9】:

        这是我做的可能类似的东西。我正在试验迭代计数,以便设计一个尽可能小的编号模式,但又给了我足够的独特性。

        我知道每次 a 添加一个 Alpha 字符,它都会增加 26 倍的可能性,但我不确定我想使用多少个字母、数字或模式。

        这让我看到了下面的代码。基本上你给它传递一个 AlphaNumber 字符串,每个有字母的位置最终都会增加到“z\Z”,每个有数字的位置最终都会增加到“9”。

        所以你可以称它为两种方式之一..

        //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
        }
        

        (对我来说,我正在做类似“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;  
            }
        

        【讨论】:

          【解决方案10】:

          这是我使用递归的尝试:

          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]);
                  }
              }
          }
          

          然后直接拨打PrintAlphabet("abcd", "");

          【讨论】:

            猜你喜欢
            • 2013-06-15
            • 1970-01-01
            • 2017-10-10
            • 2013-05-23
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2022-11-14
            相关资源
            最近更新 更多