From bc512fd02d459fd486a14015c9b727623435fdc2 Mon Sep 17 00:00:00 2001 From: Luke Date: Wed, 27 Aug 2025 10:41:19 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=AD=97=E7=AC=A6?= =?UTF-8?q?=E4=B8=B2=E8=BD=AC=E4=B9=89/=E5=8F=8D=E8=BD=AC=E4=B9=89?= =?UTF-8?q?=E5=B7=A5=E5=85=B7=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 StringEscape 类,提供编译期和运行期的字符串转义/反转义功能 - 支持常见控制字符、特殊字符和 Unicode 字符的转义/反转义 - 适用于 .water指令文件的保存和虚拟机执行时的字符串还原 --- .../org/jcnc/snow/common/StringEscape.java | 120 ++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 src/main/java/org/jcnc/snow/common/StringEscape.java diff --git a/src/main/java/org/jcnc/snow/common/StringEscape.java b/src/main/java/org/jcnc/snow/common/StringEscape.java new file mode 100644 index 0000000..e27c107 --- /dev/null +++ b/src/main/java/org/jcnc/snow/common/StringEscape.java @@ -0,0 +1,120 @@ +package org.jcnc.snow.common; + +/** + *

+ * 字符串转义/反转义工具类,主要用于: + *

+ *
+ * 转义规则兼容 Java 字符串转义(包括 \n, \t, \r 等常见控制字符),同时对于不可见或非 ASCII 字符,会编码为 Unicode 形式(如 uXXXX)。 + *

+ */ +public final class StringEscape { + + /** + * 工具类私有构造方法,禁止实例化。 + */ + private StringEscape() { + } + + /** + * 编译期方法: + *

将普通字符串编码为可安全存储于单行文本(如 .water 指令)的转义字符串。

+ * + * + * + * @param input 原始字符串(可能包含特殊/不可见字符) + * @return 转义后的字符串,可安全作为单行文本保存 + */ + public static String escape(String input) { + StringBuilder sb = new StringBuilder(); + for (char ch : input.toCharArray()) { + switch (ch) { + case '\n' -> sb.append("\\n"); // 换行 + case '\t' -> sb.append("\\t"); // 制表符 + case '\r' -> sb.append("\\r"); // 回车 + case '\b' -> sb.append("\\b"); // 退格 + case '\f' -> sb.append("\\f"); // 换页 + case '\\' -> sb.append("\\\\"); // 反斜杠 + case '"' -> sb.append("\\\""); // 双引号 + case '\'' -> sb.append("\\'"); // 单引号 + default -> { + // 对于不可见的 ASCII 字符(<0x20)或非 ASCII 可见字符(>0x7E),转为 Unicode + if (ch < 0x20 || ch > 0x7E) { + sb.append(String.format("\\u%04X", (int) ch)); + } else { + sb.append(ch); // 其他字符直接附加 + } + } + } + } + return sb.toString(); + } + + /** + * 运行期方法: + *

将转义序列(如 \n, \u4E16 等)还原为实际字符。

+ * + * + * + * @param src 含有转义序列的字符串 + * @return 反转义后的字符串,原样还原 + */ + public static String unescape(String src) { + StringBuilder out = new StringBuilder(); + for (int i = 0; i < src.length(); i++) { + char c = src.charAt(i); + if (c != '\\') { // 非转义字符,直接输出 + out.append(c); + continue; + } + + // 如果是最后一个字符为反斜杠,则原样输出 + if (i == src.length() - 1) { + out.append('\\'); + break; + } + + char n = src.charAt(++i); // 下一个字符 + switch (n) { + case 'n' -> out.append('\n'); // 换行 + case 't' -> out.append('\t'); // 制表符 + case 'r' -> out.append('\r'); // 回车 + case 'b' -> out.append('\b'); // 退格 + case 'f' -> out.append('\f'); // 换页 + case '\\' -> out.append('\\'); // 反斜杠 + case '"' -> out.append('"'); // 双引号 + case '\'' -> out.append('\''); // 单引号 + case 'u' -> { + // Unicode 转义,需读取接下来的 4 位十六进制数字 + if (i + 4 <= src.length() - 1) { + String hex = src.substring(i + 1, i + 5); + try { + out.append((char) Integer.parseInt(hex, 16)); + i += 4; + } catch (NumberFormatException ignore) { + // 非法 hex,原样输出 + out.append("\\u").append(hex); + i += 4; + } + } else { + // 字符串末尾长度不足,原样输出 + out.append("\\u"); + } + } + default -> out.append(n); // 其他未定义的转义序列,原样输出 + } + } + return out.toString(); + } +}