Как я могу преобразовать целое число в его строковое представление римскими цифрами в C?
Как преобразовать целочисленное значение в строку римских цифр?
Ответы (2)
Самый простой способ, вероятно, настроить три массива для сложных случаев и использовать простую функцию, например:
// convertToRoman:
// In: val: value to convert.
// res: buffer to hold result.
// Out: n/a
// Cav: caller responsible for buffer size.
void convertToRoman (unsigned int val, char *res) {
char *huns[] = {"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"};
char *tens[] = {"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"};
char *ones[] = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"};
int size[] = { 0, 1, 2, 3, 2, 1, 2, 3, 4, 2};
// Add 'M' until we drop below 1000.
while (val >= 1000) {
*res++ = 'M';
val -= 1000;
}
// Add each of the correct elements, adjusting as we go.
strcpy (res, huns[val/100]); res += size[val/100]; val = val % 100;
strcpy (res, tens[val/10]); res += size[val/10]; val = val % 10;
strcpy (res, ones[val]); res += size[val];
// Finish string off.
*res = '\0';
}
Это будет обрабатывать любое целое число без знака, хотя большие числа будут иметь очень много символов M
впереди, и вызывающий должен убедиться, что их буфер достаточно велик.
Как только число становится меньше 1000, выполняется простой поиск по трем таблицам, по одной для сотен, десятков и единиц. Например, возьмем случай, когда val
равно 314
.
val/100
в этом случае будет 3
, поэтому поиск массива huns
даст CCC
, затем val = val % 100
даст вам 14
для поиска tens
.
Тогда val/10
будет 1
в этом случае, поэтому поиск массива tens
даст X
, затем val = val % 10
даст вам 4
для поиска ones
.
Тогда val
будет 4
в этом случае, поэтому поиск массива ones
даст IV
.
Это дает вам CCCXIV
за 314
.
Версия с проверкой переполнения буфера — это простой шаг вперед:
// convertToRoman:
// In: val: value to convert.
// res: buffer to hold result.
// Out: returns 0 if not enough space, else 1.
// Cav: n/a
int convertToRoman (unsigned int val, char *res, size_t sz) {
char *huns[] = {"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"};
char *tens[] = {"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"};
char *ones[] = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"};
int size[] = { 0, 1, 2, 3, 2, 1, 2, 3, 4, 2};
// Add 'M' until we drop below 1000.
while (val >= 1000) {
if (sz-- < 1) return 0;
*res++ = 'M';
val -= 1000;
}
// Add each of the correct elements, adjusting as we go.
if (sz < size[val/100]) return 0;
sz -= size[val/100];
strcpy (res, huns[val/100]);
res += size[val/100];
val = val % 100;
if (sz < size[val/10]) return 0;
sz -= size[val/10];
strcpy (res, tens[val/10]);
res += size[val/10];
val = val % 10;
if (sz < size[val) return 0;
sz -= size[val];
strcpy (res, ones[val]);
res += size[val];
// Finish string off.
if (sz < 1) return 0;
*res = '\0';
return 1;
}
хотя на тот момент вы могли бы подумать о рефакторинге обработки сотен, десятков и единиц в отдельную функцию, поскольку они так похожи. Я оставлю это как дополнительное упражнение.
не используйте предварительно рассчитанную карту для сложных случаев.
/* roman.c */
#include <stdio.h>
/* LH(1) roman numeral conversion */
int RN_LH1 (char *buf, const size_t maxlen, int n)
{
int S[] = { 0, 2, 4, 2, 4, 2, 4 };
int D[] = { 1000, 500, 100, 50, 10, 5, 1 };
char C[] = { 'M', 'D', 'C', 'L', 'X', 'V', 'I' };
const size_t L = sizeof(D) / sizeof(int) - 1;
size_t k = 0; /* index into output buffer */
int i = 0; /* index into maps */
int r, r2;
while (n > 0) {
if (D[i] <= n) {
r = n / D[i];
n = n - (r * D[i]);
/* lookahead */
r2 = n / D[i+1];
if (i < L && r2 >= S[i+1]) {
/* will violate repeat boundary on next pass */
n = n - (r2 * D[i+1]);
if (k < maxlen) buf[k++] = C[i+1];
if (k < maxlen) buf[k++] = C[i-1];
}
else if (S[i] && r >= S[i]) {
/* violated repeat boundary on this pass */
if (k < maxlen) buf[k++] = C[i];
if (k < maxlen) buf[k++] = C[i-1];
}
else
while (r-- > 0 && k < maxlen)
buf[k++] = C[i];
}
i++;
}
if (k < maxlen) buf[k] = '\0';
return k;
}
/* gcc -Wall -ansi roman.c */
int main (int argc, char **argv)
{
char buf[1024] = {'\0'};
size_t len;
int k;
for (k = 1991; k < 2047; k++)
{
len = RN_LH1(buf, 1023, k);
printf("%3lu % 4d %s\n", len, k, buf);
}
return 0;
}
на самом деле вам также не нужно объявлять S
. должно быть легко понять, почему.