Comment convertir un entier en chaîne en chiffres romains en C?
Le moyen le plus simple est probablement de configurer trois tableaux pour les cas complexes et d’utiliser une fonction simple comme:
// 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';
}
Ceci gérera tout entier non signé, bien que les grands nombres aient énormément de caractères M
à l'avant et que l'appelant s'assure que leur mémoire tampon est suffisamment grande.
Une fois que le nombre a été réduit à moins de 1000, il s'agit d'une simple recherche dans 3 tables, une pour les centaines, les dizaines et les unités. Par exemple, prenons le cas où val
est 314
.
val/100
sera 3
dans ce cas donc la recherche de tableau huns
donnera CCC
, puis val = val % 100
vous donnera 14
pour la recherche tens
.
Alors val/10
sera 1
dans ce cas donc la recherche de tableau tens
donnera X
, puis val = val % 10
vous donnera 4
pour la recherche ones
.
Alors val
sera 4
dans ce cas donc la recherche de tableau ones
donnera IV
.
Cela vous donne CCCXIV
pour 314
.
Une version de contrôle de débordement de tampon est une étape simple à partir de là:
// 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;
}
bien que, à ce moment-là, vous puissiez penser à refactoriser le traitement de centaines, dizaines et unités dans une fonction distincte car elles sont si similaires. Je vais laisser cela comme un exercice supplémentaire.
n'utilisez pas de carte pré-calculée pour les cas difficiles.
/* 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;
}
vous n'avez pas non plus besoin de déclarer S
. il devrait être facile de voir pourquoi.
#ifndef numberof
#define numberof(A) ((int)(sizeof(A)/sizeof((A)[0])))
#endif
static void roman(char *out, int max, int number)
{
static int romanI[] = { 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 };
static const char *romanA[] = {
"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"
};
int i, v;
out[0] = 0;
for (i = 0; i < numberof(romanI); i++) {
if (number <= 0)
break;
v = romanI[i];
while (number >= v) {
strcat_s(out, max, romanA[i]);
number -= v;
}
}
}
Je pense que ValueConverter est l’une des méthodes les plus élégantes pour convertir un entier en chiffre romain. J'espère que Dante n'est pas trop en colère à propos de cela que je poste son code ici:
public class RomanNumeralizer : IValueConverter
{
private static IList<RomanNumeralPair> _Pairs;
static RomanNumeralizer()
{
var list = new List<RomanNumeralPair>();
list.Add(new RomanNumeralPair(1000, "M"));
list.Add(new RomanNumeralPair(900, "CM"));
list.Add(new RomanNumeralPair(500, "D"));
list.Add(new RomanNumeralPair(400, "CD"));
list.Add(new RomanNumeralPair(100, "C"));
list.Add(new RomanNumeralPair(90, "XC"));
list.Add(new RomanNumeralPair(50, "L"));
list.Add(new RomanNumeralPair(40, "XL"));
list.Add(new RomanNumeralPair(10, "X"));
list.Add(new RomanNumeralPair(9, "IX"));
list.Add(new RomanNumeralPair(5, "V"));
list.Add(new RomanNumeralPair(4, "IV"));
list.Add(new RomanNumeralPair(1, "I"));
_Pairs = list.AsReadOnly();
}
private IList<RomanNumeralPair> PairSet
{
get
{
return _Pairs;
}
}
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return ConvertToRomanNumeral(System.Convert.ToInt32(value));
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
private string ConvertToRomanNumeral(int input)
{
StringBuilder myBuilder = new StringBuilder();
foreach (RomanNumeralPair thisPair in _Pairs)
{
while (input >= thisPair.Value)
{
myBuilder.Append(thisPair.RomanValue);
input -= thisPair.Value;
}
}
return myBuilder.ToString();
}
}
public class RomanNumeralPair
{
private string _RomanValue;
private int _Value;
public RomanNumeralPair(int value, string stringValue)
{
this._Value = value;
this._RomanValue = stringValue;
}
public string RomanValue
{
get
{
return this._RomanValue;
}
}
public int Value
{
get
{
return this._Value;
}
}
}