Progtwigndo Enigmas: ¿Cómo podrías traducir un nombre de columna de Excel a un número?

Recientemente me pidieron en una entrevista de trabajo que resolviera un rompecabezas de progtwigción que pensé que sería interesante compartir. Se trata de traducir las letras de las columnas de Excel a números reales, si lo recuerdas, Excel nombra sus columnas con letras de la A a la Z, y luego la secuencia va AA, AB, AC … AZ, BA, BB, etc.

Debe escribir una función que acepte una cadena como parámetro (como “AABCCE”) y devuelve el número de columna real.

La solución puede ser en cualquier idioma.

Escribí esto hace siglos para algunos guiones de Python:

 def index_to_int(index): s = 0 pow = 1 for letter in index[::-1]: d = int(letter,36) - 9 s += pow * d pow *= 26 # excel starts column numeration from 1 return s 

Suena como una reducción estándar para mí:

Pitón:

 def excel2num(x): return reduce(lambda s,a:s*26+ord(a)-ord('A')+1, x, 0) 

DO#:

 int ExcelToNumber(string x) { return x.Aggregate(0, (s, c) => s * 26 + c - 'A' + 1 ); } 

Lea el nombre de una columna de STDIN e imprima su número correspondiente:

 perl -le '$x = $x * 26 - 64 + ord for <> =~ /./g; print $x' 

Advertencias: Asume ASCII.

EDITAR: Reemplazó " con ' para que su caparazón no interpolara $x en la cadena.

