Domanda

Ho una domanda su iterate tramite Alphabet. Vorrei avere un ciclo che inizia con & Quot; a & Quot; e termina con " z " ;. Successivamente, il ciclo inizia & Quot; aa & Quot; e conta fino a " az " ;. dopodiché inizia con " ba " fino a " bz " e così via ...

Qualcuno conosce qualche soluzione?

Grazie

EDIT: ho dimenticato di dare un carattere " un " alla funzione quindi la funzione deve restituire b. se dai " bnc " quindi la funzione deve restituire " bnd "

È stato utile?

Soluzione

Modifica: fatto esattamente come vuole l'ultima modifica dell'OP

Questa è la soluzione più semplice e testata:

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

Altri suggerimenti

Primo sforzo, con solo a-z quindi a-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);
        }
    }
}

Nota che questo si fermerà su 'zz'. Certo, c'è qualche brutta duplicazione qui in termini di loop. Fortunatamente, è facile da risolvere e può anche essere ancora più flessibile:

Secondo tentativo: alfabeto più flessibile

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

Ora se vuoi generare solo a, b, c, d, aa, ab, ac, ad, ba, ... chiameresti GetExcelColumns("abcd").

Terzo tentativo (rivisto ulteriormente) - sequenza infinita

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

È possibile che sia un codice più pulito usando la ricorsione, ma non sarebbe altrettanto efficiente.

Nota che se vuoi fermarti ad un certo punto, puoi semplicemente usare LINQ:

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

quot; quot Riavvio &; l'iteratore

Per riavviare l'iteratore da un determinato punto, puoi effettivamente utilizzare SkipWhile come suggerito dal software jedi. Questo è abbastanza inefficiente, ovviamente. Se riesci a mantenere uno stato tra una chiamata e l'altra, puoi semplicemente mantenere l'iteratore (per entrambe le soluzioni):

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

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

In alternativa, potresti essere in grado di strutturare il tuo codice in modo che utilizzi comunque foreach, semplicemente inserendo il primo valore che puoi effettivamente usare.

Di seguito viene popolato un elenco con le stringhe richieste:

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

So che ci sono molte risposte qui, e una è stata accettata, ma l'IMO rende tutto più difficile di quanto debba essere. Penso che quanto segue sia più semplice e più pulito:

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

Nota che ciò non comporta alcuna convalida dell'input. Se non ti fidi dei chiamanti, dovresti aggiungere un IsNullOrEmpty controllo all'inizio e un c[i] >= 'A' && c[i] <= 'Z' || c[i] >= 'a' && c[i] <= 'z' controllo nella parte superiore del ciclo. O semplicemente lascialo essere e lascia che sia GIGO .

Puoi anche trovare l'uso per queste funzioni di accompagnamento:

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

Queste due funzioni sono a base zero. Cioè, & Quot; A & Quot; = 0, & Quot; Z & Quot; = 25, & Quot; AA & Quot; = 26, ecc. Per renderli a una base (come l'interfaccia COM di Excel), rimuovere la riga sopra la riga commentata in ciascuna funzione e rimuovere il commento da tali righe.

Come per la funzione NextColumn, queste funzioni non convalidano i loro input. Entrambi ti danno spazzatura se è quello che ottengono.

Qui & # 8217; s quello che mi è venuto in mente.

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

solo curioso, perché non solo

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

È come visualizzare un int, usando solo la base 26 al posto della base 10. Provare il seguente algoritmo per trovare l'ennesima voce dell'array

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

Ovviamente, se vuoi le prime n voci, questa non è la soluzione più efficiente. In questo caso, prova qualcosa come la soluzione di Daniel.

Ho provato e ho pensato a questo:

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

Ecco qualcosa che avevo preparato che potrebbe essere simile. Stavo sperimentando i conteggi delle iterazioni al fine di progettare uno schema di numerazione il più piccolo possibile, ma mi dava abbastanza unicità.

Sapevo che ogni volta che un personaggio Alpha veniva aggiunto, aumentava le possibilità di 26 volte, ma non ero sicuro di quante lettere, numeri o schema volessi usare.

Questo mi porta al codice qui sotto. Fondamentalmente le passi una stringa AlphaNumber, e ogni posizione che ha una Lettera, alla fine aumenterebbe a & Quot; z \ Z & Quot; e ogni posizione che aveva un Numero, alla fine aumenterebbe a " 9 " ;.

Quindi puoi chiamarlo in due modi.

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

(Per me, stavo facendo qualcosa come " 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;  
    }

Ecco il mio tentativo di ricorsione:

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

Quindi chiama semplicemente PrintAlphabet("abcd", "");

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top