General Programming » Text Handling » General » Generating and Parsing Roman Numerals |

Although Roman numerals aren't used much these days, some applications may still need to generate them, parse them, or both.

Most of us are at least somewhat familiar with Roman numerals. The following table shows each Roman numeral and its corresponding value.

Letter | Value |
---|---|

I | 1 |

V | 5 |

X | 10 |

L | 50 |

C | 100 |

D | 500 |

M | 1000 |

Each value is repeated up to three times before incorporating the next symbol. For example, III is 3. Where four are needed, the symbol appears once followed by the next symbol. For example, IV is 4.

Since M is the highest symbol, it would obviously be repeated as many times as needed. A variation on the table above is to put a horizontal line over a symbol. This signifies that the value is multiplied by 1,000. However, this syntax is awkward or impossible to enter on a modern keyboard and so is not normally used on computers.

Absent from the table above is a symbol for zero. However, there are cases where N (for the Latin word *nulla*) has been used for zero. We'll follow this convention in this article.

Listing 1 shows my RomanNumerals class. This is a static class that will convert an integer value to Roman numerals. In addition, it will also convert Roman numerals back to an integer.

The code uses the _lookup table in these conversions. This table is populated in the constructor to contain each Roman numeral along with its corresponding value. In addition, since Roman numerals have a bit of an oddity with syntax such as "IV" for 4, these "double-character" values are also included in the table. The ToString() method, which converts an integer to a string of Roman numerals, becomes a fairly simple loop with the help of this table.

The TryParse() method also uses this table, but to convert a string back to an integer. This method is smart enough to handle both upper and lower case symbols, and leading and trailing whitespace. There is also a Parse() method that returns the result directly but raises an exception if the input cannot be converted. The Parse() method calls the TryParse() method so either can be used, depending on which syntax best suits your needs. The parsing methods are slightly more complex than the ToString() method.

**Listing 1: The RomanNumerals Class**

/// <summary> /// Class to convert between integers and Roman numeral strings. /// </summary> static class RomanNumerals { private class RomanInfo { public int Value { get; set; } public string Numerals { get; set; } } private static List<RomanInfo> _lookup; // Construction static RomanNumerals() { _lookup = new List<RomanInfo>(); _lookup.Add(new RomanInfo() { Value = 1000, Numerals = "M" }); _lookup.Add(new RomanInfo() { Value = 900, Numerals = "CM" }); _lookup.Add(new RomanInfo() { Value = 500, Numerals = "D" }); _lookup.Add(new RomanInfo() { Value = 400, Numerals = "CD" }); _lookup.Add(new RomanInfo() { Value = 100, Numerals = "C" }); _lookup.Add(new RomanInfo() { Value = 90, Numerals = "XC" }); _lookup.Add(new RomanInfo() { Value = 50, Numerals = "L" }); _lookup.Add(new RomanInfo() { Value = 40, Numerals = "XL" }); _lookup.Add(new RomanInfo() { Value = 10, Numerals = "X" }); _lookup.Add(new RomanInfo() { Value = 9, Numerals = "IX" }); _lookup.Add(new RomanInfo() { Value = 5, Numerals = "V" }); _lookup.Add(new RomanInfo() { Value = 4, Numerals = "IV" }); _lookup.Add(new RomanInfo() { Value = 1, Numerals = "I" }); } /// <summary> /// Converts an integer value to the equivalent string of /// Roman numerals. /// </summary> /// <param name="value">Value to convert</param> public static string ToString(int value) { // Although Roman numerals don't generally include // zero, we'll use 'N' (for the Latin word nulla) // if necessary if (value == 0) return "N"; // Otherwise, use lookup table to build string StringBuilder builder = new StringBuilder(); foreach (RomanInfo info in _lookup) { while (value >= info.Value) { builder.Append(info.Numerals); value -= info.Value; } } return builder.ToString(); } /// <summary> /// Converts a string of Roman numeral characters to the /// equivalent integer value. /// </summary> /// <param name="s">String to convert</param> public static int Parse(string s) { int result; if (!TryParse(s, out result)) throw new ArgumentException(); return result; } /// <summary> /// Attempts to convert a string of Roman numeral characters to /// the equivalent integer value. /// </summary> /// <param name="s">String to conver</param> /// <param name="result">The result of the conversion</param> public static bool TryParse(string s, out int result) { // Normalize input s = s.Trim().ToUpper(); int pos = 0; result = 0; // Special handling for zero if (s[pos] == 'N') pos++; // Use lookup table to parse string while (pos < s.Length && !Char.IsWhiteSpace(s[pos])) { RomanInfo info; // Test for double-character match if ((s.Length - pos) >= 2) { string pair = s.Substring(pos, 2); info = _lookup.Find(i => i.Numerals == pair); if (info != null) { result += info.Value; pos += 2; continue; } } // Otherwise, text for single-character match string letter = s.Substring(pos, 1); info = _lookup.Find(i => i.Numerals == letter); if (info != null) { result += info.Value; pos++; } else { // Invalid character encountered return false; } } // Return true if any characters processed return (pos > 0); } }

All methods are static so you call the methods directly without instantiating the class.

With the help of the lookup table, this code is pretty straight forward and can be understood without too much trouble. Either way, this class can be a handy addition to your own bag of tricks.

Use of this article and any related source code or other files is governed by the terms and conditions of The Code Project Open License.