罗马数字到整数

我有一个转移产品,不幸的是必须与产品名称匹配。 这里最大的问题是我可能会因为罗马数字而获得重复的产品。 有时,相同的产品将使用罗马数字命名,有时则是常规产品。

我正在谷歌搜索可能已经制作的字符串函数来转换它,但没有运气。 我想我自己也不会那么难,但我希望听到一些关于如何处理这种情况的意见,而且如果你知道一个已经完成的function,那就命名吧。

编辑:这些产品是移动设备。 示例 – 三星Galaxy SII – 三星Galaxy S2

我注意到了一些非常复杂的解决方案,但这是一个非常简单的问题。 我提出了一个解决方案,避免了对“exception”(IV,IX,XL等)进行硬编码的需要。 我使用for循环来outlook罗马数字字符串中的下一个字符,看看是否应该减去与数字相关联的数字或将其添加到总数中。 为简单起见,我假设所有输入都是有效的。

 private static Dictionary RomanMap = new Dictionary() { {'I', 1}, {'V', 5}, {'X', 10}, {'L', 50}, {'C', 100}, {'D', 500}, {'M', 1000} }; public static int RomanToInteger(string roman) { int number = 0; for (int i = 0; i < roman.Length; i++) { if (i + 1 < roman.Length && RomanMap[roman[i]] < RomanMap[roman[i + 1]]) { number -= RomanMap[roman[i]]; } else { number += RomanMap[roman[i]]; } } return number; } 

我最初尝试在字符串上使用foreach ,我认为这是一个稍微更易读的解决方案但是我最后添加了每一个数字并在之后两次减去它,如果它被certificate是exception之一,我不喜欢。 无论如何我都会在这里发帖给后人。

 public static int RomanToInteger(string roman) { int number = 0; char previousChar = roman[0]; foreach(char currentChar in roman) { number += RomanMap[currentChar]; if(RomanMap[previousChar] < RomanMap[currentChar]) { number -= RomanMap[previousChar] * 2; } previousChar = currentChar; } return number; } 

一个更简单易读的C#实现:

  • 映射I到1,V到5,X到10,L到50,C到100,D到500,M到1000。
  • 使用一个单独的foreach循环( foreach有意使用,前一个值保持)。
  • 将映射的数字添加到总数中。
  • 减去之前添加的数字的两倍,如果我在V或X之前,在L或C之前的X,在D或M之前的C(此处不允许所有的字符!)。
  • 返回0(未使用罗马数字)空字符串,错误字母或不允许使用字符用于减法。
  • 注意:它仍然没有完全完成,我们没有检查有效输入字符串的所有可能条件!

码:

 private static Dictionary _romanMap = new Dictionary { {'I', 1}, {'V', 5}, {'X', 10}, {'L', 50}, {'C', 100}, {'D', 500}, {'M', 1000} }; public static int ConvertRomanToNumber(string text) { int totalValue = 0, prevValue = 0; foreach (var c in text) { if (!_romanMap.ContainsKey(c)) return 0; var crtValue = _romanMap[c]; totalValue += crtValue; if (prevValue != 0 && prevValue < crtValue) { if (prevValue == 1 && (crtValue == 5 || crtValue == 10) || prevValue == 10 && (crtValue == 50 || crtValue == 100) || prevValue == 100 && (crtValue == 500 || crtValue == 1000)) totalValue -= 2 * prevValue; else return 0; } prevValue = crtValue; } return totalValue; } 

