在自动生成文档大纲、制度文件或Markdown目录时,带圈数字(①⑪⑳)常用于最低层级小标题。Python标准库没有直接提供该转换方法,网上多数代码仅支持1到10,且缺少参数校验和统一封装。本文将基于Unicode偏移公式,实现一套覆盖1到20的圆圈数字转换工具,包括基础函数、安全容错版本、工具类封装以及业务整合方案。
## 原理:Unicode编码偏移
带圈数字在Unicode中是独立字符,编码区间为:
- ① U+2460 ← 对应数字1
- ⑩ U+2469 ← 对应数字10
- ⑪ U+246A ← 对应数字11
- ⑳ U+2473 ← 对应数字20
规律非常简洁:数字n对应的代码点 = 0x2460 + n - 1。例如n=11时,0x2460+10=0x246A,即字符⑪。利用该偏移公式,无需手动维护字典,一行chr()即可获取圆圈字符。
## 基础转换函数
以下函数接受整数1~20,返回对应圆圈字符,并包含类型和范围校验。
- def num_to_circle_char(n: int) -> str:
- """
- 阿拉伯数字 1~20 转换为圆圈数字 ①②③…⑳
- :param n: 输入整数,范围 1 ~ 20
- :return: 圆圈单个字符
- """
- if not isinstance(n, int):
- raise TypeError("输入必须为整数类型")
- if not 1 <= n <= 20:
- raise ValueError("当前仅支持 1 ~ 20 的数字转换圆圈序号")
- code_point = 0x2460 + n - 1
- return chr(code_point)
- if __name__ == "__main__":
- test_list = [1, 5, 10, 13, 18, 20]
- for num in test_list:
- res = num_to_circle_char(num)
- print(f"{num} → {res}")
复制代码
运行输出:- 1 → ①
- 5 → ⑤
- 10 → ⑩
- 13 → ⑬
- 18 → ⑱
- 20 → ⑳
复制代码
## 异常容错安全版(生产推荐)
业务代码不希望因非法输入直接崩溃,可改用安全版:超出范围或类型错误时返回默认值,不抛异常。
- def num_to_circle_char_safe(n: int, default: str = "") -> str:
- """
- 安全版,越界返回默认值
- :param n: 目标数字
- :param default: 超出范围/类型错误时返回内容
- :return: 圆圈字符或默认值
- """
- try:
- if not isinstance(n, int):
- return default
- if 1 <= n <= 20:
- code_point = 0x2460 + n - 1
- return chr(code_point)
- return default
- except Exception:
- return default
- # 测试
- print(num_to_circle_char_safe(21)) # 空
- print(num_to_circle_char_safe(0, "无")) # 无
- print(num_to_circle_char_safe("abc")) # 空
- print(num_to_circle_char_safe(12)) # ⑫
复制代码
## 统一工具类封装
将圆圈数字转换与已有的“中文数字”、“第X篇”、“第X部分”整合到一个工具类中,便于项目管理。
- class ChapterNumUtil:
- digits = ["", "一", "二", "三", "四", "五", "六", "七", "八", "九"]
- @classmethod
- def num_to_plain_chinese(cls, n: int) -> str:
- """纯中文数字:1→一、15→十五、100→一百"""
- if not isinstance(n, int) or n < 1 or n > 100:
- raise ValueError("仅支持1~100整数")
- if n == 100:
- return "一百"
- ten = n // 10
- unit = n % 10
- if ten == 0:
- return cls.digits[unit]
- elif ten == 1:
- return "十" if unit == 0 else f"十{cls.digits[unit]}"
- else:
- return f"{cls.digits[ten]}十" if unit == 0 else f"{cls.digits[ten]}十{cls.digits[unit]}"
- @classmethod
- def num_to_chinese_article_title(cls, n: int) -> str:
- return f"第{cls.num_to_plain_chinese(n)}篇"
- @classmethod
- def num_to_chinese_part_title(cls, n: int) -> str:
- return f"第{cls.num_to_plain_chinese(n)}部分"
- @classmethod
- def num_to_circle_char(cls, n: int) -> str:
- if not isinstance(n, int) or not 1 <= n <= 20:
- raise ValueError("圆圈序号仅支持1~20")
- return chr(0x2460 + n - 1)
- @classmethod
- def num_to_circle_char_safe(cls, n: int, default="") -> str:
- try:
- return cls.num_to_circle_char(n)
- except (TypeError, ValueError):
- return default
- # 统一调用示例
- util = ChapterNumUtil
- print(util.num_to_chinese_article_title(3)) # 第三篇
- print(util.num_to_chinese_part_title(12)) # 第十二部分
- print(util.num_to_circle_char(7)) # ⑦
- print(util.num_to_circle_char(19)) # ⑲
- print(util.num_to_circle_char_safe(99, "超出范围")) # 超出范围
复制代码
## 实战场景:大纲层级搭配
制度文档中常需多级编号,可结合工具类实现:
- 层级0:num_to_chinese_article_title(2) → 第二篇
- 层级1:num_to_chinese_part_title(3) → 第三部分
- 层级2:普通阿拉伯数字 1、2、3
- 层级3:小数 1.1、1.2
- 层级6:num_to_circle_char(2) → ②
示例代码:- article = ChapterNumUtil.num_to_chinese_article_title(2)
- circle = ChapterNumUtil.num_to_circle_char(2)
- print(f"{article}\n 4.1 管理要求\n {circle} 业务办理流程")
复制代码
输出:
## 常见踩坑
1. **超过20无标准字符**:Unicode只定义了①到⑳(U+2460~U+2473),21及以上没有标准圆圈字符。业务设计时建议控制层级不超过20,若必须使用21以上,改用(21)括号文本替代。
2. **不要用字符串拼接(1)代替**:(1)是三个普通符号,与原生圆圈字符在字体排版、对齐、导出Word/Markdown时视觉差异明显。规范文档必须使用Unicode圆圈字符。
3. **浮点数类型问题**:10.0虽数值等于10,但类型为float,会触发类型校验。调用前需强制转整数 int(num)。
4. **编码兼容性**:主流编辑器、Word、WPS、浏览器均完美支持U+2460~U+2473,无乱码风险。
## 拓展:批量生成全部圆圈序号
快速打印1~20所有圆圈字符用于调试:- circle_list = [num_to_circle_char(i) for i in range(1, 21)]
- print(circle_list)
- # ['①', '②', '③', '④', '⑤', '⑥', '⑦', '⑧', '⑨', '⑩', '⑪', '⑫', '⑬', '⑭', '⑮', '⑯', '⑰', '⑱', '⑲', '⑳']
复制代码
## 总结
圆圈数字本质是连续Unicode字符,通过固定偏移公式0x2460+n-1快速计算,无需字典映射。基础函数严格限制1~20,安全容错版本适配生产环境。与中文数字、第X篇等封装在同一工具类中,命名统一,多层级大纲生成时便于调用。原生Unicode圆圈字符排版规范,适合自动化生成制度手册或Markdown文档。注意序号上限20,超出需改用括号格式作为备选方案。 |