问题
I'm working on an Excel spreadsheet, and I have to use only one type of formula for a huge amount of data. Since in the formula the only necessary changes concern letters, I was wondering if there is a way to make a program that increments them following the Excel columns order (A, B, C...Z; AA, AB, AC...AZ; BA, BB, BC...BZ).
In my case, I would need to increment letters each time by five, so here is the kind of code I'm trying to obtain:
#include <iostream>
using namespace std;
int main() {
char x = 'B';
char y = 'E';
for (int z = 1; z < 2255; z++) {
cout << "=SUMPRODUCT(SUBTOTAL(4,OFFSET(" << x << "1:" << y << "1,ROW(" << x << "1:" << x << "100)-ROW(" << x << "1),)))" << endl;
x = x + 5;
y= y + 5;
}
return 0;
}
Of course it won't work because it goes over 'z', but still, is there a way to do this?
回答1:
General solution descriptions
Solution 1: Create base-26 system itself:
Assume that you have 26 letters. So first lets make 26 number system. We use 1 byte for each digit. We create an array of digits, and then we need to adjust sometimes when adding exceeds 26.
Let's assume your current digit is 25. We add 7 to it, and then we need to handle the overflow, assuming 256 (1 byte) as maximum, and our digit limit is 26. Hence the adjustment will be 256-26=230
. (We do this calculation on short (16 bits), so we get overflow at 26+7=33 -> 33+230=263
. Therefore, the higher byte will be 1, the lower byte will be 7.)
Having calculated the overflow threshold (above it was 1), we can then add it to next coming digit and do the same if overflow occur.
Finally, for display we just add 65 ('A') to each of the two bytes. Our last byte will be '\0' null terminating so we could turn it into a string.
Solution 2 Perform all calculations and then convert it to a 26-base number:
In this case,
number/26 = x
and
remainder r1 = (number%26)
We store r1 to a byte.
x/26 = x1
and
remainder r2 = (x%26)
We store r2 to the next byte.
x1/26 = x2
and
remainder r3 = (x%26)
We store r3 to the next byte. We get a string r3 r2 r1 '\0'
and then add 65 'A' to each byte.
回答2:
You could do it in two nested loops, both from 'A'
to 'Z'
, then just append both characters together. Put the results in a vector.
To get the single-letter 'A'
to 'Z'
, add them to the vector before the nested loops.
回答3:
Do this:
x = (x + 5 > 'Z' ? x + 5 - 'Z' + 'A' : x + 5);
y = (y + 5 > 'Z' ? y + 5 - 'Z' + 'A' : y + 5);
回答4:
The result you're trying to produce can be viewed as a base-27 number, using the characters 'A' through 'Z' as the 26 digits. It is a little unusual, in that 'A' is really '0', but when you get to a two-digit number, it's "AA", (where a normal two-digit would be BA
-- i.e., just like the next number after 9 is 10
). We have to do a fairly minor adjustment to compensate for that oddity.
You can do the counting with a normal int
(or whatever) and then convert to base 27 when needed. Since I don't think it supports more columns than that, I've restricted this version to 2 digits, though extending to more would be fairly trivial:
std::string to_excel_column(int input) {
int digit1 = input % 26;
int digit2 = input / 26;
std::string result;
if (input > 26)
result.push_back(digit2 + 'A' - 1);
result.push_back(digit1 + 'A');
return result;
}
回答5:
You could, as one option, create a new data type consisting of two characters (something like std::pair<char,char>
and define arithmetics for this.
However, I think it would be simpler to keep using int
(or unsigned int
) for the counting and arithmetic, and define a function to_col_heading
which translates the integer into a one/two-character column heading for Excel:
#include <string>
#include <iostream>
#include <stdexcept>
#include <iterator>
/* This is the function that performs
the transformation. */
std::string to_col_heading(unsigned i)
{
if (i > 26+26*26)
throw std::overflow_error
("Value too large for transformation into Excel column heading.");
char char1 = 'A' + (i / 26) - 1;
char char2 = 'A' + (i % 26);
if (char1 < 'A')
return std::string(&char2,&char2 + 1);
else
return std::string(&char1,&char1 + 1) + char2;
}
int main()
{
std::ostream_iterator<std::string> out(std::cout,", ");
for (unsigned i = 0 ; i < 26+26*26 ; i += 5)
*out = to_col_heading(i);
std::cout << std::endl;
return 0;
}
Output:
A, F, K, P, U, Z, AE, AJ, AO, AT, AY, BD, BI, BN, BS, BX, CC, CH, CM, CR, CW, DB, DG, DL, DQ, DV, EA, EF, EK, EP, EU, EZ, FE, FJ, FO, FT, FY, GD, GI, GN, GS, GX, HC, HH, HM, HR, HW, IB, IG, IL, IQ, IV, JA, JF, JK, JP, JU, JZ, KE, KJ, KO, KT, KY, LD, LI, LN, LS, LX, MC, MH, MM, MR, MW, NB, NG, NL, NQ, NV, OA, OF, OK, OP, OU, OZ, PE, PJ, PO, PT, PY, QD, QI, QN, QS, QX, RC, RH, RM, RR, RW, SB, SG, SL, SQ, SV, TA, TF, TK, TP, TU, TZ, UE, UJ, UO, UT, UY, VD, VI, VN, VS, VX, WC, WH, WM, WR, WW, XB, XG, XL, XQ, XV, YA, YF, YK, YP, YU, YZ, ZE, ZJ, ZO, ZT, ZY,
To support up to 3 (alphabetical) digits for the column headings, the function below works. It handles the first 26 values completely separately to simplify the calculation of the rest. I've also introduced some static constants to deal in a more consistent way with the powers of 26. For three digits, you need 260, 261 and 262, but the system can be expanded to cover any number of digits.
I am sure there a ways to optimize this for speed; this was not the focus here.
std::string to_col_heading(unsigned i)
{
const static unsigned pow1 = 26;
const static unsigned pow2 = 26*26;
const static unsigned max = pow1 + pow2 + 26 * pow2;
if (i < pow1)
{
char c = 'A' + i % pow1;
return std::string(&c,&c+1);
}
if (i > max)
throw std::overflow_error
("Value too large for transformation into Excel column heading.");
i -= pow1;
char char1 = 'A' + (i / pow2) - 1;
char char2 = 'A' + (i%pow2) / pow1;
char char3 = 'A' + i % pow1;
if (char1 < 'A')
return std::string(&char2,&char2 + 1) + char3;
else
return std::string(&char1,&char1 + 1) + char2 + char3;
}
来源:https://stackoverflow.com/questions/17334729/how-to-increment-letter-combinations-in-c-beyond-z