我刚刚编写了一个简单的罗马数字转换器,但它没有进行大量的错误检查,但它似乎适用于我可以抛出的所有格式正确的东西。

 public class RomanNumber { public string Numeral { get; set; } public int Value { get; set; } public int Hierarchy { get; set; } } public List RomanNumbers = new List { new RomanNumber {Numeral = "M", Value = 1000, Hierarchy = 4}, //{"CM", 900}, new RomanNumber {Numeral = "D", Value = 500, Hierarchy = 4}, //{"CD", 400}, new RomanNumber {Numeral = "C", Value = 100, Hierarchy = 3}, //{"XC", 90}, new RomanNumber {Numeral = "L", Value = 50, Hierarchy = 3}, //{"XL", 40}, new RomanNumber {Numeral = "X", Value = 10, Hierarchy = 2}, //{"IX", 9}, new RomanNumber {Numeral = "V", Value = 5, Hierarchy = 2}, //{"IV", 4}, new RomanNumber {Numeral = "I", Value = 1, Hierarchy = 1} }; ///  /// Converts the roman numeral to int, assumption roman numeral is properly formatted. ///  /// The roman numeral string. ///  private int ConvertRomanNumeralToInt(string romanNumeralString) { if (romanNumeralString == null) return int.MinValue; var total = 0; for (var i = 0; i < romanNumeralString.Length; i++) { // get current value var current = romanNumeralString[i].ToString(); var curRomanNum = RomanNumbers.First(rn => rn.Numeral.ToUpper() == current.ToUpper()); // last number just add the value and exit if (i + 1 == romanNumeralString.Length) { total += curRomanNum.Value; break; } // check for exceptions IV, IX, XL, XC etc var next = romanNumeralString[i + 1].ToString(); var nextRomanNum = RomanNumbers.First(rn => rn.Numeral.ToUpper() == next.ToUpper()); // exception found if (curRomanNum.Hierarchy == (nextRomanNum.Hierarchy - 1)) { total += nextRomanNum.Value - curRomanNum.Value; i++; } else { total += curRomanNum.Value; } } return total; } 

这是我的解决方案

 public int SimplerConverter(string number) { number = number.ToUpper(); var result = 0; foreach (var letter in number) { result += ConvertLetterToNumber(letter); } if (number.Contains("IV")|| number.Contains("IX")) result -= 2; if (number.Contains("XL")|| number.Contains("XC")) result -= 20; if (number.Contains("CD")|| number.Contains("CM")) result -= 200; return result; } private int ConvertLetterToNumber(char letter) { switch (letter) { case 'M': { return 1000; } case 'D': { return 500; } case 'C': { return 100; } case 'L': { return 50; } case 'X': { return 10; } case 'V': { return 5; } case 'I': { return 1; } default: { throw new ArgumentException("Ivalid charakter"); } } } 

System.Linq上借了很多这个。 String实现IEnumerable ,所以我认为这是合适的,因为我们无论如何都将它视为一个可枚举的对象。 针对一堆随机数进行测试,包括1,3,4,8,83,99,404,555,846,927,1999,2420。

  public static IDictionary CharValues { get { return new Dictionary {{'I', 1}, {'V', 5}, {'X', 10}, {'L', 50}, {'C', 100}, {'D', 500}, {'M', 1000}}; } } public static int RomanNumeralToInteger(IEnumerable romanNumerals) { int retVal = 0; //go backwards for (int i = romanNumerals.Count() - 1; i >= 0; i--) { //get current character char c = romanNumerals.ElementAt(i); //error checking if (!CharValues.ContainsKey(c)) throw new InvalidRomanNumeralCharacterException(c); //determine if we are adding or subtracting bool op = romanNumerals.Skip(i).Any(rn => CharValues[rn] > CharValues[c]); //then do so retVal = op ? retVal - CharValues[c] : retVal + CharValues[c]; } return retVal; } 

我在这里寻找一个罗马数字解析器的一个小实现,但是在尺寸和优雅方面提供的答案并不满意。 我在这里留下最后的递归实现,以帮助其他人搜索一个小实现。


通过递归转换罗马数字

  • 该算法也能够使用非相邻数字(fe XIIX )。
  • 此实现可能仅适用于格式良好(字符串匹配/[mdclxvi]*/i )罗马数字。
  • 该实现未针对速度进行优化。
 // returns the value for a roman literal private static int romanValue(int index) { int basefactor = ((index % 2) * 4 + 1); // either 1 or 5... // ...multiplied with the exponentation of 10, if the literal is `x` or higher return index > 1 ? (int) (basefactor * System.Math.Pow(10.0, index / 2)) : basefactor; } public static int FromRoman(string roman) { roman = roman.ToLower(); string literals = "mdclxvi"; int value = 0, index = 0; foreach (char literal in literals) { value = romanValue(literals.Length - literals.IndexOf(literal) - 1); index = roman.IndexOf(literal); if (index > -1) return FromRoman(roman.Substring(index + 1)) + (index > 0 ? value - FromRoman(roman.Substring(0, index)) : value); } return 0; } 

它是如何工作的?

此算法通过从罗马数字中取最高值并递归地加上/减去文字剩余左/右部分的值来计算罗马数字的值。

 ii X iiv # Pick the greatest value in the literal `iixiiv` (symbolized by uppercase) 

然后递归地重新评估并减去左侧并添加右侧:

 (iiv) + x - (ii) # Subtract the lefthand-side, add the righthand-side (V - (ii)) + x - ((I) + i) # Pick the greatest values, again (v - ((I) + i)) + x - ((i) + i) # Pick the greatest value of the last numeral compound 

最后,数字由它们的整数值代替:

 (5 - ((1) + 1)) + 10 - ((1) + 1) (5 - (2)) + 10 - (2) 3 + 10 - 2 = 11 

我将通过在.net中使用数组来建议一个最简单的方法:在C#部分给出注释以供解释

VB.net

 Public Class Form1 Dim indx() As Integer = {1, 2, 3, 4, 5, 10, 50, 100, 500, 1000} Dim row() As String = {"I", "II", "III", "IV", "V", "X", "L", "C", "D", "M"} Dim limit As Integer = 9 Dim output As String = "" Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Dim num As Integer output = "" num = CInt(txt1.Text) While num > 0 num = find(num) End While txt2.Text = output End Sub Public Function find(ByVal Num As Integer) As Integer Dim i As Integer = 0 While indx(i) <= Num i += 1 End While If i <> 0 Then limit = i - 1 Else limit = 0 End If output = output & row(limit) Num = Num - indx(limit) Return Num End Function End Class 

C#

 using Microsoft.VisualBasic; using System; using System.Collections; using System.Collections.Generic; using System.Data; using System.Diagnostics; public class Form1 { int[] indx = { 1, 2, 3, 4, 5, 10, 50, 100, 500, 1000 // initialize array of integers }; string[] row = { "I", "II", "III", "IV", "V", "X", "L", "C", "D", "M" //Carasponding roman letters in for the numbers in the array }; // integer to indicate the position index for link two arrays int limit = 9; //string to store output string output = ""; private void Button1_Click(System.Object sender, System.EventArgs e) { int num = 0; // stores the input output = ""; // clear output before processing num = Convert.ToInt32(txt1.Text); // get integer value from the textbox //Loop until the value became 0 while (num > 0) { num = find(num); //call function for processing } txt2.Text = output; // display the output in text2 } public int find(int Num) { int i = 0; // loop variable initialized with 0 //Loop until the indx(i).value greater than or equal to num while (indx(i) <= Num) { i += 1; } // detemine the value of limit depends on the itetration if (i != 0) { limit = i - 1; } else { limit = 0; } output = output + row(limit); //row(limit) is appended with the output Num = Num - indx(limit); // calculate next num value return Num; //return num value for next itetration } } 

我在这个博客中提到。 您可以反转罗马数字,然后与进行比较相比,所有事情都会更容易。
public static int pairConversion(int dec,int lastNum,int lastDec){if(lastNum> dec)return lastDec – dec; 否则返回lastDec + dec; }

  public static int ConvertRomanNumtoInt(string strRomanValue) { var dec = 0; var lastNum = 0; foreach (var c in strRomanValue.Reverse()) { switch (c) { case 'I': dec = pairConversion(1, lastNum, dec); lastNum = 1; break; case 'V': dec=pairConversion(5,lastNum, dec); lastNum = 5; break; case 'X': dec = pairConversion(10, lastNum, dec); lastNum = 10; break; case 'L': dec = pairConversion(50, lastNum, dec); lastNum = 50; break; case 'C': dec = pairConversion(100, lastNum, dec); lastNum = 100; break; case 'D': dec = pairConversion(500, lastNum, dec); lastNum = 500; break; case 'M': dec = pairConversion(1000, lastNum, dec); lastNum = 1000; break; } } return dec; } 

这个使用堆栈:

  public int RomanToInt(string s) { var dict = new Dictionary(); dict['I'] = 1; dict['V'] = 5; dict['X'] = 10; dict['L'] = 50; dict['C'] = 100; dict['D'] = 500; dict['M'] = 1000; Stack st = new Stack(); foreach (char ch in s.ToCharArray()) st.Push(ch); int result = 0; while (st.Count > 0) { var c1=st.Pop(); var ch1 = dict[c1]; if (st.Count > 0) { var c2 = st.Peek(); var ch2 = dict[c2]; if (ch2 < ch1) { result += (ch1 - ch2); st.Pop(); } else { result += ch1; } } else { result += ch1; } } return result; } 

我只是使用数组写了这个。
我在这里省略了测试代码,但看起来它可以正常工作。

 public static class RomanNumber { static string[] units = { "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX" }; static string[] tens = { "", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC" }; static string[] hundreds = { "", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM" }; static string[] thousands = { "", "M", "MM", "MMM" }; static public bool IsRomanNumber(string source) { try { return RomanNumberToInt(source) > 0; } catch { return false; } } ///  /// Parses a string containing a roman number. ///  /// source string /// The integer value of the parsed roman numeral ///  /// Throws an exception on invalid source. /// Throws an exception if source is not a valid roman number. /// Supports roman numbers from "I" to "MMMCMXCIX" ( 1 to 3999 ) /// NOTE : "IMMM" is not valid public static int RomanNumberToInt(string source) { if (String.IsNullOrWhiteSpace(source)) { throw new ArgumentNullException(); } int total = 0; string buffer = source; // parse the last four characters in the string // each time we check the buffer against a number array, // starting from units up to thousands // we quit as soon as there are no remaing characters to parse total += RipOff(buffer, units, out buffer); if (buffer != null) { total += (RipOff(buffer, tens, out buffer)) * 10; } if (buffer != null) { total += (RipOff(buffer, hundreds, out buffer)) * 100; } if (buffer != null) { total += (RipOff(buffer, thousands, out buffer)) * 1000; } // after parsing for thousands, if there is any character left, this is not a valid roman number if (buffer != null) { throw new ArgumentException(String.Format("{0} is not a valid roman number", buffer)); } return total; } ///  /// Given a string, takes the four characters on the right, /// search an element in the numbers array and returns the remaing characters. ///  /// source string to parse /// array of roman numerals /// remaining characters on the left /// If it finds a roman numeral returns its integer value; otherwise returns zero public static int RipOff(string source, string[] numbers, out string left) { int result = 0; string buffer = null; // we take the last four characters : this is the length of the longest numeral in our arrays // ("VIII", "LXXX", "DCCC") // or all if source length is 4 or less if (source.Length > 4) { buffer = source.Substring(source.Length - 4); left = source.Substring(0, source.Length - 4); } else { buffer = source; left = null; } // see if buffer exists in the numbers array // if it does not, skip the first character and try again // until buffer contains only one character // append the skipped character to the left arguments while (!numbers.Contains(buffer)) { if (buffer.Length == 1) { left = source; // failed break; } else { left += buffer.Substring(0, 1); buffer = buffer.Substring(1); } } if (buffer.Length > 0) { if (numbers.Contains(buffer)) { result = Array.IndexOf(numbers, buffer); } } return result; } } } 

编辑
忘掉它 !
在这里查看BrunoLM解决方案。
它简单而优雅。
唯一需要注意的是它没有检查来源。

这是我的解决方案:

  ///  /// Converts a Roman number string into a Arabic number ///  /// the Roman number string /// the Arabic number (0 if the given string is not convertible to a Roman number) public static int ToArabicNumber(string romanNumber) { string[] replaceRom = { "CM", "CD", "XC", "XL", "IX", "IV" }; string[] replaceNum = { "DCCCC", "CCCC", "LXXXX", "XXXX", "VIIII", "IIII" }; string[] roman = { "M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I" }; int[] arabic = { 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 }; return Enumerable.Range(0, replaceRom.Length) .Aggregate ( romanNumber, (agg, cur) => agg.Replace(replaceRom[cur], replaceNum[cur]), agg => agg.ToArray() ) .Aggregate ( 0, (agg, cur) => { int idx = Array.IndexOf(roman, cur.ToString()); return idx < 0 ? 0 : agg + arabic[idx]; }, agg => agg ); } ///  /// Converts a Arabic number into a Roman number string ///  /// the Arabic number /// the Roman number string public static string ToRomanNumber(int arabicNumber) { string[] roman = { "M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I" }; int[] arabic = { 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 }; return Enumerable.Range(0, arabic.Length) .Aggregate ( Tuple.Create(arabicNumber, string.Empty), (agg, cur) => { int remainder = agg.Item1 % arabic[cur]; string concat = agg.Item2 + string.Concat(Enumerable.Range(0, agg.Item1 / arabic[cur]).Select(num => roman[cur])); return Tuple.Create(remainder, concat); }, agg => agg.Item2 ); } 

这是方法如何工作的解释:

ToArabicNumber

第一个聚合步骤是替换罗马数特殊情况(例如:IV – > IIII)。 第二个聚合步骤简单地总结了罗马字母的等效阿拉伯数字(例如V – > 5)

ToRomanNumber:

我用给定的阿拉伯数字开始聚合。 对于每一步,该数字将除以罗马字母的等效数字。 然后,该划分的剩余部分是下一步的输入。 除法结果将转换为等效罗马数字符,该字符将附加到结果字符串。

 public static int ConvertRomanNumtoInt(string strRomanValue) { Dictionary RomanNumbers = new Dictionary { {"M", 1000}, {"CM", 900}, {"D", 500}, {"CD", 400}, {"C", 100}, {"XC", 90}, {"L", 50}, {"XL", 40}, {"X", 10}, {"IX", 9}, {"V", 5}, {"IV", 4}, {"I", 1} }; int retVal = 0; foreach (KeyValuePair pair in RomanNumbers) { while (strRomanValue.IndexOf(pair.Key.ToString()) == 0) { retVal += int.Parse(pair.Value.ToString()); strRomanValue = strRomanValue.Substring(pair.Key.ToString().Length); } } return retVal; }