Files
AohDrllTools/DrillTapeParameterCleaner.cs
Mr.Xia b75439cff4 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 错误且警告集与基线逐条相同。
2026-09-20 11:30:04 +08:00

118 lines
3.9 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
namespace DrillTools
{
internal static class DrillTapeParameterCleaner
{
private const int Gb2312CodePage = 936;
private static readonly Regex HeaderToolDefinitionStartPattern = new(
@"^T\d+C",
RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static readonly Regex ToolDefinitionPattern = new(
@"^(?<prefix>\s*T\d+C\d+(?:\.\d+)?)(?<parameters>H.+)$",
RegexOptions.Compiled | RegexOptions.IgnoreCase);
public static bool CanClearParameters(string drillTapeContent)
{
var lines = DrillTapeLineParser.SplitLines(drillTapeContent);
if (lines.Count < 2)
return false;
string secondLine = lines[1].Text.Trim();
if (!secondLine.StartsWith(";") || secondLine.Length == 1)
return false;
var toolDefinitionLines = GetHeaderToolDefinitionLines(lines);
if (toolDefinitionLines.Count == 0)
return false;
foreach (var line in toolDefinitionLines)
{
if (!ToolDefinitionPattern.IsMatch(line.Text.TrimEnd()))
return false;
}
return true;
}
public static string ClearParameters(string drillTapeContent)
{
if (!CanClearParameters(drillTapeContent))
throw new InvalidOperationException("钻带文件不符合清空参数条件");
var lines = DrillTapeLineParser.SplitLines(drillTapeContent);
var builder = new StringBuilder(drillTapeContent.Length);
for (int i = 0; i < lines.Count; i++)
{
if (i == 1)
continue;
string text = lines[i].Text;
var match = ToolDefinitionPattern.Match(text.TrimEnd());
if (match.Success && IsBeforeHeaderEnd(lines, i))
{
int trailingWhitespaceLength = text.Length - text.TrimEnd().Length;
string trailingWhitespace = trailingWhitespaceLength > 0 ? text[^trailingWhitespaceLength..] : string.Empty;
text = match.Groups["prefix"].Value + trailingWhitespace;
}
builder.Append(text);
builder.Append(lines[i].LineEnding);
}
return builder.ToString();
}
public static void ClearParametersAndSave(string filePath)
{
string content = AnsiFileReader.ReadAllText(filePath);
string cleanedContent = ClearParameters(content);
string backupFilePath = filePath + ".bak";
File.Copy(filePath, backupFilePath, true);
using var writer = new StreamWriter(filePath, false, CreateAnsiEncoding());
writer.Write(cleanedContent);
}
private static List<DrillTapeLine> GetHeaderToolDefinitionLines(IReadOnlyList<DrillTapeLine> lines)
{
var toolDefinitionLines = new List<DrillTapeLine>();
foreach (var line in lines)
{
string trimmedLine = line.Text.Trim();
if (trimmedLine == "%")
break;
if (HeaderToolDefinitionStartPattern.IsMatch(trimmedLine))
toolDefinitionLines.Add(line);
}
return toolDefinitionLines;
}
private static bool IsBeforeHeaderEnd(IReadOnlyList<DrillTapeLine> lines, int lineIndex)
{
for (int i = 0; i < lineIndex; i++)
{
if (lines[i].Text.Trim() == "%")
return false;
}
return true;
}
private static Encoding CreateAnsiEncoding()
{
return Encoding.GetEncoding(Gb2312CodePage);
}
}
}