如何让Java Normalizer处理通过StringBuilder/数组拼接的Unicode转义字符串?
问题:Java中拼接的Unicode转义字符串无法被Normalizer处理的解决方法
我需要用Java读取文件,扫描其中的Unicode转义序列(如\u00e0),将其转换为可读的英文文本后写入文件。但测试时发现:直接定义的Unicode字符串(String works = "\u00e0";)能被Normalizer正常归一化,而通过数组或StringBuilder拼接生成的形似\u00e0的字符串,却无法被归一化,输出仍是原字面量字符串。
测试代码
import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileReader; import java.io.FileWriter; import java.text.Normalizer; import java.util.regex.Pattern; public class Main { public static void main(String[] args) { String broke = ""; String[] testArr = {"\\","u","0","0","e","0"}; for(int i = 0; i < 6; i++) { broke+=testArr[i]; } String works = "\u00e0"; System.out.println("broke: " + broke); System.out.println("works: " + works); String temp = Normalizer.normalize(works, Normalizer.Form.NFD); System.out.println("temp:" + temp); Pattern pattern = Pattern.compile("\\p{InCombiningDiacriticalMarks}+"); String fixedUnicode = pattern.matcher(temp).replaceAll(""); System.out.println("fixedUnicode: " + fixedUnicode); } }
注:Java会自动转换直接定义的
"\u00e0"为对应Unicode字符,但拼接生成的同形式字符串输出时仍是字面量"\u00e0"。
核心原因
Java在编译阶段会自动解析直接定义的Unicode转义序列(如"\u00e0")为对应的字符,但拼接生成的"\u00e0"只是普通的6个字符组成的字符串(\、u、0、0、e、0),并非实际的Unicode字符,所以Normalizer无法识别处理。
解决方法:手动解析Unicode转义序列
编写工具方法,将形似\uXXXX的字符串转换为对应的Unicode字符,之后再使用Normalizer处理。
实现代码示例
import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileReader; import java.io.FileWriter; import java.text.Normalizer; import java.util.regex.Matcher; import java.util.regex.Pattern; public class Main { public static void main(String[] args) { String broke = ""; String[] testArr = {"\\","u","0","0","e","0"}; for(int i = 0; i < 6; i++) { broke+=testArr[i]; } // 解析拼接的Unicode转义字符串 String parsedBroke = parseUnicodeEscape(broke); String works = "\u00e0"; System.out.println("broke: " + broke); System.out.println("parsedBroke: " + parsedBroke); System.out.println("works: " + works); // 处理解析后的字符串 String temp1 = Normalizer.normalize(parsedBroke, Normalizer.Form.NFD); Pattern pattern = Pattern.compile("\\p{InCombiningDiacriticalMarks}+"); String fixedUnicode1 = pattern.matcher(temp1).replaceAll(""); System.out.println("fixed parsedBroke: " + fixedUnicode1); String temp2 = Normalizer.normalize(works, Normalizer.Form.NFD); String fixedUnicode2 = pattern.matcher(temp2).replaceAll(""); System.out.println("fixed works: " + fixedUnicode2); } // 解析Unicode转义序列的工具方法 private static String parseUnicodeEscape(String input) { // 匹配\uXXXX格式的转义序列 Pattern unicodePattern = Pattern.compile("\\\\u([0-9a-fA-F]{4})"); Matcher matcher = unicodePattern.matcher(input); StringBuffer sb = new StringBuffer(); while (matcher.find()) { // 将十六进制字符串转换为对应的字符 String hex = matcher.group(1); char unicodeChar = (char) Integer.parseInt(hex, 16); matcher.appendReplacement(sb, String.valueOf(unicodeChar)); } matcher.appendTail(sb); return sb.toString(); } }
代码说明
parseUnicodeEscape方法通过正则匹配\uXXXX格式的字符串,将每组十六进制数字转换为对应的Unicode字符。- 解析后的字符串与直接定义的
"\u00e0"一致,是实际的Unicode字符,能被Normalizer正常处理。
扩展:处理文件中的Unicode转义序列
如果要处理文件内容,可在读取每行时先解析转义序列,再进行归一化和写入:
// 读取文件并处理示例 try (BufferedReader reader = new BufferedReader(new FileReader("input.txt")); BufferedWriter writer = new BufferedWriter(new FileWriter("output.txt"))) { String line; Pattern diacriticPattern = Pattern.compile("\\p{InCombiningDiacriticalMarks}+"); while ((line = reader.readLine()) != null) { // 解析行中的Unicode转义序列 String parsedLine = parseUnicodeEscape(line); // 归一化并去除变音符号 String normalized = Normalizer.normalize(parsedLine, Normalizer.Form.NFD); String processedLine = diacriticPattern.matcher(normalized).replaceAll(""); // 写入处理后的行 writer.write(processedLine); writer.newLine(); } } catch (Exception e) { e.printStackTrace(); }
内容的提问来源于stack exchange,提问作者MrDudee
相关产品推荐
相关产品推荐

