Iterando a través del alfabeto – C # a-caz

Tengo una pregunta sobre iterar a través del alfabeto. Me gustaría tener un ciclo que comience con “a” y termine con “z”. Después de eso, el ciclo comienza “aa” y cuenta hasta “az”. después de eso comienza con “ba” hasta “bz” y así sucesivamente …

¿Alguien sabe alguna solución?

Gracias

EDITAR: Olvidé que doy un carácter “a” a la función y luego la función debe devolver b. si das “bnc” entonces la función debe devolver “bnd”

Editar: Hecho exactamente como lo quiere la última edición de OP

Esta es la solución más simple y probada:

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

Primer esfuerzo, con solo az y luego aa-zz

 public static IEnumerable 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); } } } 

Tenga en cuenta que esto se detendrá en 'zz'. Por supuesto, hay una fea duplicación aquí en términos de los bucles. Afortunadamente, es fácil de arreglar, y puede ser aún más flexible:

Segundo bash: alfabeto más flexible

 private const string Alphabet = "abcdefghijklmnopqrstuvwxyz"; public static IEnumerable GetExcelColumns() { return GetExcelColumns(Alphabet); } public static IEnumerable 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); } } } 

Ahora, si quieres generar solo a, b, c, d, aa, ab, ac, ad, ba, ... llamarías a GetExcelColumns("abcd") .

Tercer bash (revisado más adelante) - secuencia infinita

 public static IEnumerable 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); } } 

Es posible que sea un código más limpio mediante la recursión, pero no sería tan eficiente.

Tenga en cuenta que si desea detenerse en cierto punto, puede usar LINQ:

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

"Reiniciar" el iterador

Para reiniciar el iterador desde un punto determinado, puede usar SkipWhile como lo sugiere thesoftwarejedi. Eso es bastante ineficiente, por supuesto. Si puede mantener cualquier estado entre llamadas, puede mantener el iterador (para cualquiera de las soluciones):

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

Alternativamente, puede ser capaz de estructurar su código para usar un foreach todos modos, simplemente aprovechando el primer valor que realmente puede usar.

A continuación, se completa una lista con las cadenas necesarias:

 List result = new List(); 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()); } } 

Sé que hay muchas respuestas aquí, y una ha sido aceptada, pero IMO todas hacen que sea más difícil de lo que debe ser. Creo que lo siguiente es más simple y más limpio:

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

Tenga en cuenta que esto no hace ninguna validación de entrada. Si no confía en las personas que llaman, debe agregar una verificación IsNullOrEmpty al principio, y a c[i] >= 'A' && c[i] <= 'Z' || c[i] >= 'a' && c[i] <= 'z' c[i] >= 'A' && c[i] <= 'Z' || c[i] >= 'a' && c[i] <= 'z' verificar en la parte superior del ciclo. O simplemente déjalo y deja que sea GIGO .

También puede encontrar uso para estas funciones complementarias:

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

Estas dos funciones son basadas en cero. Es decir, "A" = 0, "Z" = 25, "AA" = 26, etc. Para hacerlos basados ​​en una sola (como la interfaz COM de Excel), elimine la línea sobre la línea comentada en cada función y elimine los comentarios de aquellos líneas.

Al igual que con la función NextColumn , estas funciones no validan sus entradas. Ambos te dan basura si eso es lo que obtienen.

Esto es lo que se me ocurrió.

 ///  /// Return an incremented alphabtical string ///  /// The string to be incremented /// the incremented string 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 curiosidad, ¿por qué no 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]; } } 

Esto es como mostrar un int, usando solamente la base 26 en lugar de la base 10. Pruebe el siguiente algoritmo para encontrar la enésima entrada de la matriz

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

Por supuesto, si quiere las primeras n entradas, esta no es la solución más eficiente. En este caso, pruebe algo como la solución de Daniel.

Le di una oportunidad y se me ocurrió esto:

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

Aquí hay algo que he cocinado que puede ser similar. Estaba experimentando con recuentos de iteración para diseñar un esquema de numeración que fuera lo más pequeño posible, pero que me proporcionara la singularidad suficiente.

Sabía que cada vez que añadía un personaje Alfa, boostía las posibilidades 26 veces, pero no estaba seguro de cuántas letras, números o el patrón que quería usar.

Eso me llevó al código a continuación. Básicamente se pasa una cadena AlphaNumber, y cada posición que tenga una letra, eventualmente se incrementará a “z \ Z” y cada posición que tenga un número, eventualmente se incrementará a “9”.

Entonces puedes llamarlo de dos maneras …

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

(Para mí, estaba haciendo algo como “1AA000”)

 public string IncrementAlphaNumericValue(string Value) { //We only allow Characters ab, AZ, 0-9 if (System.Text.RegularExpressions.Regex.IsMatch(Value, "^[a-zA-Z0-9]+$") == false) { throw new Exception("Invalid Character: Must be aZ 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; } 

Aquí está mi bash de usar la recursión:

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

Luego simplemente llame a PrintAlphabet("abcd", "") ;

    Intereting Posts