using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Text; using System.Text.RegularExpressions; namespace DrillTools { /// /// ROU 成型参数替换:将参数文件中的 S/F/R/Z/H 参数追加到 .rou 文件的刀径定义行。 /// internal static class RouParameterReplacer { private const int Gb2312CodePage = 936; private const double DiameterTolerance = 0.001; /// /// 匹配参数行:T00C<直径>S<参数...> /// 例:T00C1.800S55.0F5.0R25Z0H600 /// private static readonly Regex ParamLinePattern = new( @"^T00C(?\d+(?:\.\d+)?)(?S.+?)\s*$", RegexOptions.Compiled | RegexOptions.IgnoreCase); /// /// 匹配 .rou 刀径定义行:T<刀号>C<直径> /// 例:T01C1.8 /// private static readonly Regex ToolLinePattern = new( @"^T\d+C(?\d+(?:\.\d+)?)", RegexOptions.Compiled | RegexOptions.IgnoreCase); /// /// 执行参数替换。 /// /// .rou 文件完整路径 /// 参数 .txt 文件完整路径 public static void ReplaceParameters(string rouFilePath, string paramFilePath, out List unmatchedDiameters) { // 1. 读取参数文件,构建直径→参数字符串映射 var paramDict = LoadParameterDictionary(paramFilePath); if (paramDict.Count == 0) throw new InvalidOperationException("参数文件中未找到有效的参数行(格式:T00C<直径>S...)"); // 2. 读取 .rou 文件 string rouContent = CommandTypeFileReader.ReadAllText(rouFilePath); // 3. 执行替换(同时收集未匹配直径) var unmatchedSet = new HashSet(); string replacedContent = ReplaceParametersInContent(rouContent, paramDict, unmatchedSet); // 4. 备份原文件 string backupFilePath = rouFilePath + ".bak"; File.Copy(rouFilePath, backupFilePath, true); // 5. 写回文件 using var writer = new StreamWriter(rouFilePath, false, Encoding.GetEncoding(Gb2312CodePage)); writer.Write(replacedContent); // 6. 输出未匹配直径 unmatchedDiameters = new List(unmatchedSet); } /// /// 从参数文件加载直径→参数字符串(从 S 开始到行尾)的映射。 /// private static Dictionary LoadParameterDictionary(string paramFilePath) { var dict = new Dictionary(); string content = CommandTypeFileReader.ReadAllText(paramFilePath); var lines = DrillTapeLineParser.SplitLines(content); foreach (var line in lines) { string trimmed = line.Text.Trim(); var match = ParamLinePattern.Match(trimmed); if (!match.Success) continue; if (!double.TryParse(match.Groups["diameter"].Value, NumberStyles.Float, CultureInfo.InvariantCulture, out double diameter)) continue; string paramString = match.Groups["params"].Value; // 如果同一直径出现多次,后面的覆盖前面的 dict[diameter] = paramString; } return dict; } /// /// 在 .rou 文件内容中,将 M48~% 区域内的刀径行末尾追加参数。 /// private static string ReplaceParametersInContent(string rouContent, Dictionary paramDict, HashSet unmatchedDiameters) { var lines = DrillTapeLineParser.SplitLines(rouContent); var builder = new StringBuilder(rouContent.Length + 1024); bool inHeader = false; foreach (var line in lines) { string trimmed = line.Text.Trim(); // 检测 M48 头部开始 if (!inHeader && trimmed == "M48") { inHeader = true; builder.Append(line.Text); builder.Append(line.LineEnding); continue; } // 检测 % 头部结束 if (inHeader && trimmed == "%") { inHeader = false; builder.Append(line.Text); builder.Append(line.LineEnding); continue; } // 在头部区域内匹配刀径定义行 if (inHeader) { var match = ToolLinePattern.Match(trimmed); if (match.Success && double.TryParse(match.Groups["diameter"].Value, NumberStyles.Float, CultureInfo.InvariantCulture, out double diameter)) { // 按直径数值匹配参数(允许浮点误差) string? paramString = FindMatchingParameter(diameter, paramDict); if (paramString != null) { // 保留原始行尾空白 string textWithoutTrailing = line.Text.TrimEnd(); int trailingLength = line.Text.Length - textWithoutTrailing.Length; string trailing = trailingLength > 0 ? line.Text[^trailingLength..] : string.Empty; builder.Append(textWithoutTrailing); builder.Append(paramString); builder.Append(trailing); builder.Append(line.LineEnding); continue; } else { unmatchedDiameters.Add(diameter); } } } // 其他行原样保留 builder.Append(line.Text); builder.Append(line.LineEnding); } return builder.ToString(); } /// /// 按直径查找匹配的参数字符串,允许 0.001 的浮点误差。 /// 返回 null 表示未找到匹配。 /// private static string? FindMatchingParameter(double diameter, Dictionary paramDict) { // 精确匹配优先 if (paramDict.TryGetValue(diameter, out string? paramString)) return paramString; // 浮点容差匹配 foreach (var kvp in paramDict) { if (Math.Abs(kvp.Key - diameter) < DiameterTolerance) return kvp.Value; } return null; } } }