Итерация через Алфавит - С# a-caz

У меня вопрос об итерации по алфавиту. Я хотел бы иметь цикл, который начинается с "a" и заканчивается на "z". После этого петля начинается "aa" и отсчитывается до "az". после чего начинается с "ba" до "bz" и т.д....

Кто-нибудь знает какое-то решение?

Спасибо

EDIT: Я забыл, что я даю char "a" функции, тогда функция должна возвращать b. если u дает "bnc", тогда функция должна возвращать "bnd"

Ответы

Ответ 1

Изменить: сделал это точно так же, как требуется последнее редактирование 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;
}

Ответ 2

Первое усилие, только с 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");

"Перезапуск" итератора

Чтобы перезапустить итератор из заданной точки, вы действительно можете использовать SkipWhile, как было предложено thesoftwarejedi. Это довольно неэффективно, конечно. Если вы можете сохранить любое состояние между вызовами, вы можете просто сохранить итератор (для любого решения):

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

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

В качестве альтернативы вы можете структурировать свой код для использования foreach в любом случае, просто вырвавшись из первого значения, которое вы действительно можете использовать.

Ответ 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());
    }
}

Ответ 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] >= 'A' && c[i] <= 'Z' || c[i] >= 'a' && c[i] <= '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 и т.д. Чтобы сделать их однонаправленными (например, COM-интерфейс Excel), удалите строку над прокомментированной строкой в ​​каждой функции и раскомментируйте те линии.

Как и в функции 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];
         }
    }

Ответ 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;
        }
    }
}

Ответ 9

Вот что я приготовил, чтобы быть похожим. Я экспериментировал с подсчетом итераций, чтобы составить схему нумерации, которая была как можно меньше, но при этом дала мне достаточно уникальности.

Я знал, что каждый раз, когда добавляется символ Alpha, это увеличит возможности 26x, но я не был уверен, сколько букв, цифр или шаблона я хотел использовать.

Это приведет меня к приведенному ниже коду. В основном вы передаете ему строку 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 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 ASCII value
            Int32 myCharValue = Convert.ToInt32(myCharacterArray[myCharIndex]);

            //We only Increment this Character Position, if it is not already at it 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", "");