如何在PHP中将罗马数字转换为整数?

时间:2011-06-07 13:05:02

标签: php integer roman-numerals

使用PHP,我想将包含罗马数字的字符串转换为其整数表示。我需要这个,因为我需要对它们进行计算。

Wikipedia on Roman numerals

仅识别基本的罗马数字字符就足够了,如:

$roman_values=array(
    'I' => 1,
    'V' => 5,
    'X' => 10,
    'L' => 50,
    'C' => 100,
    'D' => 500,
    'M' => 1000,
);

这意味着最高可能的数字是3999(MMMCMXCIX)。我将使用N来表示零,除了仅支持正整数。

我不能将PEAR库用于罗马数字。

我在SO上发现了一个关于如何测试字符串是否包含有效罗马数字的重要问题:

How do you match only valid roman numerals with a regular expression?

最好的编码方式是什么?

12 个答案:

答案 0 :(得分:35)

这个怎么样:

$romans = array(
    '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,
);

$roman = 'MMMCMXCIX';
$result = 0;

foreach ($romans as $key => $value) {
    while (strpos($roman, $key) === 0) {
        $result += $value;
        $roman = substr($roman, strlen($key));
    }
}
echo $result;

应为提供的$roman输出3999。它似乎适用于我的有限测试:

MCMXC = 1990
MM = 2000
MMXI = 2011
MCMLXXV = 1975

您可能还想首先进行一些验证: - )

答案 1 :(得分:9)

我不确定你是否有ZF,但如果你(或任何正在阅读本文的人)在这里做的是我的片段:

$number = new Zend_Measure_Number('MCMLXXV', Zend_Measure_Number::ROMAN);
$number->convertTo (Zend_Measure_Number::DECIMAL);
echo $number->getValue();

答案 2 :(得分:9)

这是我提出的那个,我也添加了有效性检查。

class RomanNumber {
    //array of roman values
    public static $roman_values=array(
        'I' => 1, 'V' => 5, 
        'X' => 10, 'L' => 50,
        'C' => 100, 'D' => 500,
        'M' => 1000,
    );
    //values that should evaluate as 0
    public static $roman_zero=array('N', 'nulla');
    //Regex - checking for valid Roman numerals
    public static $roman_regex='/^M{0,3}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$/';

    //Roman numeral validation function - is the string a valid Roman Number?
    static function IsRomanNumber($roman) {
         return preg_match(self::$roman_regex, $roman) > 0;
    }

    //Conversion: Roman Numeral to Integer
    static function Roman2Int ($roman) {
        //checking for zero values
        if (in_array($roman, self::$roman_zero)) {
            return 0;
        }
        //validating string
        if (!self::IsRomanNumber($roman)) {
            return false;
        }

        $values=self::$roman_values;
        $result = 0;
        //iterating through characters LTR
        for ($i = 0, $length = strlen($roman); $i < $length; $i++) {
            //getting value of current char
            $value = $values[$roman[$i]];
            //getting value of next char - null if there is no next char
            $nextvalue = !isset($roman[$i + 1]) ? null : $values[$roman[$i + 1]];
            //adding/subtracting value from result based on $nextvalue
            $result += (!is_null($nextvalue) && $nextvalue > $value) ? -$value : $value;
        }
        return $result;
    }
}

答案 3 :(得分:4)

快速创意 - 如果值$current(左侧更多)小于$previous,则从右到左浏览罗马数字,然后从结果中减去它,如果更大,则加上它。

$romanValues=array(
    'I' => 1,
    'V' => 5,
    'X' => 10,
    'L' => 50,
    'C' => 100,
    'D' => 500,
    'M' => 1000,
);
$roman = 'MMMCMXCIX';

// RTL
$arabic = 0;
$prev = null;
for ( $n = strlen($roman) - 1; $n >= 0; --$n ) {
    $curr = $roman[$n];
    if ( is_null($prev) ) {
        $arabic += $romanValues[$roman[$n]];
    } else {
        $arabic += $romanValues[$prev] > $romanValues[$curr] ? -$romanValues[$curr] : +$romanValues[$curr];
    }
    $prev = $curr;
}
echo $arabic, "\n";

