发布于2026-06-29 阅读(0)
扫一扫,手机访问
之前看到 TkDocs 教程里有个用 Tkinter 做图形界面的入门例子,感觉挺实用的。正好手头有个小需求——写一个正整数和罗马数字互相转换的工具,就想着拿这个例子练练手。网上已经有文章把“整数转罗马数字”的部分讲清楚了,那咱们今天就来聊聊反向操作:怎么把罗马数字正确解析成整数。
转换规则其实不复杂,可以参考 LeetCode 第 13 题“罗马数字转整数”的思路。拿 MMMXCIV 举个例子,手动拆解的话,可以分成三部分:MMM 表示 3000,XC 表示 90,IV 表示 4,加起来就是 3094。但代码实现时不用这么麻烦,有更简洁的方式——从左到右遍历字符,累加对应的数值,同时注意一个细节:如果当前字符的值比前一个字符的值大,说明遇到了“减法”组合,需要把之前多加的那部分减掉两倍。具体实现如下:
def to_int(roman):
mapping = {
"I": 1,
"V": 5,
"X": 10,
"L": 50,
"C": 100,
"D": 500,
"M": 1000
}
result = 0
prev_value = 0
for char in roman:
curr_value = mapping[char]
result += curr_value
if prev_value != 0 and prev_value < curr_value:
result -= prev_value * 2
prev_value = curr_value
return result
光有转换函数还不够,用户输入可能是乱写的。比如 IIMMM 按上面的逻辑会算出 3000,但它根本不是合法的罗马数字(3000 的正确写法是 MMM)。所以必须加一层输入校验。
之前那篇整数转罗马数字的文章里提到,我们可以分别处理千位、百位、十位、个位,每一位的合法组合是有限的。以个位为例,如果个位数字不是 0,那么合法的罗马数字就只有这几种:
| 个位上的数字 | 对应的罗马数字 |
|---|---|
| 1 | I |
| 2 | II |
| 3 | III |
| 4 | IV |
| 5 | V |
| 6 | VI |
| 7 | VII |
| 8 | VIII |
| 9 | IX |
用正则表达式暴力写当然可以,但更优雅的方式是合并同类项:1、2、3 可以合并成 I{1,3},5、6、7、8 合并成 VI{0,3},再加上 IV 和 IX,最后补上 0 的情况(即空字符串)。这样个位的正则就是:
I{0,3}|IV|VI{0,3}|IX
同样的思路,十位、百位、千位的合法模式也能推导出来:
X{0,3}|XL|LX{0,3}|XCC{0,3}|CD|DC{0,3}|CMM{0,3}把四部分拼起来,完整的正则表达式就是:
M{0,3}(C{0,3}|CD|DC{0,3}|CM)(X{0,3}|XL|LX{0,3}|XC)(I{0,3}|IV|VI{0,3}|IX)
注意,这个正则也会匹配空字符串,所以判断时还得额外排除掉 ""。
参考 TkDocs 里“A First (Real) Example”的界面结构,可以直接写出下面的 Python 3 代码:
from tkinter import *
from tkinter import ttk
import re
def convert():
try:
raw = roman.get().strip()
if raw == "":
result.set("无效输入")
return
if re.match("^(M{0,3})(C{0,3}|CD|DC{0,3}|CM)(X{0,3}|XL|LX{0,3}|XC)(I{0,3}|IV|VI{0,3}|IX)$", raw):
result.set(to_int(raw))
else:
result.set("无效输入")
return
except ValueError:
pass
def to_int(roman):
mapping = {
"I": 1,
"V": 5,
"X": 10,
"L": 50,
"C": 100,
"D": 500,
"M": 1000
}
result = 0
prev_value = 0
for char in roman:
curr_value = mapping[char]
result += curr_value
if prev_value != 0 and prev_value < curr_value:
result -= prev_value * 2
prev_value = curr_value
return result
root = Tk()
root.title("罗马数字转整数小工具")
mainframe = ttk.Frame(root, padding=(3, 3, 12, 12))
mainframe.grid(column=0, row=0, sticky=(N, W, E, S))
roman = StringVar()
roman_entry = ttk.Entry(mainframe, width=12, textvariable=roman)
roman_entry.grid(column=2, row=1, sticky=(W, E))
ttk.Button(mainframe, text="转化为整数", command=convert).grid(column=1, row=2, sticky=W)
ttk.Label(mainframe, text="请输入一个罗马数字").grid(column=1, row=1, sticky=W)
result = StringVar()
ttk.Label(mainframe, textvariable=result).grid(column=2, row=2, sticky=W)
root.columnconfigure(0, weight=1)
root.rowconfigure(0, weight=1)
mainframe.columnconfigure(2, weight=1)
for child in mainframe.winfo_children():
child.grid_configure(padx=5, pady=5)
roman_entry.focus()
root.bind("", convert)
root.mainloop()
把上面的代码保存为 from_roman.py,然后在终端里执行:
python3 from_roman.py
运行效果如下:

输入一个合法的罗马数字,比如 MCMXCVII,点击“转化为整数”按钮,就能看到结果:

再用 MMMXCIV 验证一下,结果也是对的:

运行结果符合预期。这个工具虽然简单,但把罗马数字解析的常见坑(非法输入、减法规则)都覆盖了,可以直接复用或扩展。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8