Вопрос

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

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

Спасибо

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

Это было полезно?

Решение

Правка. Сделано так, как того требует последнее правление ОП

Это самое простое и проверенное решение:

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

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 в любом случае, просто остановитесь на первом значении, которое вы действительно можете использовать.

Следующее заполняет список необходимыми строками:

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

Я знаю, что здесь есть много ответов, и один из них принят, но 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;
}

Эти две функции начинаются с нуля. То есть & Quot; A & Quot; = 0, & Quot; Z & Quot; = 25, & Quot; AA & Quot; = 26 и т. Д. Чтобы сделать их одностадийными (например, интерфейс COM в Excel), удалите строку над строкой с комментариями в каждой функции и раскомментируйте эти строки.

Как и в случае функции NextColumn, эти функции не проверяют свои входные данные. Оба дают вам мусор, если это то, что они получают.

Вот & # 8217; вот что я придумала.

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

просто любопытно, почему бы просто

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

Это похоже на отображение целого числа, использующего только основание 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 записей, это не самое эффективное решение. В этом случае попробуйте что-то вроде решения Даниэля.

Я попробовал и придумал это:

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

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

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

Это привело меня к приведенному ниже коду. По сути, вы передаете ей строку AlphaNumber, и каждая позиция, в которой есть буква, в конечном итоге будет увеличиваться до & Quot; z \ Z & Quot; и каждая позиция, имеющая номер, в конечном итоге будет увеличиваться до " 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;  
    }

Вот моя попытка использовать рекурсию:

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", "");

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top