// LTR
$arabic = 0;
$romanLength = strlen($roman);
for ( $n = 0; $n < $romanLength; ++$n ) {
    if ( $n === $romanLength - 1 ) {
        $arabic += $romanValues[$roman[$n]];
    } else {
        $arabic += $romanValues[$roman[$n]] < $romanValues[$roman[$n+1]] ? -$romanValues[$roman[$n]] : +$romanValues[$roman[$n]];
    }
}
echo $arabic, "\n";

还应添加罗马数字的一些验证,但你说你已经找到了如何做到这一点。

答案 4 :(得分:3)

版权归这个博客所用(顺便说一下!) http://scriptsense.blogspot.com/2010/03/php-function-number-to-roman-and-roman.html

<?php

function roman2number($roman){
    $conv = array(
        array("letter" => 'I', "number" => 1),
        array("letter" => 'V', "number" => 5),
        array("letter" => 'X', "number" => 10),
        array("letter" => 'L', "number" => 50),
        array("letter" => 'C', "number" => 100),
        array("letter" => 'D', "number" => 500),
        array("letter" => 'M', "number" => 1000),
        array("letter" => 0, "number" => 0)
    );
    $arabic = 0;
    $state = 0;
    $sidx = 0;
    $len = strlen($roman);

    while ($len >= 0) {
        $i = 0;
        $sidx = $len;

        while ($conv[$i]['number'] > 0) {
            if (strtoupper(@$roman[$sidx]) == $conv[$i]['letter']) {
                if ($state > $conv[$i]['number']) {
                    $arabic -= $conv[$i]['number'];
                } else {
                    $arabic += $conv[$i]['number'];
                    $state = $conv[$i]['number'];
                }
            }
            $i++;
        }

        $len--;
    }

    return($arabic);
}


function number2roman($num,$isUpper=true) {
    $n = intval($num);
    $res = '';

    /*** roman_numerals array ***/
    $roman_numerals = array(
        '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
    );

    foreach ($roman_numerals as $roman => $number)
    {
        /*** divide to get matches ***/
        $matches = intval($n / $number);

        /*** assign the roman char * $matches ***/
        $res .= str_repeat($roman, $matches);

        /*** substract from the number ***/
        $n = $n % $number;
    }

    /*** return the res ***/
    if($isUpper) return $res;
    else return strtolower($res);
}

/* TEST */
echo $s=number2roman(1965,true);
echo "\n and bacK:\n";
echo roman2number($s);


?>

答案 5 :(得分:1)

我迟到了,但这是我的。假设字符串中的有效数字,但不测试有效的罗马数字,无论是什么......似乎没有达成共识。此功能适用于 VC (95)或 MIM (1999)或 MMMMMM (6000)等罗马数字。

function roman2dec( $roman ) {
    $numbers = array(
        'I' => 1,
        'V' => 5,
        'X' => 10,
        'L' => 50,
        'C' => 100,
        'D' => 500,
        'M' => 1000,
    );

    $roman = strtoupper( $roman );
    $length = strlen( $roman );
    $counter = 0;
    $dec = 0;
    while ( $counter < $length ) {
        if ( ( $counter + 1 < $length ) && ( $numbers[$roman[$counter]] < $numbers[$roman[$counter + 1]] ) ) {
            $dec += $numbers[$roman[$counter + 1]] - $numbers[$roman[$counter]];
            $counter += 2;
        } else {
            $dec += $numbers[$roman[$counter]];
            $counter++;
        }
    }
    return $dec;
}

答案 6 :(得分:1)

偶然发现了这个美女,不得不将其全部发布:

function roman($N)
{
    $c = 'IVXLCDM';
    for ($a = 5, $b = $s = ''; $N; $b++, $a ^= 7)
    {
        for (
            $o = $N % $a, $N = $N / $a ^ 0;

            $o--;

            $s = $c[$o > 2 ? $b + $N - ($N &= -2) + $o = 1 : $b] . $s
        );
    }
    return $s;
}

