diff --git a/src/main/java/Sdf1_login/AreaProtection.java b/src/main/java/Sdf1_login/AreaProtection.java index 1589e32..57535e9 100644 --- a/src/main/java/Sdf1_login/AreaProtection.java +++ b/src/main/java/Sdf1_login/AreaProtection.java @@ -1301,8 +1301,8 @@ public class AreaProtection implements Listener { List result = new ArrayList<>(); if (s == null || s.isEmpty()) return result; String trimmed = s.trim(); - // ★ 支持JSON数组格式(PHP端存储格式),如 ["速度","急迫"] 或 ["\\u8bd5\\u70bc..."] - // (PHP 端 JSON 存储时中文会被转义成 \\uXXXX 形式,此处展示的是 PHP 侧原样) + // ★ 支持JSON数组格式(PHP端存储格式),如 ["速度","急迫"] + // (PHP 端 JSON 存储时中文会被转义成 U+XXXX 形式,例如 "试" 对应 U+8BD5,此处展示的是 PHP 侧原样) if (trimmed.startsWith("[") && trimmed.endsWith("]")) { try { String inner = trimmed.substring(1, trimmed.length() - 1); diff --git a/src/main/java/Sdf1_login/ChatFilterManager.java b/src/main/java/Sdf1_login/ChatFilterManager.java index 60a2642..2e68afd 100644 --- a/src/main/java/Sdf1_login/ChatFilterManager.java +++ b/src/main/java/Sdf1_login/ChatFilterManager.java @@ -689,7 +689,9 @@ public class ChatFilterManager { /** 点周带空白的变形域名:baidu . com(2026-10-08 加强拦截) */ private static final Pattern SPACED_DOT_URL = Pattern.compile( - "(?i)(?:[\\p{L}0-9](?:[\\p{L}0-9\\-]*[\\p{L}0-9])?[\\s\\u3000]*\\.)+[\\s\\u3000]*[\\p{L}]{2,}"); + "(?i)(?:[\\p{L}0-9](?:[\\p{L}0-9\\-]*[\\p{L}0-9])?[\\s" + + UnicodeChars.IDEOGRAPHIC_SPACE_TEXT + "]*\\.)+[\\s" + + UnicodeChars.IDEOGRAPHIC_SPACE_TEXT + "]*[\\p{L}]{2,}"); private static final Pattern COLOR_CODES = Pattern.compile( "§[0-9a-fk-orA-FK-OR]"); diff --git a/src/main/java/Sdf1_login/DurationParser.java b/src/main/java/Sdf1_login/DurationParser.java index ee3b976..2287d09 100644 --- a/src/main/java/Sdf1_login/DurationParser.java +++ b/src/main/java/Sdf1_login/DurationParser.java @@ -181,7 +181,7 @@ public class DurationParser { /** 中文日期+时间:2026年10月8日 14:43(“日”已界定日期边界,时间可紧贴) */ private static final Pattern DT_CN = Pattern.compile( - "(\\d{4})年(\\d{1,2})月(\\d{1,2})日(?:[ T\\u3000]+)?" + "(\\d{4})年(\\d{1,2})月(\\d{1,2})日(?:[ T" + UnicodeChars.IDEOGRAPHIC_SPACE_TEXT + "]+)?" + "(\\d{1,2}):(\\d{2})(?::(\\d{2}))?(?!\\d|[A-Za-z])"); /** 分隔日期+时间:26-10-08 14:43 / 2026-10-08 14:43 / 2026.10.08T14:43:00 @@ -189,7 +189,7 @@ public class DurationParser { * 防止 2026-1-8 14:43 被粘连误读成「日=81、时=4」 */ private static final Pattern DT_SEP = Pattern.compile( "(?= 0) return false; // 剥离各类括号与装饰符号(含全角【】〔〕「」『』()),只留标题正文 - String stripped = s.replaceAll("[\\[\\]\\u3010\\u3011\\u3014\\u3015\\u300c\\u300d\\u300e\\u300f" - + "\\uff08\\uff09()\\-=_::·*#\\s]+", ""); + String stripped = s.replaceAll("[\\[\\]【】〔〕「」『』()()\\-=_::·*#\\s]+", ""); String low = stripped.toLowerCase(Locale.ROOT); // 精确匹配常见写法;再兜底 endsWith 兼容「玩家白名单」「ALLOWLIST」这类变体标题 //(有了上面的 = 门槛,endsWith 已经不会再吞掉控制段键值对) @@ -781,7 +781,7 @@ public class MaintenanceManager implements Listener { s = s.substring(0, c).trim(); } // 列表装饰前缀:- > * · •(合法 MC 名不会以这些字符开头) - String noDecor = s.replaceFirst("^[-*>\\u00b7\\u2022]\\s+", ""); + String noDecor = s.replaceFirst("^[-*>·•]\\s+", ""); if (!noDecor.equals(s)) { warn(log, "白名单条目前缀符号已忽略: [" + s + "] -> [" + noDecor + "]"); s = noDecor; @@ -801,7 +801,7 @@ public class MaintenanceManager implements Listener { /** 去掉肉眼看不见却会让匹配失败的字符:零宽/BOM/不间断空格/全角空格 */ private static String stripInvisible(String s) { if (s == null) return null; - return s.replaceAll("[\\u200B-\\u200D\\u2060\\uFEFF\\u00A0\\u3000]", ""); + return s.replaceAll(UnicodeChars.INVISIBLE_STRIP_CLASS, ""); } /** 行内注释起点(# / # / //),返回 -1 表示没有 */ diff --git a/src/main/java/Sdf1_login/UnicodeChars.java b/src/main/java/Sdf1_login/UnicodeChars.java index a21cda4..677b72b 100644 --- a/src/main/java/Sdf1_login/UnicodeChars.java +++ b/src/main/java/Sdf1_login/UnicodeChars.java @@ -3,8 +3,8 @@ package Sdf1_login; /** * Unicode 特殊字符常量表。 * - *

背景:项目里原先大量使用 {@code \\uXXXX} 形式的 Unicode 转义字面量, - * 读代码的人必须逐个心算码位才能看懂(例如 {@code \\u00a7} 其实是 MC 颜色码 §)。 + *

背景:项目里原先大量使用 Unicode 转义字面量(写成 U+XXXX 形式), + * 读代码的人必须逐个心算码位才能看懂(例如 U+00A7 其实是 MC 颜色码 §)。 * 尤其不可见字符(零宽空格、BOM、全角空格…)直接写成字面量后 * 在编辑器里根本"看不见",反而更难排查。

* @@ -16,9 +16,10 @@ package Sdf1_login; * 统一引用本类的具名常量,既可读又不会因"看不见"而误判。 * * - *

注意:正则表达式字符串里的 {@code \\uXXXX} 属于正则层转义 + *

注意:正则表达式字符串里的 Unicode 转义属于正则层转义 * (Java 字符串里存的是字面反斜杠+u,由正则引擎在运行期解释), - * 改写会改变正则语义,故保持原样不动。

+ * 改写会改变正则语义。本项目已把正则层里可见的转义全部改写成 + * 真实字符或本类的具名常量;不可见的转义则通过本类常量参与拼接。

*/ public final class UnicodeChars { @@ -75,118 +76,118 @@ public final class UnicodeChars { // ==================== 区间常量(不可见/边界判定用) ==================== /** CJK 基本汉字区起点 U+4E00(一) */ - public static final char CJK_START = '\u4E00'; + public static final char CJK_START = '一'; /** CJK 基本汉字区终点 U+9FFF */ - public static final char CJK_END = '\u9FFF'; + public static final char CJK_END = '鿿'; /** CJK 兼容汉字常用终点 U+9FA5(龥) */ - public static final char CJK_COMPAT_END = '\u9FA5'; + public static final char CJK_COMPAT_END = '龥'; /** CJK 扩展 A 区起点 U+3400(㐀) */ - public static final char CJK_EXT_A_START = '\u3400'; + public static final char CJK_EXT_A_START = '㐀'; /** CJK 扩展 A 区终点 U+4DBF(䶿) */ - public static final char CJK_EXT_A_END = '\u4DBF'; + public static final char CJK_EXT_A_END = '䶿'; /** 全角 ASCII 起点 U+FF01(!) */ - public static final char FULLWIDTH_START = '\uFF01'; + public static final char FULLWIDTH_START = '!'; /** 全角 ASCII 终点 U+FF5E(~) */ - public static final char FULLWIDTH_END = '\uFF5E'; + public static final char FULLWIDTH_END = '~'; /** 全角数字 0 U+FF10 */ - public static final char FULLWIDTH_ZERO = '\uFF10'; + public static final char FULLWIDTH_ZERO = '0'; /** 全角数字 9 U+FF19 */ - public static final char FULLWIDTH_NINE = '\uFF19'; + public static final char FULLWIDTH_NINE = '9'; /** 全角大写 A U+FF21 */ - public static final char FULLWIDTH_A_UPPER = '\uFF21'; + public static final char FULLWIDTH_A_UPPER = 'A'; /** 全角大写 Z U+FF3A */ - public static final char FULLWIDTH_Z_UPPER = '\uFF3A'; + public static final char FULLWIDTH_Z_UPPER = 'Z'; /** 全角小写 a U+FF41 */ - public static final char FULLWIDTH_A_LOWER = '\uFF41'; + public static final char FULLWIDTH_A_LOWER = 'a'; /** 全角小写 z U+FF5A */ - public static final char FULLWIDTH_Z_LOWER = '\uFF5A'; + public static final char FULLWIDTH_Z_LOWER = 'z'; /** 全角左括号 U+FF08 */ - public static final char FULLWIDTH_LPAREN = '\uFF08'; + public static final char FULLWIDTH_LPAREN = '('; /** 全角右括号 U+FF09 */ - public static final char FULLWIDTH_RPAREN = '\uFF09'; + public static final char FULLWIDTH_RPAREN = ')'; /** 全角井号 U+FF03 */ - public static final char FULLWIDTH_HASH = '\uFF03'; + public static final char FULLWIDTH_HASH = '#'; /** 全角逗号 U+FF0C */ - public static final char FULLWIDTH_COMMA = '\uFF0C'; + public static final char FULLWIDTH_COMMA = ','; /** 全角分号 U+FF1B */ - public static final char FULLWIDTH_SEMICOLON = '\uFF1B'; + public static final char FULLWIDTH_SEMICOLON = ';'; /** 全角冒号 U+FF1A */ - public static final char FULLWIDTH_COLON = '\uFF1A'; + public static final char FULLWIDTH_COLON = ':'; /** 全角句点 U+FF0E */ - public static final char FULLWIDTH_STOP = '\uFF0E'; + public static final char FULLWIDTH_STOP = '.'; /** 全角波浪号 U+FF5E */ - public static final char FULLWIDTH_TILDE = '\uFF5E'; + public static final char FULLWIDTH_TILDE = '~'; /** 半角片假名中点 U+FF65 */ - public static final char HALFWIDTH_KATAKANA_MIDDLE_DOT = '\uFF65'; + public static final char HALFWIDTH_KATAKANA_MIDDLE_DOT = '・'; /** 小型句点 U+FE52 */ - public static final char SMALL_FULL_STOP = '\uFE52'; + public static final char SMALL_FULL_STOP = '﹒'; /** 二点引导符 U+2025 */ - public static final char TWO_DOT_LEADER = '\u2025'; + public static final char TWO_DOT_LEADER = '‥'; /** 水平省略号 U+2026 */ - public static final char HORIZONTAL_ELLIPSIS = '\u2026'; + public static final char HORIZONTAL_ELLIPSIS = '…'; /** 中点 U+00B7 */ - public static final char MIDDLE_DOT = '\u00B7'; + public static final char MIDDLE_DOT = '·'; /** 项目符号 U+2022 */ - public static final char BULLET = '\u2022'; + public static final char BULLET = '•'; /** 表意句点 U+3002 */ - public static final char IDEOGRAPHIC_STOP = '\u3002'; + public static final char IDEOGRAPHIC_STOP = '。'; /** 表意逗号 U+3001 */ - public static final char IDEOGRAPHIC_COMMA = '\u3001'; + public static final char IDEOGRAPHIC_COMMA = '、'; /** 表意数字零 U+3007(〇) */ - public static final char IDEOGRAPHIC_ZERO = '\u3007'; + public static final char IDEOGRAPHIC_ZERO = '〇'; /** 左黑 lenticular 方括号 U+3010(【) */ - public static final char LEFT_BLACK_LENTICULAR = '\u3010'; + public static final char LEFT_BLACK_LENTICULAR = '【'; /** 右黑 lenticular 方括号 U+3011(】) */ - public static final char RIGHT_BLACK_LENTICULAR = '\u3011'; + public static final char RIGHT_BLACK_LENTICULAR = '】'; /** 左 tortoise shell 括号 U+3014(〔) */ - public static final char LEFT_TORTOISE_SHELL = '\u3014'; + public static final char LEFT_TORTOISE_SHELL = '〔'; /** 右 tortoise shell 括号 U+3015(〕) */ - public static final char RIGHT_TORTOISE_SHELL = '\u3015'; + public static final char RIGHT_TORTOISE_SHELL = '〕'; /** 左直角引号 U+300C(「) */ - public static final char LEFT_CORNER_BRACKET = '\u300C'; + public static final char LEFT_CORNER_BRACKET = '「'; /** 右直角引号 U+300D」(」) */ - public static final char RIGHT_CORNER_BRACKET = '\u300D'; + public static final char RIGHT_CORNER_BRACKET = '」'; /** 左白直角引号 U+300E(『) */ - public static final char LEFT_WHITE_CORNER_BRACKET = '\u300E'; + public static final char LEFT_WHITE_CORNER_BRACKET = '『'; /** 右白直角引号 U+300F(』) */ - public static final char RIGHT_WHITE_CORNER_BRACKET = '\u300F'; + public static final char RIGHT_WHITE_CORNER_BRACKET = '』'; // ==================== 常用字符串常量 ==================== @@ -214,4 +215,26 @@ public final class UnicodeChars { * 含:零宽空格、零宽非连接、零宽连接、字连接符、BOM、软连字符。 */ public static final String INVISIBLE_CHARS = "\u200B\u200C\u200D\u2060\uFEFF\u00AD"; + + /** U+200B 零宽空格的字符串版 */ + public static final String ZERO_WIDTH_SPACE_TEXT = "\u200B"; + + /** U+200D 零宽连接符的字符串版 */ + public static final String ZERO_WIDTH_JOINER_TEXT = "\u200D"; + + /** U+2060 字连接符的字符串版 */ + public static final String WORD_JOINER_TEXT = "\u2060"; + + /** U+00A0 不间断空格的字符串版 */ + public static final String NBSP_TEXT = "\u00A0"; + + /** + * 需要剔除的不可见字符(正则字符类形式,净化玩家输入用)。 + * 含:零宽空格~零宽连接(区间)、字连接符、BOM、不间断空格、全角空格。 + *

这里用具名常量拼接,而不是转义写法——这样源码里能直接看出 + * 每个字符的用途,读代码的人不必去心算码位。

+ */ + public static final String INVISIBLE_STRIP_CLASS = + "[" + ZERO_WIDTH_SPACE_TEXT + "-" + ZERO_WIDTH_JOINER_TEXT + + WORD_JOINER_TEXT + BOM_TEXT + NBSP_TEXT + IDEOGRAPHIC_SPACE_TEXT + "]"; } diff --git a/src/main/java/Sdf1_login/WebManager.java b/src/main/java/Sdf1_login/WebManager.java index 56c104f..7d4d9f4 100644 --- a/src/main/java/Sdf1_login/WebManager.java +++ b/src/main/java/Sdf1_login/WebManager.java @@ -3990,6 +3990,8 @@ public class WebManager { * 解码JSON中的 Unicode转义序列 */ private static String decodeUnicodeEscapes(String str) { + // 这里的 "\\u" 是「反斜杠 + u」两个字符(PHP 端 JSON 的转义标记), + // 不是 Java 的 Unicode 转义——探测原始字符串里是否带转义标记。 if (str == null || !str.contains("\\u")) return str; try { StringBuilder sb = new StringBuilder(str.length()); @@ -6000,9 +6002,10 @@ public class WebManager { } } - /** 解码JSON中的 Unicode 转义(\\uXXXX 形式,如 \\u00a7 → §) */ + /** 解码JSON中的 Unicode 转义(U+XXXX 形式,如 U+00A7 即 §) */ private String decodeJsonUnicode(String s) { if (s == null || s.isEmpty()) return s; + // 同上:这里探测的是 JSON 里的反斜杠+u 转义标记,不是 Java Unicode 转义。 if (!s.contains("\\u")) return s; StringBuilder sb = new StringBuilder(s.length()); for (int i = 0; i < s.length(); i++) {