Java中UTF-8字符串的打印、去重排序及文件保存问题
问题解决与实现方案
一、Eclipse控制台正确打印特殊字符的方法
Windows下Eclipse默认控制台编码为GBK,会导致UTF-8特殊字符输出时乱码成问号,按以下步骤调整:
- 打开Run Configurations,找到你的Java程序,切换到Common标签页,在Encoding区域选择UTF-8(不要用默认选项)。
- 若仍有问题,在VM参数中添加:
-Dfile.encoding=UTF-8,强制JVM输出采用UTF-8编码。 - 确认控制台字体支持目标字符:检查字体设置(Window > Preferences > General > Appearance > Colors and Fonts > Debug > Console Font),确保GNU FreeMono或其他选中字体包含所需Unicode字符集。
二、charAt() + HashSet去重的安全性问题
不安全,会出现多代码单元字符被拆分的情况。
Java中的char是UTF-16编码的16位代码单元,而Unicode中部分字符(如增补平面的字符)需要两个char组成代理对(高代理+低代理)。charAt()会将这两个代码单元拆分为独立的char,存入HashSet时会被当成两个单独的"字符",导致原字符被破坏,去重逻辑出错。
三、安全的字符比较与处理方式
必须基于**Unicode代码点(code point)**处理,每个代码点对应一个完整的Unicode字符:
- 获取字符串所有代码点:调用
str.codePoints(),返回IntStream,每个int代表一个代码点。 - 比较字符时,直接比较代码点的整数值,或使用
Character.compare(codePoint1, codePoint2)。 - 将代码点转换回字符串:使用
String.valueOf(Character.toChars(codePoint)),或通过IntStream.collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append).toString()批量转换。
四、多语言场景下安全保存字符串到文本文件
绝对不能使用默认编码的输出流(如FileWriter,Windows下默认编码为GBK),必须明确指定UTF-8编码:
方法1:使用OutputStreamWriter
try (OutputStreamWriter writer = new OutputStreamWriter( new FileOutputStream("output.txt"), StandardCharsets.UTF_8)) { writer.write(resultStr); } catch (IOException e) { e.printStackTrace(); }
方法2:使用Files类(Java 7+)
try { // Java 11+ 可使用更简洁的写法 Files.writeString(Paths.get("output.txt"), resultStr, StandardCharsets.UTF_8); // Java 7-10 写法 // Files.write(Paths.get("output.txt"), resultStr.getBytes(StandardCharsets.UTF_8)); } catch (IOException e) { e.printStackTrace(); }
保存后用支持UTF-8的文本编辑器(如Notepad++、VS Code)打开,确保编辑器编码设置为UTF-8。
五、完整实现:去重+按Unicode排序+打印+保存
以下是满足需求的完整代码,基于代码点处理避免代理对问题:
import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; import java.util.TreeSet; public class UnicodeStringProcessor { public static void main(String[] args) { String originalStr = "áçñéüöëéóíóíóíóíííããéíáéíáççãÓłńńāņšøøøøééèèÜüÜüééíéáéáříříççááññïïššäääééèèááéáéáéáéáéáéáèèèèííéèéèáééÇÇééééííüüüüííøøáááá¿¿ííóé̌Íá̌íáææööíÁíÁíííłççññá璇üşİüşİöğöğşşııããßßôèôèêééççáÁáÁééééééÇóóéíêööééííððññáñáñÓúÓúíłńłńååéééëëááéí¿¿ééÖÖáéáéöğÖüöğÖüçŞçŞııçııçııİİşİşíáíáéüüÉÉéééøññïíéé"; // 去重+按Unicode代码点排序:TreeSet自动按代码点数值排序 TreeSet<Integer> uniqueCodePoints = new TreeSet<>(); originalStr.codePoints().forEach(uniqueCodePoints::add); // 将代码点转换为字符串 StringBuilder resultBuilder = new StringBuilder(); uniqueCodePoints.forEach(resultBuilder::appendCodePoint); String resultStr = resultBuilder.toString(); // 打印结果(需确保控制台编码已设为UTF-8) System.out.println("处理后的字符串:"); System.out.println(resultStr); // 保存为UTF-8编码的文本文件 try { Files.writeString(Paths.get("unicode_result.txt"), resultStr, StandardCharsets.UTF_8); System.out.println("\n文件已成功保存为unicode_result.txt(UTF-8编码)"); } catch (IOException e) { System.err.println("保存文件失败:" + e.getMessage()); } } }
说明:
TreeSet会自动按代码点的整数值排序,即遵循Unicode编码顺序。- 用
codePoints()遍历所有完整字符,避免代理对拆分问题。 - 保存时明确指定
StandardCharsets.UTF_8,确保多语言字符正确存储。
内容的提问来源于stack exchange,提问作者user2526586
相关产品推荐
相关产品推荐

