Hướng dẫn roman to integer php

Using PHP, I'd like to convert a string containing a Roman number into its integer representation. I need this because I need to make calculations on them.

Wikipedia on Roman numerals

It would suffice to only recognize the basic Roman numeral characters, like:

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

That means the highest possible number is 3999 [MMMCMXCIX]. I will use N to represent zero, other than that only positive integers are supported.

I cannot use the PEAR library for Roman numbers.

I found this great question on SO on how to test whether the string contains a valid Roman numeral:

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

What would be the best way of coding this?

asked Jun 7, 2011 at 13:05

3

How about this:

$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;

which should output 3999 for the supplied $roman. It seems to work for my limited testing:

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

You might want to do some validation first as well :-]

answered Jun 7, 2011 at 13:45

andybandyb

42.9k12 gold badges121 silver badges149 bronze badges

7

I am not sure whether you've got ZF or not, but in case you [or any of you who's reading this] do here is my snippet:

$number = new Zend_Measure_Number['MCMLXXV', Zend_Measure_Number::ROMAN];
$number->convertTo [Zend_Measure_Number::DECIMAL];
echo $number->getValue[];
  • Zend_Measure_Number on framework.zend.com

kapa

76.2k20 gold badges156 silver badges174 bronze badges

answered Jun 7, 2011 at 13:34

akondakond

15.6k4 gold badges34 silver badges55 bronze badges

1

This is the one I came up with, I added the validity check as well.

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;
    }
}

answered Jun 9, 2011 at 10:01

kapakapa

76.2k20 gold badges156 silver badges174 bronze badges

Quick idea - go through the Roman number from right to left, if value of $current [more to the left] is smaller than $previous, then subtract it from the result, if larger, then add it.

$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";

Some validation of roman number should also be added, though you said that you already have found how to do it.

answered Jun 7, 2011 at 13:22

binaryLVbinaryLV

8,8422 gold badges39 silver badges42 bronze badges

5

Copyrights is for this blog [btw!] //scriptsense.blogspot.com/2010/03/php-function-number-to-roman-and-roman.html


kapa

76.2k20 gold badges156 silver badges174 bronze badges

answered Jun 7, 2011 at 13:10

publikz.compublikz.com

9231 gold badge12 silver badges21 bronze badges

1

I'm late to the party, but here's mine. Assumes valid Numerals in the string, but doesn't test for a valid Roman number, whatever that is...there doesn't seem to be a consensus. This function will work for Roman numbers like VC [95], or MIM [1999], or 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;
}

answered Feb 3, 2013 at 7:35

akTedakTed

2042 silver badges8 bronze badges

Define your own schema! [optional]

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;
}

Inspired by andyb's answer

answered Jun 7, 2011 at 23:33

ShadShad

14.6k2 gold badges21 silver badges34 bronze badges

1

I just wrote this in about 10 mins, it's not perfect, but seems to work for the few test cases I've given it. I'm not enforcing what values are allowed to be subtracted from what, this is just a basic loop that compares the current letter value with the next one in the sequence [if it exists] and then either adds the value or adds the subtracted amount to the total:

$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 2 ? $b + $N - [$N &= -2] + $o = 1 : $b] . $s
        ];
    }
    return $s;
}

answered Feb 19, 2013 at 10:46

3

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 100, 
    'L'  => 50, 
    'X'  => 10, 
    'V'  => 5, 
    'I'  => 1];

$a = str_split[$number];

$i = 0;
$temp = 0;
$value = 0;
$q = count[$a];
while[$i < $q] {

    $thys = $symbols[$a[$i]];
    if[isset[$a[$i +1]]] {
        $next = $symbols[$a[$i +1]];
    } else {
        $next = 0;
    }

    if[$thys < $next] {
        $value -= $thys;
    } else {
        $value += $thys;
    }

    $temp = $thys;
    $i++;
}

return $value;

}

answered Aug 23, 2015 at 20:02

function parseRomanNumerals[$input]
{
$roman_val = '';
$roman_length = strlen[$input];
$result_roman = 0;
for [$x = 0; $x 1,
        'V'=>5,
        'X'=>10,
        'L'=>50,
        'C'=>100,
        'D'=>500,
        'M'=>1000,
    ];
}

function ConvertRomanNumeralToArabic[$input_roman]{
    $input_length = strlen[$input_roman];
    if[$input_length === 0] {
        return $result;
    }
    
    $roman_numerals = RomanNumeralValues[];
    
    $current_pointer = 1;
    $result = 0;
    
    for[$i = $input_length - 1; $i > -1; $i--]{ 
        $letter = $input_roman[$i];
        $letter_value = $roman_numerals[$letter];
        
        if[$letter_value === $current_pointer] {
            $result += $letter_value;
        } elseif [$letter_value < $current_pointer] {
            $result -= $letter_value;
        } else {
            $result += $letter_value;
            $current_pointer = $letter_value;
        }
    }
    
    return $result;
}

print ConvertRomanNumeralToArabic["LIX"];

answered Jan 16 at 21:54

HoldOffHungerHoldOffHunger

16.7k8 gold badges92 silver badges121 bronze badges

Chủ Đề