| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255 |
- using UnityEngine;
- using UnityEditor;
- using System.IO;
- using System.Text.RegularExpressions;
- using System.Collections.Generic;
- public class ChineseCodeCollectorUltimate : EditorWindow
- {
- private string searchPath = "Assets/Scripts";
- private int maxLinesPerFile = 20;
- private Vector2 scrollPos;
- private List<ChineseCodeInfo> results = new List<ChineseCodeInfo>();
- [MenuItem("Tools/收集中文代码")]
- static void Open() => GetWindow<ChineseCodeCollectorUltimate>("中文代码精准收集器");
- void OnGUI()
- {
- GUILayout.Label("中文硬编码精准收集器", EditorStyles.boldLabel);
- GUILayout.Label("只排除含有 LogTool./Debug. 的日志行,其他中文照收", EditorStyles.miniLabel);
- GUILayout.Space(10);
- GUILayout.BeginHorizontal();
- GUILayout.Label("搜索目录:", GUILayout.Width(90));
- searchPath = EditorGUILayout.TextField(searchPath);
- if (GUILayout.Button("浏览", GUILayout.Width(60)))
- {
- string p = EditorUtility.OpenFolderPanel("选择目录", searchPath, "");
- if (!string.IsNullOrEmpty(p) && p.StartsWith(Application.dataPath))
- searchPath = "Assets" + p.Substring(Application.dataPath.Length);
- }
- GUILayout.EndHorizontal();
- GUILayout.BeginHorizontal();
- GUILayout.Label("每文件最多收集:", GUILayout.Width(110));
- maxLinesPerFile = EditorGUILayout.IntField(maxLinesPerFile, GUILayout.Width(60));
- maxLinesPerFile = Mathf.Max(1, maxLinesPerFile);
- GUILayout.Label("行");
- GUILayout.EndHorizontal();
- GUILayout.Space(10);
- if (GUILayout.Button("开始精准扫描", GUILayout.Height(40)))
- ScanWithPrecision();
- if (results.Count > 0)
- {
- GUILayout.Label($"发现 {results.Count} 个文件含有非日志中文", EditorStyles.boldLabel);
- scrollPos = EditorGUILayout.BeginScrollView(scrollPos);
- foreach (var info in results)
- DrawResult(info);
- EditorGUILayout.EndScrollView();
- if (GUILayout.Button("导出报告(TXT)", GUILayout.Height(35)))
- ExportReport();
- }
- }
- void ScanWithPrecision()
- {
- results.Clear();
- if (!Directory.Exists(searchPath))
- {
- EditorUtility.DisplayDialog("错误", "目录不存在:" + searchPath, "OK");
- return;
- }
- var files = Directory.GetFiles(searchPath, "*.cs", SearchOption.AllDirectories);
- foreach (var file in files)
- {
- // 只排除 Editor 文件夹下的脚本
- if (file.Contains("/Editor/") || file.Contains("\\Editor\\"))
- continue;
- var info = AnalyzeFilePrecisely(file);
- if (info != null && info.lines.Count > 0)
- results.Add(info);
- }
- Debug.Log($"精准扫描完成!共 {results.Count} 个文件含有非日志中文");
- }
- ChineseCodeInfo AnalyzeFilePrecisely(string path)
- {
- string[] lines = File.ReadAllLines(path);
- string className = ExtractClassName(lines);
- var chineseLines = new List<ChineseLine>();
- bool inRegion = false;
- bool inMultiLineComment = false;
- for (int i = 0; i < lines.Length; i++)
- {
- string raw = lines[i];
- string trimmed = raw.Trim();
- // 空行跳过
- if (string.IsNullOrWhiteSpace(trimmed)) continue;
- // #region 区域完全跳过
- if (trimmed.StartsWith("#region") || trimmed.Contains(" #region "))
- inRegion = true;
- if (trimmed.StartsWith("#endregion") || trimmed.Contains(" #endregion"))
- {
- inRegion = false;
- continue;
- }
- if (inRegion) continue;
- // 多行注释处理
- if (inMultiLineComment)
- {
- if (trimmed.Contains("*/")) inMultiLineComment = false;
- continue;
- }
- if (trimmed.Contains("/*") && !trimmed.Contains("*/"))
- {
- inMultiLineComment = true;
- continue;
- }
- // 单行注释跳过
- if (trimmed.StartsWith("//")) continue;
- // 这行如果包含日志调用,直接跳过这行
- if (IsLogLine(trimmed))
- continue;
- // 是否包含中文
- if (!ContainsChinese(raw))
- continue;
- chineseLines.Add(new ChineseLine
- {
- lineNumber = i + 1,
- code = raw.TrimEnd()
- });
- if (chineseLines.Count >= maxLinesPerFile)
- break;
- }
- if (chineseLines.Count > 0)
- {
- return new ChineseCodeInfo
- {
- filePath = path.Replace(Application.dataPath, "Assets"),
- className = className ?? Path.GetFileNameWithoutExtension(path),
- lines = chineseLines
- };
- }
- return null;
- }
- // 判断是否为日志行
- bool IsLogLine(string line)
- {
- // 常见日志调用模式(不区分大小写)
- string lower = line.ToLower();
- return lower.Contains("logtool.") ||
- lower.Contains("debug.log") ||
- lower.Contains("debug.warn") ||
- lower.Contains("debug.error") ||
- lower.Contains("debug.assert") ||
- lower.Contains("debug.draw") ||
- lower.Contains("debug.break") ||
- lower.Contains("logger.") ||
- lower.Contains("log.") && lower.Contains("info") ||
- lower.Contains("log.") && lower.Contains("warning") ||
- lower.Contains("log.") && lower.Contains("error");
- }
- bool ContainsChinese(string text)
- {
- return Regex.IsMatch(text, @"[\u4e00-\u9fff]");
- }
- string ExtractClassName(string[] lines)
- {
- foreach (string line in lines)
- {
- var m = Regex.Match(line, @"class\s+(\w+)");
- if (m.Success) return m.Groups[1].Value;
- }
- return null;
- }
- void DrawResult(ChineseCodeInfo info)
- {
- EditorGUILayout.BeginVertical("box");
- EditorGUILayout.LabelField($"{Path.GetFileName(info.filePath)}", EditorStyles.boldLabel);
- EditorGUILayout.LabelField($"类名:{info.className}", EditorStyles.miniLabel);
- EditorGUILayout.SelectableLabel(info.filePath, EditorStyles.miniLabel, GUILayout.Height(16));
- foreach (var cl in info.lines)
- {
- EditorGUILayout.BeginHorizontal();
- GUILayout.Label(cl.lineNumber.ToString("D4"), GUILayout.Width(50));
- GUILayout.Label(">", GUILayout.Width(15));
- EditorGUILayout.SelectableLabel(cl.code, GUILayout.Height(18));
- EditorGUILayout.EndHorizontal();
- }
- EditorGUILayout.EndVertical();
- GUILayout.Space(6);
- }
- void ExportReport()
- {
- string path = EditorUtility.SaveFilePanel("导出中文报告", "", "非日志中文硬编码报告.txt", "txt");
- if (string.IsNullOrEmpty(path)) return;
- using (var sw = new StreamWriter(path, false, System.Text.Encoding.UTF8))
- {
- sw.WriteLine("============================================");
- sw.WriteLine(" Unity 非日志中文硬编码精准检测报告");
- sw.WriteLine("============================================");
- sw.WriteLine($"生成时间:{System.DateTime.Now:yyyy-MM-dd HH:mm:ss}");
- sw.WriteLine($"搜索目录:{searchPath}");
- sw.WriteLine($"已排除:#region 区域、Editor 文件夹、所有日志行(LogTool./Debug./Log.等)");
- sw.WriteLine($"共发现 {results.Count} 个文件含有非日志中文");
- sw.WriteLine();
- foreach (var r in results)
- {
- sw.WriteLine($"文件:{r.filePath}");
- sw.WriteLine($"类名:{r.className}");
- sw.WriteLine("非日志中文行:");
- foreach (var l in r.lines)
- sw.WriteLine($" 第 {l.lineNumber,4} 行:{l.code}");
- sw.WriteLine(new string('-', 70));
- }
- }
- Debug.Log("精准中文报告已导出:" + path);
- EditorUtility.RevealInFinder(path);
- }
- }
- // 数据结构
- [System.Serializable]
- public class ChineseCodeInfo
- {
- public string filePath;
- public string className;
- public List<ChineseLine> lines;
- }
- [System.Serializable]
- public class ChineseLine
- {
- public int lineNumber;
- public string code;
- }
|