web-dev-qa-db-fra.com

Programmation Riddle: Comment pouvez-vous traduire un nom de colonne Excel à un numéro?

J'ai récemment été demandé dans un entretien d'embauche de résoudre un puzzle de programmation que je pensais qu'il serait intéressant de partager. Il s'agit de traduire des lettres de colonne Excel aux nombres réels, si vous vous rappelez, Excel nomme ses colonnes avec des lettres de A à Z, puis la séquence va aa, AB, AC ... AZ, BA, BB, etc.

Vous devez écrire une fonction qui accepte une chaîne en tant que paramètre (comme "AABCCE") et renvoie le numéro de colonne réel.

La solution peut être dans n'importe quelle langue.

46
Pablo Fernandez

J'ai écrit cela il y a un peu Python script:

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
16
clorz

Lisez un nom de colonne de STDIN et imprimez son numéro correspondant:

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

CAVEZIERS: suppose ASCII.

Edit : Remplacé " avec ' Pour que votre coquille ne interpolait pas $x dans la chaîne.

6
j_random_hacker

HAH - écrit déjà dans notre base de code - environ 3 fois différents :(

%% @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).

L'énigme est que ce n'est pas réellement une représentation de base26 d'un nombre (nous nous mentons dans notre nom de la fonction ici) car il n'y a pas de 0.

La séquence est: A, B, C ... Z, AA, AB, AC

et non: a, b, c ... z, ba, bb, bc

(La langue est Erlang, Mais Oui).

4
Gordon Guthrie

Vous pouvez le faire en C comme ceci:

unsigned int coltonum(char * string)
{
   unsigned result = 0;
   char ch;

   while(ch = *string++)
      result = result * 26 + ch - 'A' + 1;

  return result;
}

Aucune vérification des erreurs, fonctionne uniquement pour les chaînes majuscules, la chaîne doit être terminée null.

4
Adam Davis

Obtenez un nom de colonne d'un int in java ( en savoir plus ici ):

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

Un autre 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;
}
1
Tapas Bose

Obtenez le numéro de colonne de son nom

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

Easy Java solution ->

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

}
1
piyush121

CAVEZIER: Ces deux versions n'assument que des lettres majuscules A à Z. Tout ce que d'autre provoque une erreur de calcul. Il ne serait pas difficile d'ajouter un peu de vérification des erreurs et/ou de la majuscule pour les améliorer.

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
1
James Iry

Un autre Delphi:

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;
1
Kobus Smit

En Mathematica:

FromDigits[ToCharacterCode@# - 64, 26] &
0
Mr.Wizard

Aide-t-il à penser à la chaîne comme inverse du numéro de colonne dans la base 26 avec des chiffres représentés par A, B, ... Z?

0
dirkgently

Ceci est fondamentalement un nombre dans la base 26, avec la différence que le nombre n'utilise pas 0-9, puis des lettres mais niquement lettres.

0
Stefan
def ExcelColumnToNumber(ColumnName):
    ColNum = 0
    for i in range(0, len(ColumnName)):
        # Easier once formula determined: 'PositionValue * Base^Position'
        # i.e. 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

p.s. Mon premier Python script!

0
rvarcher

Voici un CFML One:

<cffunction name="ColToNum" returntype="Numeric">
    <cfargument name="Input" type="String" />
    <cfset var Total = 0 />
    <cfset var Pos = 0 />

    <cfloop index="Pos" from="1" to="#Len(Arguments.Input)#">
        <cfset Total += 26^(Pos-1) * ( Asc( UCase( Mid(Arguments.Input,Pos,1) ) ) - 64 ) />
    </cfloop>

    <cfreturn Total />
</cffunction>

<cfoutput>
    #ColToNum('AABCCE')#
</cfoutput>


Et parce que je suis d'une humeur étrange, voici une version 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') );
0
Peter Boughton

Lisp commun:

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

Edit : l'opération inverse:

(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))))))
0
Svante

En python, sans réduction:

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

... Il suffit d'avoir besoin d'une solution pour PHP . C'est ce que je suis venu avec:

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

Bien sûr, le script peut être raccourci un peu en combinant des trucs dans une ligne de code dans la boucle de Foreach:

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

Delphes:

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

-Al.

0
A. I. Breveleri

un autre exemple [plus cryptique] Erlang:

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

et fonction inverse:

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]).
0
Ellery Newcomer

Wikipedia a de bonnes explications et des algues

http://fr.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;    
}
0
Julien

Utilisation du code de Mathematica génial de M. Wizard, mais de vous débarrasser de la fonction pure cryptique!

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

Légèrement connexe, le meilleur défi est l'inverse: étant donné le numéro de colonne, trouvez l'étiquette de colonne en tant que chaîne.

QT Version comme ce que j'ai mis en œuvre pour 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;
}
0
Ariya Hidayat