base-conversion 1.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 xiaomaozjj666
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,156 @@
1
+ Metadata-Version: 2.4
2
+ Name: base-conversion
3
+ Version: 1.0.1
4
+ Summary: Exact arbitrary-base (2-62) number conversion: lossless big ints, Fraction-precise decimals, repeating-digit notation
5
+ Author: xiaomaozjj666
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/xiaomaozjj666/base-conversion
8
+ Project-URL: Issues, https://github.com/xiaomaozjj666/base-conversion/issues
9
+ Project-URL: Changelog, https://github.com/xiaomaozjj666/base-conversion/blob/main/CHANGELOG.md
10
+ Keywords: base,radix,conversion,numbers,fractions,cli
11
+ Classifier: Development Status :: 6 - Mature
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Classifier: Topic :: Utilities
23
+ Requires-Python: >=3.9
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Provides-Extra: dev
27
+ Requires-Dist: pytest>=7; extra == "dev"
28
+ Requires-Dist: ruff>=0.5; extra == "dev"
29
+ Requires-Dist: mypy>=1.8; extra == "dev"
30
+ Requires-Dist: build>=1.0; extra == "dev"
31
+ Requires-Dist: setuptools>=69; extra == "dev"
32
+ Dynamic: license-file
33
+
34
+ # base-conversion
35
+
36
+ > **⚠️ 本仓库已归档**:进制转换已迁入 [`programming-exercises`](https://github.com/xiaomaozjj666/programming-exercises)(`python|js|cpp/base_converter.*` 三语实现),算法基准并入其 `benchmark/`。本仓库仅保留库源码与基准的历史快照,不再演进——测试与 CI 持续可跑,但不再接受功能变更。
37
+
38
+ <p align="center">
39
+ <a href="https://github.com/xiaomaozjj666/base-conversion/actions/workflows/ci.yml"><img src="https://github.com/xiaomaozjj666/base-conversion/actions/workflows/ci.yml/badge.svg" alt="CI" /></a>
40
+ <img src="https://img.shields.io/badge/python-3.9%2B-blue" alt="Python 3.9+" />
41
+ <img src="https://img.shields.io/badge/ruff-linted-261230" alt="Ruff" />
42
+ <img src="https://img.shields.io/badge/tests-76%20passed-brightgreen" alt="Tests" />
43
+ <img src="https://img.shields.io/badge/license-MIT-blue" alt="MIT License" />
44
+ </p>
45
+
46
+ **任意进制(2-62)精确转换库** — 大整数无精度损失、小数用分数精确算术、循环小数用括号循环节表示并可无损往返。
47
+
48
+ ## 为什么用它
49
+
50
+ | 能力 | 内置 `int(s, base)` | 本库 |
51
+ |------|---------------------|------|
52
+ | 进制范围 | 2-36 | **2-62**(`0-9A-Za-z`,37 起大小写敏感) |
53
+ | 小数 | ✗ | ✓ **Fraction 精确**(二进制 `"0.1"` ≡ 1/2) |
54
+ | 循环小数 | ✗ | ✓ `1/3` ↔ `"0.(3)"` 无损往返 |
55
+ | 大整数 | ✓ | ✓(同一量级性能,见下) |
56
+ | CLI | ✗ | ✓ `baseconv` 一条命令 |
57
+
58
+ 浮点实现的转换在 `0.1` 这类值上必然引入二进制近似误差;本库全程使用 `int` 与 `fractions.Fraction`,**任何输入的转换结果都是数学上精确的**。
59
+
60
+ ## 安装
61
+
62
+ ```bash
63
+ git clone https://github.com/xiaomaozjj666/base-conversion.git
64
+ cd base-conversion
65
+ python -m pip install .
66
+ ```
67
+
68
+ > 尚未发布到 PyPI;从源码安装(零运行时依赖,仅标准库)。
69
+
70
+ ## 快速开始
71
+
72
+ ```python
73
+ >>> from fractions import Fraction
74
+ >>> from base_conversion import to_base, to_decimal
75
+ >>> to_base(255, 16)
76
+ 'FF'
77
+ >>> to_decimal("ff", 16) # 2-36 进制大小写不敏感
78
+ 255
79
+ >>> to_decimal("0.1", 2) # 精确:不是 0.5 的浮点近似
80
+ Fraction(1, 2)
81
+ >>> to_base(Fraction(1, 3), 10) # 循环小数 → 括号循环节
82
+ '0.(3)'
83
+ >>> to_decimal("0.(3)", 10) # 且可无损解析回来
84
+ Fraction(1, 3)
85
+ >>> to_base(10**100 + 7, 62) # 大整数无损
86
+ 'QCyvrY2MJnQFGlUHTCA95Xz8AHOrLuoIO0fuPkHHCcyXy9ytM5N1lqsh'
87
+ ```
88
+
89
+ 命令行:
90
+
91
+ ```bash
92
+ baseconv 255 10 16 # → FF
93
+ baseconv -- -FF 16 10 # → -255(位置参数负数用 -- 分隔)
94
+ baseconv -n -FF -f 16 -t 10 # → -255(-n 可直接接收负数值)
95
+ baseconv -n 0.1 -f 2 -t 10 # → 0.5(二进制小数精确转十进制)
96
+ ```
97
+
98
+ ## API
99
+
100
+ | 函数 | 签名 | 说明 |
101
+ |------|------|------|
102
+ | `to_decimal` | `(s: str, base: int) → int \| Fraction` | 解析 `base` 进制字符串;支持 `±`、小数点、括号循环节 |
103
+ | `to_base` | `(value: int \| Fraction \| float, base: int) → str` | 渲染为目标进制;非整数 float 拒绝(精度陷阱) |
104
+ | `decimal_to_base` / `base_to_decimal` | 同上 | 旧 API 兼容别名 |
105
+ | `decimal_to_binary` / `binary_to_decimal` | — | 二进制快捷方式 |
106
+ | `decimal_to_hex` / `hex_to_decimal` | — | 十六进制快捷方式(输出大写) |
107
+
108
+ 常量:`BASE_MIN=2`、`BASE_MAX=62`、`DIGITS`(62 字符表)。
109
+
110
+ ### 文法(`to_decimal` 接受的输入)
111
+
112
+ ```
113
+ [sign] digits [ "." digits ] [ "(" digits ")" ]
114
+ ```
115
+
116
+ - 2-36 进制大小写不敏感;37-62 大小写敏感(`'A'`=10、`'a'`=36);
117
+ - `".5"`、`"5."` 合法,单独的 `"."` 非法;
118
+ - 循环节括号必须收尾、非空、不嵌套:`"0.(3)"`、`"0.1(6)"` 合法,`"0.()"`、`"0.(3"` 非法。
119
+
120
+ ### 错误处理
121
+
122
+ - 非法字符 / 文法错误 → `ValueError`(信息含具体字符与进制);
123
+ - 进制越界(非 2-62)→ `ValueError`;非 int 进制或 `bool` → `TypeError`;
124
+ - `to_base` 收到非整数 `float` → `ValueError`(提示改用 `Fraction`;`to_base` 不接受数字字符串)。
125
+
126
+ ## 与内置的性能关系
127
+
128
+ 核心整数路径就是「除基取余 / 乘基累加」,与内置 `int(s, base)`、`format(n, 'x')` 同一数量级——测试含 500 例随机大整数交叉验证(对照内置 `int(s, base)`)与 300 例随机 Fraction 往返性质测试,10^60 量级毫秒级完成。本库的定位差异在**能力**(62 进制、精确小数、循环节)而非速度;极限吞吐场景请直接用内置函数。
129
+
130
+ ## 历史与归档
131
+
132
+ 本仓库早期是三语言(C++ / Python / JS)进制转换 + 查找排序基准的对比实现,2026-09 归档整理后:
133
+
134
+ - Python 部分重写为本库(2-62 进制、Fraction 精确小数、CLI、76 个测试);
135
+ - 三语言基准脚本保留在 [`archive/`](archive/)(历史快照,不再维护);
136
+ - 查找/排序算法练习与多语言题解迁移至 [programming-exercises](https://github.com/xiaomaozjj666/programming-exercises)。
137
+
138
+ 历史 benchmark 实测数据(2026-08-21 本机,三语言算法对比)见 [`docs/images/benchmark.svg`](docs/images/benchmark.svg)。
139
+
140
+ ## 开发
141
+
142
+ ```bash
143
+ python -m pip install -e ".[dev]"
144
+ pytest # 76 个测试(含随机交叉验证与往返性质测试)
145
+ ruff check src tests # 代码检查
146
+ mypy src # 类型检查
147
+ python -m build # 构建分发包
148
+ ```
149
+
150
+ CI 在 Python 3.9-3.13 矩阵上运行测试,并执行 lint、类型检查与构建验证。
151
+
152
+ 维护者发布流程见 [docs/RELEASING.md](docs/RELEASING.md),版本变更记录见 [CHANGELOG.md](CHANGELOG.md)。
153
+
154
+ ## License
155
+
156
+ [MIT](LICENSE)
@@ -0,0 +1,123 @@
1
+ # base-conversion
2
+
3
+ > **⚠️ 本仓库已归档**:进制转换已迁入 [`programming-exercises`](https://github.com/xiaomaozjj666/programming-exercises)(`python|js|cpp/base_converter.*` 三语实现),算法基准并入其 `benchmark/`。本仓库仅保留库源码与基准的历史快照,不再演进——测试与 CI 持续可跑,但不再接受功能变更。
4
+
5
+ <p align="center">
6
+ <a href="https://github.com/xiaomaozjj666/base-conversion/actions/workflows/ci.yml"><img src="https://github.com/xiaomaozjj666/base-conversion/actions/workflows/ci.yml/badge.svg" alt="CI" /></a>
7
+ <img src="https://img.shields.io/badge/python-3.9%2B-blue" alt="Python 3.9+" />
8
+ <img src="https://img.shields.io/badge/ruff-linted-261230" alt="Ruff" />
9
+ <img src="https://img.shields.io/badge/tests-76%20passed-brightgreen" alt="Tests" />
10
+ <img src="https://img.shields.io/badge/license-MIT-blue" alt="MIT License" />
11
+ </p>
12
+
13
+ **任意进制(2-62)精确转换库** — 大整数无精度损失、小数用分数精确算术、循环小数用括号循环节表示并可无损往返。
14
+
15
+ ## 为什么用它
16
+
17
+ | 能力 | 内置 `int(s, base)` | 本库 |
18
+ |------|---------------------|------|
19
+ | 进制范围 | 2-36 | **2-62**(`0-9A-Za-z`,37 起大小写敏感) |
20
+ | 小数 | ✗ | ✓ **Fraction 精确**(二进制 `"0.1"` ≡ 1/2) |
21
+ | 循环小数 | ✗ | ✓ `1/3` ↔ `"0.(3)"` 无损往返 |
22
+ | 大整数 | ✓ | ✓(同一量级性能,见下) |
23
+ | CLI | ✗ | ✓ `baseconv` 一条命令 |
24
+
25
+ 浮点实现的转换在 `0.1` 这类值上必然引入二进制近似误差;本库全程使用 `int` 与 `fractions.Fraction`,**任何输入的转换结果都是数学上精确的**。
26
+
27
+ ## 安装
28
+
29
+ ```bash
30
+ git clone https://github.com/xiaomaozjj666/base-conversion.git
31
+ cd base-conversion
32
+ python -m pip install .
33
+ ```
34
+
35
+ > 尚未发布到 PyPI;从源码安装(零运行时依赖,仅标准库)。
36
+
37
+ ## 快速开始
38
+
39
+ ```python
40
+ >>> from fractions import Fraction
41
+ >>> from base_conversion import to_base, to_decimal
42
+ >>> to_base(255, 16)
43
+ 'FF'
44
+ >>> to_decimal("ff", 16) # 2-36 进制大小写不敏感
45
+ 255
46
+ >>> to_decimal("0.1", 2) # 精确:不是 0.5 的浮点近似
47
+ Fraction(1, 2)
48
+ >>> to_base(Fraction(1, 3), 10) # 循环小数 → 括号循环节
49
+ '0.(3)'
50
+ >>> to_decimal("0.(3)", 10) # 且可无损解析回来
51
+ Fraction(1, 3)
52
+ >>> to_base(10**100 + 7, 62) # 大整数无损
53
+ 'QCyvrY2MJnQFGlUHTCA95Xz8AHOrLuoIO0fuPkHHCcyXy9ytM5N1lqsh'
54
+ ```
55
+
56
+ 命令行:
57
+
58
+ ```bash
59
+ baseconv 255 10 16 # → FF
60
+ baseconv -- -FF 16 10 # → -255(位置参数负数用 -- 分隔)
61
+ baseconv -n -FF -f 16 -t 10 # → -255(-n 可直接接收负数值)
62
+ baseconv -n 0.1 -f 2 -t 10 # → 0.5(二进制小数精确转十进制)
63
+ ```
64
+
65
+ ## API
66
+
67
+ | 函数 | 签名 | 说明 |
68
+ |------|------|------|
69
+ | `to_decimal` | `(s: str, base: int) → int \| Fraction` | 解析 `base` 进制字符串;支持 `±`、小数点、括号循环节 |
70
+ | `to_base` | `(value: int \| Fraction \| float, base: int) → str` | 渲染为目标进制;非整数 float 拒绝(精度陷阱) |
71
+ | `decimal_to_base` / `base_to_decimal` | 同上 | 旧 API 兼容别名 |
72
+ | `decimal_to_binary` / `binary_to_decimal` | — | 二进制快捷方式 |
73
+ | `decimal_to_hex` / `hex_to_decimal` | — | 十六进制快捷方式(输出大写) |
74
+
75
+ 常量:`BASE_MIN=2`、`BASE_MAX=62`、`DIGITS`(62 字符表)。
76
+
77
+ ### 文法(`to_decimal` 接受的输入)
78
+
79
+ ```
80
+ [sign] digits [ "." digits ] [ "(" digits ")" ]
81
+ ```
82
+
83
+ - 2-36 进制大小写不敏感;37-62 大小写敏感(`'A'`=10、`'a'`=36);
84
+ - `".5"`、`"5."` 合法,单独的 `"."` 非法;
85
+ - 循环节括号必须收尾、非空、不嵌套:`"0.(3)"`、`"0.1(6)"` 合法,`"0.()"`、`"0.(3"` 非法。
86
+
87
+ ### 错误处理
88
+
89
+ - 非法字符 / 文法错误 → `ValueError`(信息含具体字符与进制);
90
+ - 进制越界(非 2-62)→ `ValueError`;非 int 进制或 `bool` → `TypeError`;
91
+ - `to_base` 收到非整数 `float` → `ValueError`(提示改用 `Fraction`;`to_base` 不接受数字字符串)。
92
+
93
+ ## 与内置的性能关系
94
+
95
+ 核心整数路径就是「除基取余 / 乘基累加」,与内置 `int(s, base)`、`format(n, 'x')` 同一数量级——测试含 500 例随机大整数交叉验证(对照内置 `int(s, base)`)与 300 例随机 Fraction 往返性质测试,10^60 量级毫秒级完成。本库的定位差异在**能力**(62 进制、精确小数、循环节)而非速度;极限吞吐场景请直接用内置函数。
96
+
97
+ ## 历史与归档
98
+
99
+ 本仓库早期是三语言(C++ / Python / JS)进制转换 + 查找排序基准的对比实现,2026-09 归档整理后:
100
+
101
+ - Python 部分重写为本库(2-62 进制、Fraction 精确小数、CLI、76 个测试);
102
+ - 三语言基准脚本保留在 [`archive/`](archive/)(历史快照,不再维护);
103
+ - 查找/排序算法练习与多语言题解迁移至 [programming-exercises](https://github.com/xiaomaozjj666/programming-exercises)。
104
+
105
+ 历史 benchmark 实测数据(2026-08-21 本机,三语言算法对比)见 [`docs/images/benchmark.svg`](docs/images/benchmark.svg)。
106
+
107
+ ## 开发
108
+
109
+ ```bash
110
+ python -m pip install -e ".[dev]"
111
+ pytest # 76 个测试(含随机交叉验证与往返性质测试)
112
+ ruff check src tests # 代码检查
113
+ mypy src # 类型检查
114
+ python -m build # 构建分发包
115
+ ```
116
+
117
+ CI 在 Python 3.9-3.13 矩阵上运行测试,并执行 lint、类型检查与构建验证。
118
+
119
+ 维护者发布流程见 [docs/RELEASING.md](docs/RELEASING.md),版本变更记录见 [CHANGELOG.md](CHANGELOG.md)。
120
+
121
+ ## License
122
+
123
+ [MIT](LICENSE)
@@ -0,0 +1,71 @@
1
+ [build-system]
2
+ requires = ["setuptools>=69"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "base-conversion"
7
+ version = "1.0.1"
8
+ description = "Exact arbitrary-base (2-62) number conversion: lossless big ints, Fraction-precise decimals, repeating-digit notation"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ authors = [{ name = "xiaomaozjj666" }]
12
+ requires-python = ">=3.9"
13
+ keywords = ["base", "radix", "conversion", "numbers", "fractions", "cli"]
14
+ classifiers = [
15
+ "Development Status :: 6 - Mature",
16
+ "Intended Audience :: Developers",
17
+
18
+ "Operating System :: OS Independent",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.9",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Programming Language :: Python :: 3.13",
25
+ "Topic :: Scientific/Engineering :: Mathematics",
26
+ "Topic :: Software Development :: Libraries :: Python Modules",
27
+ "Topic :: Utilities",
28
+ ]
29
+
30
+ [project.urls]
31
+ Homepage = "https://github.com/xiaomaozjj666/base-conversion"
32
+ Issues = "https://github.com/xiaomaozjj666/base-conversion/issues"
33
+ Changelog = "https://github.com/xiaomaozjj666/base-conversion/blob/main/CHANGELOG.md"
34
+
35
+ [project.scripts]
36
+ baseconv = "base_conversion.cli:main"
37
+
38
+ [project.optional-dependencies]
39
+ dev = ["pytest>=7", "ruff>=0.5", "mypy>=1.8", "build>=1.0", "setuptools>=69"]
40
+
41
+ [tool.setuptools]
42
+ package-dir = { "" = "src" }
43
+
44
+ [tool.setuptools.packages.find]
45
+ where = ["src"]
46
+
47
+ [tool.setuptools.package-data]
48
+ base_conversion = ["py.typed"]
49
+
50
+ [tool.pytest.ini_options]
51
+ testpaths = ["tests"]
52
+ addopts = "-q"
53
+
54
+ [tool.ruff]
55
+ line-length = 100
56
+ target-version = "py39"
57
+
58
+ [tool.ruff.lint]
59
+ select = ["E", "F", "W", "I", "UP", "B", "SIM"]
60
+
61
+ [tool.ruff.lint.per-file-ignores]
62
+ # 3.9 兼容的类型写法(from __future__ import annotations 已覆盖注解位置)
63
+ "tests/*" = ["SIM115"]
64
+
65
+ [tool.mypy]
66
+ # python_version = "3.9" 已删除:mypy ≥2 不再支持 3.9 目标,该行被静默拒绝
67
+ # (死配置)。代码仍保持 3.9 兼容(requires-python = ">=3.9" 不变),
68
+ # 类型检查在 CI 的 3.12 解释器下执行。
69
+ files = ["src"]
70
+ warn_unused_ignores = true
71
+ warn_redundant_casts = true
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,49 @@
1
+ """base-conversion — 任意进制(2-62)精确转换库。
2
+
3
+ 核心能力:
4
+
5
+ - 大整数无精度损失(Python 原生 ``int``);
6
+ - 小数用 ``Fraction`` 精确算术,循环小数用括号循环节表示并可无损往返
7
+ (``1/3`` ↔ ``"0.(3)"``);
8
+ - 2-36 进制大小写不敏感,37-62 大小写敏感(``0-9A-Za-z``);
9
+ - 提供 ``baseconv`` 命令行入口(``pip install`` 后可用)。
10
+
11
+ 快速上手::
12
+
13
+ >>> from base_conversion import to_base, to_decimal
14
+ >>> to_base(255, 16)
15
+ 'FF'
16
+ >>> to_decimal("0.1", 2)
17
+ Fraction(1, 2)
18
+ """
19
+
20
+ from base_conversion.converter import (
21
+ BASE_MAX,
22
+ BASE_MIN,
23
+ DIGITS,
24
+ base_to_decimal,
25
+ binary_to_decimal,
26
+ decimal_to_base,
27
+ decimal_to_binary,
28
+ decimal_to_hex,
29
+ hex_to_decimal,
30
+ to_base,
31
+ to_decimal,
32
+ )
33
+
34
+ __version__ = "1.0.0"
35
+
36
+ __all__ = [
37
+ "BASE_MAX",
38
+ "BASE_MIN",
39
+ "DIGITS",
40
+ "__version__",
41
+ "base_to_decimal",
42
+ "binary_to_decimal",
43
+ "decimal_to_base",
44
+ "decimal_to_binary",
45
+ "decimal_to_hex",
46
+ "hex_to_decimal",
47
+ "to_base",
48
+ "to_decimal",
49
+ ]
@@ -0,0 +1,129 @@
1
+ """``baseconv`` 命令行入口。
2
+
3
+ 用法::
4
+
5
+ baseconv NUMBER [FROM_BASE] [TO_BASE] # 位置参数式(基数默认 10)
6
+ baseconv -n NUMBER -f FROM -t TO # 选项式(负数输入推荐)
7
+ baseconv -- -123 10 2 # ``--`` 分隔符式
8
+
9
+ 输出即目标进制表示:整数为数字串,精确小数为有限位或括号循环节
10
+ (``0.(3)`` 表示 1/3 的十进制循环展开)。
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import argparse
16
+ import re
17
+ import sys
18
+
19
+ from base_conversion import __version__
20
+ from base_conversion.converter import to_base, to_decimal
21
+
22
+ # 带符号输入可能以数字、字母、小数点或循环括号开头,如 -123、-FF、-.5。
23
+ _SIGNED_NUMBER = re.compile(r"^[-+](?=[0-9A-Za-z.(])")
24
+ _OPTION_FLAGS = frozenset(
25
+ {"-h", "--help", "-V", "--version", "-n", "--number", "-f", "--from-base", "-t", "--to-base"}
26
+ )
27
+
28
+
29
+ def _is_signed_number(value: str) -> bool:
30
+ """判断参数是否形如带符号数字,而非 CLI 自身的选项。"""
31
+ return value not in _OPTION_FLAGS and _SIGNED_NUMBER.match(value) is not None
32
+
33
+
34
+ def _normalize_argv(argv: list[str]) -> list[str]:
35
+ """避免 argparse 将带符号数字误判为选项。
36
+
37
+ ``-n``/``--number`` 后的带符号值改写为 ``--number=VALUE``;首个位置参数
38
+ 为带符号值时保留原有兼容行为,自动在其前插入 ``--``。
39
+ """
40
+ normalized: list[str] = []
41
+ index = 0
42
+ while index < len(argv):
43
+ arg = argv[index]
44
+ if arg == "--":
45
+ normalized.extend(argv[index:])
46
+ break
47
+ if (
48
+ arg in ("-n", "--number")
49
+ and index + 1 < len(argv)
50
+ and _is_signed_number(argv[index + 1])
51
+ ):
52
+ normalized.append(f"--number={argv[index + 1]}")
53
+ index += 2
54
+ continue
55
+ normalized.append(arg)
56
+ index += 1
57
+
58
+ if normalized and _is_signed_number(normalized[0]):
59
+ return ["--", *normalized]
60
+ return normalized
61
+
62
+
63
+ def build_parser() -> argparse.ArgumentParser:
64
+ """构建命令行解析器。"""
65
+ parser = argparse.ArgumentParser(
66
+ prog="baseconv",
67
+ description=(
68
+ "任意进制(2-62)精确转换:大整数无损、小数用分数精确表示、循环小数输出括号循环节。"
69
+ ),
70
+ epilog=(
71
+ "示例:\n"
72
+ " baseconv 255 10 16 # → FF\n"
73
+ " baseconv -- -FF 16 10 # → -255(位置参数负数用 -- 分隔)\n"
74
+ " baseconv -n -FF -f 16 -t 10 # → -255(-n 可直接接收负数值)\n"
75
+ " baseconv -n 0.1 -f 2 -t 10 # → 0.5(二进制小数精确转十进制)"
76
+ ),
77
+ formatter_class=argparse.RawDescriptionHelpFormatter,
78
+ )
79
+ parser.add_argument(
80
+ "-V",
81
+ "--version",
82
+ action="version",
83
+ version=f"base-conversion {__version__}",
84
+ help="显示版本并退出",
85
+ )
86
+ parser.add_argument(
87
+ "number",
88
+ nargs="?",
89
+ help="待转换的数字(源进制字符串;负数可用 -- 分隔或用 -n 提供)",
90
+ )
91
+ parser.add_argument("from_base", nargs="?", type=int, default=10, help="源进制(默认 10)")
92
+ parser.add_argument("to_base_pos", nargs="?", type=int, default=10, help="目标进制(默认 10)")
93
+ parser.add_argument(
94
+ "-n", "--number", dest="number_opt", help="数字(可直接接收 -FF、-0.1 等负数值)"
95
+ )
96
+ parser.add_argument(
97
+ "-f", "--from-base", dest="from_base_opt", type=int, help="源进制(选项式,优先于位置)"
98
+ )
99
+ parser.add_argument(
100
+ "-t", "--to-base", dest="to_base_opt", type=int, help="目标进制(选项式,优先于位置)"
101
+ )
102
+ return parser
103
+
104
+
105
+ def main(argv: list[str] | None = None) -> int:
106
+ """CLI 入口:返回 0 成功 / 1 转换错误 / 2 用法错误(argparse 自带)。"""
107
+ args_list = sys.argv[1:] if argv is None else list(argv)
108
+ args_list = _normalize_argv(args_list)
109
+ parser = build_parser()
110
+ ns = parser.parse_args(args_list)
111
+
112
+ number = ns.number_opt if ns.number_opt is not None else ns.number
113
+ from_base = ns.from_base_opt if ns.from_base_opt is not None else ns.from_base
114
+ target_base = ns.to_base_opt if ns.to_base_opt is not None else ns.to_base_pos
115
+
116
+ if number is None:
117
+ parser.error("必须提供待转换的数字(位置参数或 -n)")
118
+
119
+ try:
120
+ value = to_decimal(number, from_base)
121
+ print(to_base(value, target_base))
122
+ except (ValueError, TypeError) as exc:
123
+ print(f"baseconv: 错误: {exc}", file=sys.stderr)
124
+ return 1
125
+ return 0
126
+
127
+
128
+ if __name__ == "__main__":
129
+ raise SystemExit(main())