答案 7 :(得分:0)

定义您自己的架构! (可选)

function rom2arab($rom,$letters=array()){
    if(empty($letters)){
        $letters=array('M'=>1000,
                       'D'=>500,
                       'C'=>100,
                       'L'=>50,
                       'X'=>10,
                       'V'=>5,
                       'I'=>1);
    }else{
        arsort($letters);
    }
    $arab=0;
    foreach($letters as $L=>$V){
        while(strpos($rom,$L)!==false){
            $l=$rom[0];
            $rom=substr($rom,1);
            $m=$l==$L?1:-1;
            $arab += $letters[$l]*$m;
        }
    }
    return $arab;
}

受到andyb的回答启发

答案 8 :(得分:0)

我刚刚写了大约10分钟,这并不完美,但似乎适用于我给它的少数测试用例。我没有强制允许从什么值中减去哪些值,这只是一个基本循环,它将当前字母值与序列中的下一个值(如果存在)进行比较,然后添加值或添加减去的数量到总数:

$roman = strtolower($_GET['roman']);

$values = array(
'i' => 1,
'v' => 5,
'x' => 10,
'l' => 50,
'c' => 100,
'd' => 500,
'm' => 1000,
);
$total = 0;
for($i=0; $i<strlen($roman); $i++)
{
    $v = $values[substr($roman, $i, 1)];
    $v2 = ($i < strlen($roman))?$values[substr($roman, $i+1, 1)]:0;

    if($v2 && $v < $v2)
    {
        $total += ($v2 - $v);
        $i++;
    }
    else
        $total += $v;

}

echo $total;

答案 9 :(得分:0)

function Romannumeraltonumber($input_roman){
  $di=array('I'=>1,
            'V'=>5,
            'X'=>10,
            'L'=>50,
            'C'=>100,
            'D'=>500,
            'M'=>1000);
  $result=0;
  if($input_roman=='') return $result;
  //LTR
  for($i=0;$i<strlen($input_roman);$i++){ 
    $result=(($i+1)<strlen($input_roman) and 
          $di[$input_roman[$i]]<$di[$input_roman[$i+1]])?($result-$di[$input_roman[$i]]) 
                                                        :($result+$di[$input_roman[$i]]);
   }
 return $result;
}

答案 10 :(得分:0)

尝试这个简单的功能

function arabicToRomanNumber($arabicNumber='') {

            $arabicNumValue = intval($arabicNumber);
            $convertedNumber = '';
            $romans = array(
                '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);
            foreach ($romans as $roman => $romanValue) 
            {
                  $matchingNumber = intval($arabicNumValue / $romanValue); // Determine the number of matches
                  $convertedNumber .= str_repeat($roman, $matchingNumber); // Store that many characters
                  $arabicNumValue = $arabicNumValue % $romanValue; // Substract that from the number
            }

            return $convertedNumber;

      }

最后一句只需将阿拉伯数字传递为

echo arabicToRomanNumber('25'); //结果将是XXV

echo arabicToRomanNumber('250'); //结果将是CCL

答案 11 :(得分:0)

function parseRomanNumerals($input)
{
$roman_val = '';
$roman_length = strlen($input);
$result_roman = 0;
for ($x = 0; $x <= $roman_length; $x++) {
$roman_val_prev = $roman_val;
$roman_numeral = substr($input, $roman_length-$x,1);

switch ($roman_numeral) {
case "M":
$roman_val = 1000;
break;
case "D":
$roman_val = 500;
break;
case "C":
$roman_val = 100;
break;
case "L":
$roman_val = 50;
break;
case "X":
$roman_val = 10;
break;
case "V":
$roman_val = 5;
break;
case "I":
$roman_val = 1;
break;
default:
$roman_val = 0;
}
if ($roman_val_prev<$roman_val) {
$result_roman = $result_roman - $roman_val;
}
else {
$result_roman = $result_roman + $roman_val;
}
}
return abs($result_roman);
}