您在这里:首页 > 深入 Python > 测试优先编程 > roman.py,阶段 5 | << >> | ||||
深入 Python从 Python 新手到专家 |
既然 fromRoman 能够正确处理有效的输入,现在是时候解决最后一个难题了:让它能够正确处理无效的输入。这意味着要找到一种方法来检查一个字符串并确定它是否是一个有效的罗马数字。这本质上比在 toRoman 中验证数字输入更难,但您有一个强大的工具可以使用:正则表达式。
如果您不熟悉正则表达式,并且没有阅读第 7 章,正则表达式,现在是阅读的好时机。
正如您在第 7.3 节,“案例研究:罗马数字”中所见,使用字母 M、D、C、L、X、V 和 I 构建罗马数字有几个简单的规则。让我们回顾一下这些规则:
此文件位于示例目录的 py/roman/stage5/ 中。
如果您还没有这样做,您可以下载本书中使用的此示例和其他示例。
"""Convert to and from Roman numerals""" import re #Define exceptions class RomanError(Exception): pass class OutOfRangeError(RomanError): pass class NotIntegerError(RomanError): pass class InvalidRomanNumeralError(RomanError): pass #Define digit mapping romanNumeralMap = (('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)) def toRoman(n): """convert integer to Roman numeral""" if not (0 < n < 4000): raise OutOfRangeError, "number out of range (must be 1..3999)" if int(n) <> n: raise NotIntegerError, "non-integers can not be converted" result = "" for numeral, integer in romanNumeralMap: while n >= integer: result += numeral n -= integer return result #Define pattern to detect valid Roman numerals romanNumeralPattern = '^M?M?M?(CM|CD|D?C?C?C?)(XC|XL|L?X?X?X?)(IX|IV|V?I?I?I?)$'def fromRoman(s): """convert Roman numeral to integer""" if not re.search(romanNumeralPattern, s):
raise InvalidRomanNumeralError, 'Invalid Roman numeral: %s' % s result = 0 index = 0 for numeral, integer in romanNumeralMap: while s[index:index+len(numeral)] == numeral: result += integer index += len(numeral) return result
![]() |
这只是您在第 7.3 节,“案例研究:罗马数字”中讨论的模式的延续。十位是 XC (90)、XL (40),或者是一个可选的 L 后跟 0 到 3 个可选的 X 字符。个位是 IX (9)、IV (4),或者是一个可选的 V 后跟 0 到 3 个可选的 I 字符。 |
![]() |
将所有这些逻辑编码到正则表达式中后,检查无效罗马数字的代码就变得微不足道了。如果 re.search 返回一个对象,则表示正则表达式匹配,输入有效;否则,输入无效。 |
在这一点上,您可能会怀疑这个又大又丑的正则表达式是否真的能捕捉到所有类型的无效罗马数字。但不要相信我的话,看看结果吧:
fromRoman should only accept uppercase input ... oktoRoman should always return uppercase ... ok fromRoman should fail with malformed antecedents ... ok
fromRoman should fail with repeated pairs of numerals ... ok
fromRoman should fail with too many repeated numerals ... ok fromRoman should give known result with known input ... ok toRoman should give known result with known input ... ok fromRoman(toRoman(n))==n for all n ... ok toRoman should fail with non-integer input ... ok toRoman should fail with negative input ... ok toRoman should fail with large input ... ok toRoman should fail with 0 input ... ok ---------------------------------------------------------------------- Ran 12 tests in 2.864s OK
![]() |
|
当所有测试都通过时,停止编码。 |
<< roman.py,阶段 4 |
| 1 | 2 | 3 | 4 | 5 | |
重构 >> |