لغز البرمجة: كيف يمكنك ترجمة اسم عمود Excel إلى رقم؟

StackOverflow https://stackoverflow.com/questions/763691

  •  11-09-2019
  •  | 
  •  

سؤال

لقد طلبت مؤخرا في مقابلة عمل لحل لغز البرمجة اعتقدت أنه سيكون من المثير للاهتمام المشاركة. إنه يتعلق بترجمة رسائل عمود Excel إلى الأرقام الفعلية، إذا كنت تتذكر، تقوم Excel بأسماء أعمدةها بأحرف من A إلى Z، ثم يذهب التسلسل AA، AB، AC ... AZ، BA، BB، إلخ.

عليك أن تكتب وظيفة تقبل سلسلة كمعلمة (مثل "AABCCE") وإرجاع رقم العمود الفعلي.

يمكن أن يكون الحل بأي لغة.

هل كانت مفيدة؟

المحلول

كتبت هذه الأعمار قبل بعض النصي من 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

نصائح أخرى

يبدو وكأنه تقليل قياسي لي:

بيثون:

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

C #:

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

اقرأ اسم العمود من Stdin وطباعة رقمه المقابل:

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

تحذيرات: يفترض ASCII.

تعديل: استبدالها " مع ' بحيث لن يتم نقل شل الخاص بك $x في السلسلة.

من قبيل الصدفة لقد حلت نفس المشكلة باستخدام جافا سكريبت

$(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));
    });
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<form id="form">
<input type="text" id="number"></input>
    <button>submit</button>
</form>
<p id="answer"></p>

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

http://jsfiddle.net/m7xty/1/

هاه - كتبه بالفعل في قاعدة التعليمات البرمجية لدينا - حوالي 3 أوقات مختلفة :(

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

اللغز هو أنه ليس في الواقع تمثيل Base26 للرقم (نحن ملقاة لأنفسنا في اسم وظيفتنا هنا) لأنه لا يوجد 0 في ذلك.

التسلسل هو: A، B، C ... Z، AA، AB، AC

وليس: A، B، C ... Z، BA، BB، BC

(اللغة هي ارلانج، ميس أوي).

يمكنك القيام بذلك في C مثل هذا:

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

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

  return result;
}

لا يوجد فحص خطأ، يعمل فقط من أجل سلاسل الحالة العليا، يجب أن تكون السلسلة فارغة.

احصل على رقم العمود من اسمه

جافا:

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

على افتراض العمود 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<exponent; i++)
    power *= number;
  return power;
}

احصل على اسم عمود من Int in Java (اقرأ المزيد هنا):

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

التحذير: يفترض كل من هذه الإصدارات أحرف كبيرة فقط A إلى Z. أي شيء آخر يسبب سوء تقدير. لن يكون من الصعب إضافة القليل من فحص الأخطاء و / أو الذنب إلى تحسينها.

سكالا

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

آخر دلفي واحد:

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;

جافا آخر:

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

حل جافا سهل ->

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

}

هل يساعده في التفكير في السلسلة كعاوت من رقم العمود في قاعدة 26 مع أرقام ممثلة بواسطة A، B، ... Z؟

هذا هو أساسا رقم في قاعدة 26، مع الفرق الذي لا يستخدم الرقم 0-9 ثم الحروف ولكن فقط حروف.

إليك واحدة CFML:

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


ولأنني في مزاج غريب، إليك نسخة 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') );

آخر [المزيد من الخفف] 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.

وظيفة معكوس:

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]).

دلفي:

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

ذات صلة قليلا، التحدي الأفضل هو العكس: بالنظر إلى رقم العمود، ابحث عن تسمية العمود كسلسلة.

نسخة QT كما قمت بتنفيذ 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;
}

lisp المشتركة:

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

تعديل: العملية العكسية:

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

هذا الإصدار وظيفي بحت ويسمح بتسلسلات "رمز" بديل، على سبيل المثال إذا كنت ترغب فقط في استخدام الحروف "A" إلى "C". في سكالا، مع اقتراح من 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'
        # 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

سكرتير خاص لي أول بيثون النصي!

في الرياضيات:

FromDigits[ToCharacterCode@# - 64, 26] &

باستخدام رمز Mathematica الرائع للسيد معالج، ولكن التخلص من الوظيفة الخالكة الخفية!

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

ويكيبيديا لديها تفسيرات جيدة والجوز

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

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

... فقط حاجة إلى حل ل بي أتش بي. وبعد هذا هو ما خطرت لي:

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

بالطبع يمكن تقصير البرنامج النصي قليلا من خلال الجمع بين بعض الأشياء في سطر واحد من التعليمات البرمجية داخل حلقة Foreach:

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

في بيثون، دون تقليل:

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

هنا إصدار آخر من هذا الرمز في بيثون:

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.
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top