fix: 修复含特殊字符路径的钻带文件读取失败 — 移除 cmd /c type,改为 AnsiFileReader 直接文件IO
问题:路径含 ( ) & % ! ^ @ 等字符时报"文件路径包含不允许的命令行特殊字符",
(D2-f)、(1) 这类正常业务文件名直接命中。
根因:读取统一入口把路径拼进命令行(cmd.exe /d /c type "..."),为"防炸"在 03d9b8b
引入 ForbiddenPathChars 黑名单——并非业务需求,是实现的自我约束。
实测:真正会坏的只有 %VAR%(必坏) 与延迟展开开启时的 !VAR!,而 ( ) & ^ @ 空格在
双引号内均正常;且 type 是字节透传,这层 cmd 对当初的编码问题毫无帮助。
改动:
- 新增 AnsiFileReader.cs:FileStream + StreamReader(默认 936 解码 + BOM 探测,
与旧 StreamReader 解码路径逐字符等价),不经命令行
- 删除 CommandTypeFileReader.cs 及其 ForbiddenPathChars 字符校验
- 路径只保留"非空"与"文件存在"校验;IO 异常包装为
InvalidOperationException("读取文件失败:{路径}({原因})")并保留 InnerException
- 15 处调用点同步改名;更新 CLAUDE.md 与 Docs 说明
验证:14 个 demo_drl 样例新旧实现字符串/行数逐字符一致;含特殊字符路径(含 %TEMP% 字面
路径,旧实现 rc=1)可读可解析;UTF-8/UTF-16 BOM 结果与旧实现一致;
dotnet build -p:Platform=x86 0 错误且警告集与基线逐条相同。
This commit is contained in:
59
AnsiFileReader.cs
Normal file
59
AnsiFileReader.cs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace DrillTools
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 钻带/参数文件统一读取入口。
|
||||||
|
/// 默认按 ANSI/GB2312(code page 936) 解码,等价于历史 `cmd type` 实现的字节透传语义,
|
||||||
|
/// 但不经过命令行,因此路径中的特殊字符(如 `(` `)` `&` `%` `!` `^` `@` 空格)不会影响读取。
|
||||||
|
/// </summary>
|
||||||
|
internal static class AnsiFileReader
|
||||||
|
{
|
||||||
|
private const int Gb2312CodePage = 936;
|
||||||
|
|
||||||
|
public static string ReadAllText(string filePath)
|
||||||
|
{
|
||||||
|
return ReadAllText(filePath, CreateAnsiEncoding());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ReadAllText(string filePath, Encoding encoding)
|
||||||
|
{
|
||||||
|
string normalizedPath = ValidateAndNormalizePath(filePath);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var stream = new FileStream(normalizedPath, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||||
|
|
||||||
|
// detectEncodingFromByteOrderMarks: true 与历史实现保持一致:
|
||||||
|
// 原先经 Process.StandardOutput(StreamReader) 解码,默认会探测 BOM,
|
||||||
|
// 因此带 UTF-8/UTF-16 BOM 的文件解码结果保持不变。
|
||||||
|
using var reader = new StreamReader(stream, encoding, detectEncodingFromByteOrderMarks: true);
|
||||||
|
return reader.ReadToEnd();
|
||||||
|
}
|
||||||
|
catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"读取文件失败:{normalizedPath}({ex.Message})", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ValidateAndNormalizePath(string filePath)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(filePath))
|
||||||
|
throw new ArgumentException("文件路径不能为空", nameof(filePath));
|
||||||
|
|
||||||
|
string normalizedPath = Path.GetFullPath(filePath);
|
||||||
|
|
||||||
|
if (!File.Exists(normalizedPath))
|
||||||
|
throw new FileNotFoundException($"文件不存在:{normalizedPath}", normalizedPath);
|
||||||
|
|
||||||
|
return normalizedPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Encoding CreateAnsiEncoding()
|
||||||
|
{
|
||||||
|
return Encoding.GetEncoding(Gb2312CodePage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,7 +39,7 @@ namespace DrillTools
|
|||||||
ShutdownMode = ShutdownMode.OnExplicitShutdown;
|
ShutdownMode = ShutdownMode.OnExplicitShutdown;
|
||||||
|
|
||||||
// 只读取和解析文件一次,提取所有需要的信息
|
// 只读取和解析文件一次,提取所有需要的信息
|
||||||
string content = CommandTypeFileReader.ReadAllText(filePath);
|
string content = AnsiFileReader.ReadAllText(filePath);
|
||||||
var viewModel = new MainWindowViewModel
|
var viewModel = new MainWindowViewModel
|
||||||
{
|
{
|
||||||
IsStartupDrillTapeFile = true,
|
IsStartupDrillTapeFile = true,
|
||||||
@@ -150,7 +150,7 @@ namespace DrillTools
|
|||||||
ShouldCheckSortFileOnLoad = false
|
ShouldCheckSortFileOnLoad = false
|
||||||
};
|
};
|
||||||
|
|
||||||
string content = CommandTypeFileReader.ReadAllText(filePath);
|
string content = AnsiFileReader.ReadAllText(filePath);
|
||||||
viewModel.OriginalFilePath = filePath;
|
viewModel.OriginalFilePath = filePath;
|
||||||
viewModel.LoadToolsFromDrillTape(content);
|
viewModel.LoadToolsFromDrillTape(content);
|
||||||
|
|
||||||
@@ -185,7 +185,7 @@ namespace DrillTools
|
|||||||
ShouldCheckSortFileOnLoad = false
|
ShouldCheckSortFileOnLoad = false
|
||||||
};
|
};
|
||||||
|
|
||||||
string content = CommandTypeFileReader.ReadAllText(filePath);
|
string content = AnsiFileReader.ReadAllText(filePath);
|
||||||
viewModel.LoadToolsFromDrillTape(content);
|
viewModel.LoadToolsFromDrillTape(content);
|
||||||
string outputFilePath = viewModel.GeneratePpDrillTape();
|
string outputFilePath = viewModel.GeneratePpDrillTape();
|
||||||
|
|
||||||
@@ -232,7 +232,7 @@ namespace DrillTools
|
|||||||
|
|
||||||
var result = scalingWindow.ScalingResult.Value;
|
var result = scalingWindow.ScalingResult.Value;
|
||||||
|
|
||||||
string content = CommandTypeFileReader.ReadAllText(filePath);
|
string content = AnsiFileReader.ReadAllText(filePath);
|
||||||
string scaledContent = Integration.ExcellonScaler.Scale(content, result.Kx, result.Ky);
|
string scaledContent = Integration.ExcellonScaler.Scale(content, result.Kx, result.Ky);
|
||||||
|
|
||||||
// 在源文件所在目录下新建子文件夹 dr2 x{InputX} y{InputY}
|
// 在源文件所在目录下新建子文件夹 dr2 x{InputX} y{InputY}
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ Supported file extensions: `.txt`, `.drl`, `.dr2`, `.dpin`
|
|||||||
## Important Technical Details
|
## Important Technical Details
|
||||||
|
|
||||||
- **File encoding**: ANSI/GB2312 (code page 936) — drill tape files use this encoding, not UTF-8
|
- **File encoding**: ANSI/GB2312 (code page 936) — drill tape files use this encoding, not UTF-8
|
||||||
- **Encrypted file reading**: Uses `cmd.exe /c type` to read drill tape files (some files are "encrypted" and cannot be read directly via standard file I/O)
|
- **File reading**: Drill tape / parameter files are read through `AnsiFileReader` (direct `FileStream` + `StreamReader` with ANSI/936 as the default encoding, BOM detection kept as before), never through a command line. Paths containing `(`, `)`, `&`, `%`, `!`, `^`, `@` or spaces read fine. The old `cmd.exe /c type` reader was removed on 2026-09-20 — there is no encryption layer (verified: all `demo_drl/` samples decode identically via plain file I/O).
|
||||||
- **Slot hole calculation**: Uses CAM350 standard tolerance (0.0127mm) for computing hole counts along slot paths
|
- **Slot hole calculation**: Uses CAM350 standard tolerance (0.0127mm) for computing hole counts along slot paths
|
||||||
- **Backup strategy**: Creates `.bak` files when saving modified drill tape data
|
- **Backup strategy**: Creates `.bak` files when saving modified drill tape data
|
||||||
- **Command-line support**: App accepts a file path argument to auto-load a drill tape file on startup
|
- **Command-line support**: App accepts a file path argument to auto-load a drill tape file on startup
|
||||||
|
|||||||
@@ -1,85 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace DrillTools
|
|
||||||
{
|
|
||||||
internal static class CommandTypeFileReader
|
|
||||||
{
|
|
||||||
private const int Gb2312CodePage = 936;
|
|
||||||
|
|
||||||
private static readonly char[] ForbiddenPathChars =
|
|
||||||
{
|
|
||||||
'&', '|', '<', '>', '^', '%', '!', '(', ')', '@', '"', '\r', '\n'
|
|
||||||
};
|
|
||||||
|
|
||||||
public static string ReadAllText(string filePath)
|
|
||||||
{
|
|
||||||
return ReadAllText(filePath, CreateAnsiEncoding());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string ReadAllText(string filePath, Encoding encoding)
|
|
||||||
{
|
|
||||||
string normalizedPath = ValidateAndNormalizePath(filePath);
|
|
||||||
|
|
||||||
using var process = new Process
|
|
||||||
{
|
|
||||||
StartInfo = CreateStartInfo(normalizedPath, encoding)
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!process.Start())
|
|
||||||
throw new InvalidOperationException("无法启动文件读取进程");
|
|
||||||
|
|
||||||
var outputTask = process.StandardOutput.ReadToEndAsync();
|
|
||||||
var errorTask = process.StandardError.ReadToEndAsync();
|
|
||||||
process.WaitForExit();
|
|
||||||
|
|
||||||
string output = outputTask.GetAwaiter().GetResult();
|
|
||||||
string error = errorTask.GetAwaiter().GetResult();
|
|
||||||
|
|
||||||
if (process.ExitCode != 0)
|
|
||||||
throw new InvalidOperationException($"读取文件失败,退出代码:{process.ExitCode},错误:{error.Trim()}");
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ProcessStartInfo CreateStartInfo(string filePath, Encoding encoding)
|
|
||||||
{
|
|
||||||
return new ProcessStartInfo
|
|
||||||
{
|
|
||||||
FileName = "cmd.exe",
|
|
||||||
Arguments = $"/d /c type \"{filePath}\"",
|
|
||||||
RedirectStandardOutput = true,
|
|
||||||
RedirectStandardError = true,
|
|
||||||
UseShellExecute = false,
|
|
||||||
CreateNoWindow = true,
|
|
||||||
StandardOutputEncoding = encoding,
|
|
||||||
StandardErrorEncoding = encoding
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string ValidateAndNormalizePath(string filePath)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(filePath))
|
|
||||||
throw new ArgumentException("文件路径不能为空", nameof(filePath));
|
|
||||||
|
|
||||||
if (filePath.IndexOfAny(ForbiddenPathChars) >= 0)
|
|
||||||
throw new ArgumentException("文件路径包含不允许的命令行特殊字符", nameof(filePath));
|
|
||||||
|
|
||||||
string normalizedPath = Path.GetFullPath(filePath);
|
|
||||||
if (normalizedPath.IndexOfAny(ForbiddenPathChars) >= 0)
|
|
||||||
throw new ArgumentException("文件路径包含不允许的命令行特殊字符", nameof(filePath));
|
|
||||||
|
|
||||||
if (!File.Exists(normalizedPath))
|
|
||||||
throw new FileNotFoundException($"文件不存在:{normalizedPath}", normalizedPath);
|
|
||||||
|
|
||||||
return normalizedPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Encoding CreateAnsiEncoding()
|
|
||||||
{
|
|
||||||
return Encoding.GetEncoding(Gb2312CodePage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
107
Docs/含特殊字符路径读取修复说明.md
Normal file
107
Docs/含特殊字符路径读取修复说明.md
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
# 含特殊字符路径的钻带文件读取修复说明
|
||||||
|
|
||||||
|
> 修复日期:2026-09-20(任务 `.trellis/tasks/09-20-fix-special-char-path-read`)
|
||||||
|
> 影响面:所有钻带/参数文件读取入口(共 15 处调用)
|
||||||
|
|
||||||
|
## 问题描述
|
||||||
|
|
||||||
|
选择路径或文件名中含特定字符的钻带/参数文件时,加载直接失败:
|
||||||
|
|
||||||
|
```
|
||||||
|
加载钻带文件失败:文件路径包含不允许的命令行特殊字符(Parameter 'filePath')
|
||||||
|
```
|
||||||
|
|
||||||
|
命中字符包括 `(` `)` `&` `%` `!` `^` `@` `|` `<` `>` 等。实际业务文件名很容易命中,例如:
|
||||||
|
|
||||||
|
- `1009B-2013钻机参数表(D2-f)(A1A2厂) (1).doc`
|
||||||
|
- `demo_drl (1)/xx.drl`
|
||||||
|
|
||||||
|
## 问题分析
|
||||||
|
|
||||||
|
### 直接原因
|
||||||
|
|
||||||
|
读取统一入口 `CommandTypeFileReader.ReadAllText` 把文件路径拼进命令行,用子进程读文件:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
FileName = "cmd.exe",
|
||||||
|
Arguments = $"/d /c type \"{filePath}\"",
|
||||||
|
```
|
||||||
|
|
||||||
|
因为路径会成为命令行的组成部分,代码在 `ValidateAndNormalizePath` 里加了一串字符黑名单
|
||||||
|
(`ForbiddenPathChars`)“防炸”,命中即抛异常。
|
||||||
|
|
||||||
|
### 历史成因(git 证据)
|
||||||
|
|
||||||
|
| 提交 | 内容 |
|
||||||
|
|---|---|
|
||||||
|
| `44de1cb`(2025-12-17) | “优化 sort.txt 读取方式…改用 cmd type 命令读取…提升编码兼容性”。真实动因是编码:.NET 6 下 `Encoding.Default` = UTF-8,读 GB2312 文件乱码,而当时 `Encoding.GetEncoding(936)` 因未注册 CodePages provider 会抛异常,于是绕道 `cmd type` + `StandardOutputEncoding = 936` |
|
||||||
|
| `473b2ca`(2026-05-23) | 注册 `CodePagesEncodingProvider`,删掉 `GetEncoding` 的 catch 回退 —— 真正的根因已修,但没拆掉多余的 cmd 层 |
|
||||||
|
| `03d9b8b`(2026-05-16,Refactor) | 把散落的内联 `ProcessStartInfo` 抽成 `CommandTypeFileReader`,**首次**引入 `ForbiddenPathChars` 黑名单 |
|
||||||
|
|
||||||
|
即:把路径拼进命令行 ⇒ 必须“防炸” ⇒ 造出黑名单。黑名单并非业务需求,是实现的自我约束。
|
||||||
|
|
||||||
|
### 实测结论(Windows 10.0.26100,`cmd /d /c type "<path>"`)
|
||||||
|
|
||||||
|
| 路径中的字符 | 实测结果 | 说明 |
|
||||||
|
|---|---|---|
|
||||||
|
| `(` `)` `&` `^` `@` 空格 | **可正常读取** | 双引号内 cmd 不把 `&`/`^` 当分隔符 |
|
||||||
|
| `%CD%` / `%TEMP%` / `%USERNAME%` | **读取失败**(rc=1) | 变量展开在双引号内**依然发生** |
|
||||||
|
| `!CD!` | 默认可读;`/v:on` 时失败 | 依赖延迟展开开关 |
|
||||||
|
| 含 `0x1A` 等二进制字节 | 字节完全一致 | `type` 是字节透传,不做任何转换 |
|
||||||
|
|
||||||
|
结论:黑名单属于**过度防御** —— 真正会坏的只有 `%VAR%`(必坏)和 `!VAR!`(延迟展开开启时坏),
|
||||||
|
却把 `(` `)` `&` `^` `@` 等完全安全的字符一并禁掉,直接误伤 `(D2-f)`、`(1)` 这类正常文件名。
|
||||||
|
而 `type` 既然只是字节透传,这层 cmd 对编码问题其实毫无贡献。
|
||||||
|
|
||||||
|
## 解决方案
|
||||||
|
|
||||||
|
1. 新增 `AnsiFileReader.cs`(原 `CommandTypeFileReader.cs` 已删除),改为直接文件 IO:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using var stream = new FileStream(normalizedPath, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||||
|
using var reader = new StreamReader(stream, encoding, detectEncodingFromByteOrderMarks: true);
|
||||||
|
return reader.ReadToEnd();
|
||||||
|
```
|
||||||
|
|
||||||
|
`detectEncodingFromByteOrderMarks: true` 是为了**保持旧行为**:原先经 `Process.StandardOutput`(内部是
|
||||||
|
`StreamReader`)解码时默认会探测 BOM,所以带 UTF-8/UTF-16 BOM 的文件仍然按 BOM 指定的编码解码。
|
||||||
|
|
||||||
|
2. 删除 `ForbiddenPathChars` 与两处字符校验;路径只保留“非空”与“文件存在”两项校验。
|
||||||
|
3. IO 异常统一包装为可诊断消息:`InvalidOperationException("读取文件失败:{路径}({原因})")`,并保留 `innerException`。
|
||||||
|
4. 默认编码仍为 ANSI/GB2312(code page 936),`ReadAllText(filePath, encoding)` 重载能力不变。
|
||||||
|
5. 同步更新 `.trellis/spec/backend/` 中 6 处已失效的规范描述(`cmd type`、危险字符校验、进程退出码)。
|
||||||
|
|
||||||
|
## 验证结果
|
||||||
|
|
||||||
|
用真实 `demo_drl/` 样例与旧实现对照(同一编码解码):
|
||||||
|
|
||||||
|
| 验证项 | 结果 |
|
||||||
|
|---|---|
|
||||||
|
| 14 个 demo 样例:新旧实现字符串**逐字符一致、行数一致** | ✅ PASS |
|
||||||
|
| 特殊字符路径可读:`demo (a&b) 100%/xx (1) -A&B.drl`、`yy (2)!^.drl` | ✅ PASS |
|
||||||
|
| `%VAR%` 字面路径可读:`pct%TEMP%end/pct%TEMP%end (3).drl`(旧实现 rc=1 失败) | ✅ PASS |
|
||||||
|
| BOM 行为一致:UTF-8 BOM / UTF-16 LE / UTF-16 BE 文件新旧实现结果相同 | ✅ PASS |
|
||||||
|
| 文件被独占占用 → `InvalidOperationException("读取文件失败:...")` 且 `InnerException is IOException` | ✅ PASS |
|
||||||
|
| 空路径 → `ArgumentException("文件路径不能为空")` | ✅ PASS |
|
||||||
|
| 不存在路径 → `FileNotFoundException("文件不存在:...")` | ✅ PASS |
|
||||||
|
| `dotnet build -p:Platform=x86` | 0 错误,无新增警告 |
|
||||||
|
|
||||||
|
## 行为一致性说明(解码层)
|
||||||
|
|
||||||
|
本次只替换“怎么拿到字节”(去掉命令行),**没有改变“怎么解码”**:
|
||||||
|
|
||||||
|
| 场景 | 旧实现(cmd type + StreamReader) | 新实现(FileStream + StreamReader) |
|
||||||
|
|---|---|---|
|
||||||
|
| 无 BOM 的 GB2312/ANSI 文件 | 936 解码 | 936 解码(一致) |
|
||||||
|
| UTF-8 BOM 文件 | 探测到 BOM → 改用 UTF-8、去掉 BOM | 相同(一致) |
|
||||||
|
| UTF-16 LE/BE BOM 文件 | 探测到 BOM → 按 UTF-16 解码 | 相同(一致) |
|
||||||
|
| 含 `0x1A` 等二进制字节 | 字节透传后 936 解码 | 相同(一致) |
|
||||||
|
|
||||||
|
即:保留历史实现**隐式具备**的 BOM 支持,而不是新增特性,也不是回归。
|
||||||
|
若将来要改成固定 936(不探测 BOM),属于行为变更,需要单独任务与用户确认。
|
||||||
|
|
||||||
|
## 后续约定
|
||||||
|
|
||||||
|
- 钻带/参数文件读取统一走 `AnsiFileReader.ReadAllText`,**不要再引入子进程/命令行方式读文件**。
|
||||||
|
- 不要对文件路径做字符黑名单校验:读取不经过命令行,此类限制只会误伤正常文件名。
|
||||||
|
- 需要更换编码时使用 `ReadAllText(filePath, encoding)` 重载,不要另起第二套读取逻辑。
|
||||||
@@ -70,7 +70,7 @@ namespace DrillTools
|
|||||||
|
|
||||||
public static void ClearParametersAndSave(string filePath)
|
public static void ClearParametersAndSave(string filePath)
|
||||||
{
|
{
|
||||||
string content = CommandTypeFileReader.ReadAllText(filePath);
|
string content = AnsiFileReader.ReadAllText(filePath);
|
||||||
string cleanedContent = ClearParameters(content);
|
string cleanedContent = ClearParameters(content);
|
||||||
|
|
||||||
string backupFilePath = filePath + ".bak";
|
string backupFilePath = filePath + ".bak";
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ namespace DrillTools
|
|||||||
public static void ReplaceParameters(string drlFilePath, string paramFilePath, out List<double> unmatchedDiameters)
|
public static void ReplaceParameters(string drlFilePath, string paramFilePath, out List<double> unmatchedDiameters)
|
||||||
{
|
{
|
||||||
// 1. 读取参数文件,提取第一行描述和直径→参数字符串映射
|
// 1. 读取参数文件,提取第一行描述和直径→参数字符串映射
|
||||||
string paramContent = CommandTypeFileReader.ReadAllText(paramFilePath);
|
string paramContent = AnsiFileReader.ReadAllText(paramFilePath);
|
||||||
var paramLines = DrillTapeLineParser.SplitLines(paramContent);
|
var paramLines = DrillTapeLineParser.SplitLines(paramContent);
|
||||||
|
|
||||||
string paramDescription = ExtractDescription(paramLines);
|
string paramDescription = ExtractDescription(paramLines);
|
||||||
@@ -70,7 +70,7 @@ namespace DrillTools
|
|||||||
throw new InvalidOperationException("参数文件中未找到有效的参数行(格式:T00C<直径>H...)");
|
throw new InvalidOperationException("参数文件中未找到有效的参数行(格式:T00C<直径>H...)");
|
||||||
|
|
||||||
// 2. 读取 .drl 文件
|
// 2. 读取 .drl 文件
|
||||||
string drlContent = CommandTypeFileReader.ReadAllText(drlFilePath);
|
string drlContent = AnsiFileReader.ReadAllText(drlFilePath);
|
||||||
|
|
||||||
// 3. 执行替换(同时收集未匹配直径)
|
// 3. 执行替换(同时收集未匹配直径)
|
||||||
var unmatchedSet = new HashSet<double>();
|
var unmatchedSet = new HashSet<double>();
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ namespace DrillTools
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string content = CommandTypeFileReader.ReadAllText(filePath);
|
string content = AnsiFileReader.ReadAllText(filePath);
|
||||||
var lines = DrillTapeLineParser.SplitLines(content);
|
var lines = DrillTapeLineParser.SplitLines(content);
|
||||||
if (lines.Count > 0)
|
if (lines.Count > 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ namespace DrillTools
|
|||||||
// 异步加载文件以避免阻塞UI
|
// 异步加载文件以避免阻塞UI
|
||||||
await System.Threading.Tasks.Task.Run(() =>
|
await System.Threading.Tasks.Task.Run(() =>
|
||||||
{
|
{
|
||||||
string drillTapeContent = CommandTypeFileReader.ReadAllText(filePath);
|
string drillTapeContent = AnsiFileReader.ReadAllText(filePath);
|
||||||
|
|
||||||
// 在UI线程中更新界面
|
// 在UI线程中更新界面
|
||||||
this.Dispatcher.Invoke(() =>
|
this.Dispatcher.Invoke(() =>
|
||||||
@@ -142,7 +142,7 @@ namespace DrillTools
|
|||||||
ViewModel.IsStartupDrillTapeFile = false;
|
ViewModel.IsStartupDrillTapeFile = false;
|
||||||
ViewModel.OriginalFilePath = openFileDialog.FileName;
|
ViewModel.OriginalFilePath = openFileDialog.FileName;
|
||||||
|
|
||||||
string drillTapeContent = CommandTypeFileReader.ReadAllText(openFileDialog.FileName);
|
string drillTapeContent = AnsiFileReader.ReadAllText(openFileDialog.FileName);
|
||||||
|
|
||||||
ViewModel.LoadToolsFromDrillTape(drillTapeContent);
|
ViewModel.LoadToolsFromDrillTape(drillTapeContent);
|
||||||
}
|
}
|
||||||
@@ -336,7 +336,7 @@ namespace DrillTools
|
|||||||
ViewModel.IsStartupDrillTapeFile = false;
|
ViewModel.IsStartupDrillTapeFile = false;
|
||||||
ViewModel.OriginalFilePath = files[0];
|
ViewModel.OriginalFilePath = files[0];
|
||||||
|
|
||||||
string drillTapeContent = CommandTypeFileReader.ReadAllText(files[0]);
|
string drillTapeContent = AnsiFileReader.ReadAllText(files[0]);
|
||||||
|
|
||||||
ViewModel.LoadToolsFromDrillTape(drillTapeContent);
|
ViewModel.LoadToolsFromDrillTape(drillTapeContent);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1670,7 +1670,7 @@ M30";
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string fileContent = CommandTypeFileReader.ReadAllText(sortFilePath);
|
string fileContent = AnsiFileReader.ReadAllText(sortFilePath);
|
||||||
|
|
||||||
// 按行分割内容
|
// 按行分割内容
|
||||||
string[] lines = fileContent.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
string[] lines = fileContent.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
@@ -2255,7 +2255,7 @@ M30";
|
|||||||
throw new System.IO.FileNotFoundException($"参考钻带文件不存在:{referenceFilePath}");
|
throw new System.IO.FileNotFoundException($"参考钻带文件不存在:{referenceFilePath}");
|
||||||
}
|
}
|
||||||
|
|
||||||
string drillTapeContent = CommandTypeFileReader.ReadAllText(referenceFilePath);
|
string drillTapeContent = AnsiFileReader.ReadAllText(referenceFilePath);
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(drillTapeContent))
|
if (string.IsNullOrWhiteSpace(drillTapeContent))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ namespace DrillTools
|
|||||||
throw new InvalidOperationException("参数文件中未找到有效的参数行(格式:T00C<直径>S...)");
|
throw new InvalidOperationException("参数文件中未找到有效的参数行(格式:T00C<直径>S...)");
|
||||||
|
|
||||||
// 2. 读取 .rou 文件
|
// 2. 读取 .rou 文件
|
||||||
string rouContent = CommandTypeFileReader.ReadAllText(rouFilePath);
|
string rouContent = AnsiFileReader.ReadAllText(rouFilePath);
|
||||||
|
|
||||||
// 3. 执行替换(同时收集未匹配直径)
|
// 3. 执行替换(同时收集未匹配直径)
|
||||||
var unmatchedSet = new HashSet<double>();
|
var unmatchedSet = new HashSet<double>();
|
||||||
@@ -70,7 +70,7 @@ namespace DrillTools
|
|||||||
{
|
{
|
||||||
var dict = new Dictionary<double, string>();
|
var dict = new Dictionary<double, string>();
|
||||||
|
|
||||||
string content = CommandTypeFileReader.ReadAllText(paramFilePath);
|
string content = AnsiFileReader.ReadAllText(paramFilePath);
|
||||||
var lines = DrillTapeLineParser.SplitLines(content);
|
var lines = DrillTapeLineParser.SplitLines(content);
|
||||||
|
|
||||||
foreach (var line in lines)
|
foreach (var line in lines)
|
||||||
|
|||||||
Reference in New Issue
Block a user