Casualmente, he resuelto el mismo problema usando javascript

 $(function() { //shorthand document.ready function var getNumber = function(x) { var result = 0; var multiplier = 1; for ( var i = x.length-1; i >= 0; i--) { var value = ((x[i].charCodeAt(0) - "A".charCodeAt(0)) + 1); result = result + value * multiplier; multiplier = multiplier * 26; } return result; }; $('#form').on('submit', function(e) { //use on if jQuery 1.7+ e.preventDefault(); //prevent form from submitting var data = $("#number").val(); $('#answer').text(getNumber(data)); }); }); 
  

Ja, lo escribimos ya en nuestra base de código, aproximadamente 3 veces diferentes 🙁

 %% @doc Convert an string to a decimal integer %% @spec b26_to_i(string()) -> integer() b26_to_i(List) when is_list(List) -> b26_to_i(string:to_lower(lists:reverse(List)),0,0). %% private functions b26_to_i([], _Power, Value) -> Value; b26_to_i([H|T],Power,Value)-> NewValue = case (H > 96) andalso (H < 123) of true -> round((H - 96) * math:pow(26, Power)); _ -> exit([H | T] ++ " is not a valid base 26 number") end, b26_to_i(T, Power + 1, NewValue + Value). 

El acertijo es que en realidad no es una representación Base26 de un número (nos mentimos a nosotros mismos en nuestro nombre de función aquí) porque no hay 0 en él.

La secuencia es: A, B, C … Z, AA, AB, AC

y no: A, B, C … Z, BA, BB, BC

(el idioma es Erlang, mais oui).

Puedes hacer esto en C de esta manera:

 unsigned int coltonum(char * string) { unsigned result = 0; char ch; while(ch = *string++) result = result * 26 + ch - 'A' + 1; return result; } 

Sin comprobación de errores, solo funciona para cadenas en mayúsculas, la cadena debe estar terminada en nulo.

Obtener el número de columna de su nombre

Java:

 public int getColNum (String colName) { //remove any whitespace colName = colName.trim(); StringBuffer buff = new StringBuffer(colName); //string to lower case, reverse then place in char array char chars[] = buff.reverse().toString().toLowerCase().toCharArray(); int retVal=0, multiplier=0; for(int i = 0; i < chars.length;i++){ //retrieve ascii value of character, subtract 96 so number corresponds to place in alphabet. ascii 'a' = 97 multiplier = (int)chars[i]-96; //mult the number by 26^(position in array) retVal += multiplier * Math.pow(26, i); } return retVal; } 

Asumiendo la columna A = 1

 int GetColumnNumber(string columnName) { int sum = 0; int exponent = 0; for(int i = columnName.Length - 1; i>=0; i--) { sum += (columnName[i] - 'A' + 1) * (GetPower(26, exponent)); exponent++; } return sum; } int GetPower(int number, int exponent) { int power = 1; for(int i=0; i 

Obtenga un nombre de columna de un int en Java ( lea más aquí ):

 public String getColName (int colNum) { String res = ""; int quot = colNum; int rem; /*1. Subtract one from number. *2. Save the mod 26 value. *3. Divide the number by 26, save result. *4. Convert the remainder to a letter. *5. Repeat until the number is zero. *6. Return that bitch... */ while(quot > 0) { quot = quot - 1; rem = quot % 26; quot = quot / 26; //cast to a char and add to the beginning of the string //add 97 to convert to the correct ascii number res = (char)(rem+97) + res; } return res; } 

Advertencia: ambas versiones asumen solo letras mayúsculas de la A a la Z. Cualquier otra cosa causa un error de cálculo. No sería difícil agregar un poco de comprobación de errores y / o mayúsculas para mejorarlos.

Scala

 def excel2Number(excel : String) : Int = (0 /: excel) ((accum, ch) => accum * 26 + ch - 'A' + 1) 

Haskell

 excel2Number :: String -> Int excel2Number = flip foldl 0 $ \accum ch -> accum * 26 + fromEnum ch - fromEnum 'A' + 1 

Otro Delphi uno:

 function ExcelColumnNumberToLetter(col: Integer): string; begin if (col <= 26) then begin Result := Chr(col + 64); end else begin col := col-1; Result := ExcelColumnNumberToLetter(col div 26) + ExcelColumnNumberToLetter((col mod 26) + 1); end; end; 

Otro Java:

 public static int convertNameToIndex(String columnName) { int index = 0; char[] name = columnName.toUpperCase().toCharArray(); for(int i = 0; i < name.length; i++) { index *= 26; index += name[i] - 'A' + 1; } return index; } 

Solución Java fácil ->

 public class ColumnName { public static int colIndex(String col) { int index=0; int mul=0; for(int i=col.length()-1;i>=0;i--) { index += (col.charAt(i)-64) * Math.pow(26, mul); mul++; } return index; } public static void main(String[] args) { System.out.println(colIndex("AAA")); } 

¿Ayuda pensar en la cadena como el reverso del número de columna en la base 26 con dígitos representados por A, B, … Z?

Esto es básicamente un número en la base 26, con la diferencia de que el número no usa 0-9 y luego letras, pero solo letras.

Aquí hay uno de CFML:

           #ColToNum('AABCCE')#  

Y porque estoy de un humor extraño, aquí hay una versión de CFScript:

 function ColToNum ( Input ) { var Total = 0; for ( var Pos = 1 ; Pos <= Len(Arguments.Input) ; Pos++ ) { Total += 26^(Pos-1) * ( Asc( UCase( Mid(Arguments.Input,Pos,1) ) ) - 64 ); } return Total; } WriteOutput( ColToNum('AABCCE') ); 

otro ejemplo erlang [más críptico]:

 col2int(String) -> col2int(0,String). col2int(X,[A|L]) when A >= 65, A =< 90 -> col2int(26 * X + A - 65 + 1, L); col2int(X,[]) -> X. 

y función inversa:

 int2col(Y) when Y > 0 -> int2col(Y,[]). int2col(0,L) -> L; int2col(Y,L) when Y rem 26 == 0 -> int2col(Y div 26 - 1,[(26+65-1)|L]); int2col(Y,L) -> P = Y rem 26, int2col((Y - P) div 26,[P + 65-1|L]). 

Delphi:

 // convert EXcel column name to column number 1..256 // case-sensitive; returns 0 for illegal column name function cmColmAlfaToNumb( const qSRC : string ) : integer; var II : integer; begin result := 0; for II := 1 to length(qSRC) do begin if (qSRC[II]<'A')or(qSRC[II]>'Z') then begin result := 0; exit; end; result := result*26+ord(qSRC[II])-ord('A')+1; end; if result>256 then result := 0; end; 

-Alabama.

Ligeramente relacionado, el mejor desafío es al revés: dado el número de columna, encuentre la etiqueta de la columna como cadena.

Versión de Qt como lo que implementé para KOffice:

 QString columnLabel( unsigned column ) { QString str; unsigned digits = 1; unsigned offset = 0; column--; for( unsigned limit = 26; column >= limit+offset; limit *= 26, digits++ ) offset += limit; for( unsigned c = column - offset; digits; --digits, c/=26 ) str.prepend( QChar( 'A' + (c%26) ) ); return str; } 

Common Lisp:

 (defun excel->number (string) "Converts an Excel column name to a column number." (reduce (lambda (ab) (+ (* a 26) b)) string :key (lambda (x) (- (char-int x) 64)))) 

editar: la operación inversa:

 (defun number->excel (number &optional acc) "Converts a column number to Excel column name." (if (zerop number) (concatenate 'string acc) (multiple-value-bind (rest current) (floor number 26) (if (zerop current) (number->excel (- rest 1) (cons #\Z acc)) (number->excel rest (cons (code-char (+ current 64)) acc)))))) 

Esta versión es puramente funcional y permite secuencias alternativas de ‘código’, por ejemplo, si solo quiere utilizar las letras ‘A’ a ‘C’. En Scala, con una sugerencia de dcsobral.

 def columnNumber(name: String) = { val code = 'A' to 'Z' name.foldLeft(0) { (sum, letter) => (sum * code.length) + (code.indexOf(letter) + 1) } } 
 def ExcelColumnToNumber(ColumnName): ColNum = 0 for i in range(0, len(ColumnName)): # Easier once formula determined: 'PositionValue * Base^Position' # ie AA=(1*26^1)+(1*26^0) or 792=(7*10^2)+(9*10^1)+(2*10^0) ColNum += (int(ColumnName[i],36) -9) * (pow(26, len(ColumnName)-i-1)) return ColNum 

ps Mi primer script Python!

En Mathematica:

 FromDigits[ToCharacterCode@# - 64, 26] & 

Usando el increíble código Mathematica de Mr. Wizard, ¡pero deshaciéndose de la críptica función pura!

 columnNumber[name_String] := FromDigits[ToCharacterCode[name] - 64, 26] 

Wikipedia tiene buenas explicaciones y algos

http://en.wikipedia.org/wiki/Hexavigesimal

 public static String toBase26(int value){ // Note: This is a slightly modified version of the Alphabet-only conversion algorithm value = Math.abs(value); String converted = ""; boolean iteration = false; // Repeatedly divide the number by 26 and convert the // remainder into the appropriate letter. do { int remainder = value % 26; // Compensate for the last letter of the series being corrected on 2 or more iterations. if (iteration && value < 25) { remainder--; } converted = (char)(remainder + 'A') + converted; value = (value - remainder) / 26; iteration = true; } while (value > 0); return converted; } 

… solo necesitaba una solución para PHP . Esto es lo que se me ocurrió:

 /** * Calculates the column number for a given column name. * * @param string $columnName the column name: "A", "B", …, "Y", "Z", "AA", "AB" … "AZ", "BA", … "ZZ", "AAA", … * * @return int the column number for the given column name: 1 for "A", 2 for "B", …, 25 for "Y", 26 for "Z", 27 for "AA", … 52 for "AZ", 53 for "BA", … 703 for "AAA", … */ function getColumnNumber($columnName){ // the function's result $columnNumber = 0; // at first we need to lower-case the string because we calculate with the ASCII value of (lower-case) "a" $columnName = strtolower($columnName); // ASCII value of letter "a" $aAsciiValue = ord('a') - 1; // iterate all characters by splitting the column name foreach (str_split($columnName) as $character) { // determine ASCII value of current character and substract with that one from letter "a" $characterNumberValue = ord($character) - $aAsciiValue; // through iteration and multiplying we finally get the previous letters' values on base 26 // then we just add the current character's number value $columnNumber = $columnNumber * 26 + $characterNumberValue; } // return the result return $columnNumber; } 

Por supuesto, el guión puede acortarse un poco combinando solo algunas cosas en una línea de código dentro del ciclo foreach:

 // … $columnNumber = $columnNumber * 26 + ord($character) - ord('a') + 1; // … 

En Python, sin reducir:

 def transform(column_string): return sum((ascii_uppercase.index(letter)+1) * 26**position for position, letter in enumerate(column_string[::-1])) 

Aquí hay otra versión de este código en Python:

 keycode=1 for i in range (1,len(word)): numtest[i]=word[i-1] keycode = keycode*26*int(wordtest[numtest[i]]) last=word[-1:] keycode=keycode+int(wordtest[last]) print(keycode) print(bin(keycode)) #Numtest and wordtest are dictionaries.