背景
TkDocs tutorial 里介绍了 ,其中有 A First (Real) Example 一文,这篇文章里有一个使用 生成图形化界面的简单例子。我想在那篇文章的基础上实战一下,于是想到可以写一个正整数和罗马数字互相转化的简易工具。用 Tkinter 实现一个简单的罗马数字转化工具 一文中已经提供了可以将正整数转化为罗马数字的代码,本文会探讨如何进行将罗马数字转化为正整数。
正文
如何将罗马数字转化为正整数
转化规则可以参考 13. 罗马数字转整数 这道题。以 为例,如果需要手动转化的话,可以这样分析:将 视为以下三部分
- :表示
- :表示
- :表示
因此 对应的整数是 。用代码来处理时,不必这么做,可以简单些 ⬇️
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
检查输入是否合法
上一小节提供的 to_int 方法可以将合法的罗马数字转化为对应的正整数。但是我们也要考虑到用户的输入可能非法的情况,例如 按照 to_int 方法的逻辑,会被处理成 ,但是 不是合法的罗马数字( 对应的罗马数字是 )。用 Tkinter 实现一个简单的罗马数字转化工具 一文提到,我们把整数转化为罗马数字时,可以分别处理千位/百位/十位/个位,这几位是独立的。以个位为例,如果个位不是 的话,那么合法的情况只有以下 种
| 个位上的数字 | 对应的罗马数字 |
|---|---|
那么对应的正则表达式可以写成 。但是这样写太暴力了,可以将 的情况合并,将 的情况合并。合并后变为 ,再加上个位为 的情况,正则表达式变为 ⬇️
用类似的思路进行分析,可以得出合法罗马数字的十位部分满足下方的正则表达式
用类似的思路进行分析,可以得出合法罗马数字的百位部分满足下方的正则表达式
合法罗马数字的千位部分满足下方的正则表达式
将以上结果合并在一起,正则表达式变为 ⬇️
另外还需要排除掉输入字符串为空(即,"")的情况。
完整的代码
A First (Real) Example 一文中有使用 生成图形化界面的简单例子。在它的基础上,可以写出以下 代码
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("<Return>", convert)
root.mainloop()
运行
请将上一小节展示的完整代码保存为 from_roman.py。使用如下命令可以运行 from_roman.py
python3 from_roman.py
运行效果如下
我们输入一个合法的罗马数字,例如 ,然后点击“转化为罗马数字”按钮,效果如下
再用其他罗马数字验证一下(例如 ),效果如下
运行结果符合预期