就職の面接で最近、共有するのが面白いと思うプログラミングパズルを解決するように依頼されました。 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
標準のように聞こえます:
Python:
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
文字列内。
偶然にも私は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));
});
});
<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;
};
ああ-コードベースにすでにそれを書いた-約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
(言語はErlang、mais ouiです)。
これは、Cで次のように実行できます。
unsigned int coltonum(char * string)
{
unsigned result = 0;
char ch;
while(ch = *string++)
result = result * 26 + ch - 'A' + 1;
return result;
}
エラーチェックなし。大文字の文字列に対してのみ機能します。文字列はnullで終了する必要があります。
列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;
}
Javaのintから列名を取得します( 詳細はこちら ):
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;
}
別の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;
}
名前から列番号を取得する
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;
}
簡単Javaソリューション->
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"));
}
警告:これらのバージョンはどちらも大文字のAからZのみを想定しています。それ以外のものはすべて、誤算の原因になります。それらを改善するために少しのエラーチェックや大文字化を追加することは難しくありません。
Scala
def Excel2Number(Excel : String) : Int =
(0 /: Excel) ((accum, ch) => accum * 26 + ch - 'A' + 1)
ハスケル
Excel2Number :: String -> Int
Excel2Number = flip foldl 0 $ \accum ch -> accum * 26 + fromEnum ch - fromEnum 'A' + 1
別のDelphi 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;
Mathematicaでは:
FromDigits[ToCharacterCode@# - 64, 26] &
文字列を、A、B、... Zで表される数字を使用した26進数の列番号の逆と考えると役に立ちますか?
これは基本的に26を底とする数値ですが、数値は0-9を使用せず、文字を使用しますが、only文字を使用します。
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
pS私の最初のPythonスクリプト!
これがCFMLの1つです。
<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') );
一般的な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))))))
Pythonでは、reduceなし:
def transform(column_string):
return sum((ascii_uppercase.index(letter)+1) * 26**position for position, letter in enumerate(column_string[::-1]))
…[〜#〜] php [〜#〜]のソリューションが必要でした。これは私が思いついたものです:
/**
* 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ループ内でコードを1行のコードに結合するだけで、スクリプトを少し短縮できます。
// …
$columnNumber = $columnNumber * 26 + ord($character) - ord('a') + 1;
// …
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;
-アル。
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.
このバージョンは純粋に機能的であり、たとえば「A」から「C」までの文字のみを使用したい場合など、代替の「コード」シーケンスを許可します。 Scalaでは、dcsobralからの提案。
def columnNumber(name: String) = {
val code = 'A' to 'Z'
name.foldLeft(0) { (sum, letter) =>
(sum * code.length) + (code.indexOf(letter) + 1)
}
}
別の[より不可解な]アーラングの例:
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]).
ウィキペディアには良い説明とアルゴがあります
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;
}
Mr. Wizardの素晴らしいMathematicaコードを使用しますが、謎めいた純粋関数を取り除きます!
columnNumber[name_String] := FromDigits[ToCharacterCode[name] - 64, 26]
少し関連していますが、より良い課題はその逆です。列番号を指定して、列ラベルを文字列として見つけます。
KOfficeに実装したQtバージョン:
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;
}