Java读取含非UTF-8字符文件名失败,如何正确获取并执行stat操作?
解决ISO-8859-1编码文件名在Java NIO中显示为?的问题
问题原因
Java NIO的Path对象默认使用系统默认字符编码解码文件名。如果你的文件名是ISO-8859-1(Latin-1)编码,而系统默认编码是UTF-8,Java会将无法用UTF-8解码的字节替换为�(显示为?),且解码后的字符串丢失了原始字节信息,无法直接还原。
解决方案
1. 直接执行Stat操作(无需修正文件名)
注意:即使Path.toString()显示的文件名包含?,Path对象本身依然指向正确的文件,因此执行stat等文件操作不受影响。示例代码:
Path entry = iterator.next(); // 直接执行stat操作,无需关心toString()的显示 BasicFileAttributes attrs = Files.readAttributes(entry, BasicFileAttributes.class); System.out.println("文件大小: " + attrs.size());
2. 获取正确的ISO-8859-1编码文件名(Unix/Linux/macOS)
通过Unix文件系统属性视图获取文件名的原始字节数组,再用ISO-8859-1解码,得到正确的文件名字符串:
import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.attribute.BasicFileAttributes; // 遍历目录时替换原有的toString()逻辑 Path entry = iterator.next(); // 获取原始文件名字节数组 byte[] fileNameBytes = (byte[]) Files.getAttribute(entry, "unix:name"); // 用ISO-8859-1解码得到正确文件名 String correctFileName = new String(fileNameBytes, StandardCharsets.ISO_8859_1); System.out.println("正确文件名: " + correctFileName); // 同时执行stat操作 BasicFileAttributes attrs = Files.readAttributes(entry, BasicFileAttributes.class); System.out.println("文件修改时间: " + attrs.lastModifiedTime());
3. 跨平台兼容方案(Windows/Unix通用)
通过调用系统命令读取目录原始输出,再用ISO-8859-1解码(需根据系统调整命令):
import java.io.BufferedReader; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.attribute.BasicFileAttributes; public static void listFilesWithCorrectEncoding(String dir) throws Exception { // Unix用"ls -1",Windows用"cmd /c dir /b" ProcessBuilder pb = System.getProperty("os.name").toLowerCase().contains("win") ? new ProcessBuilder("cmd", "/c", "dir", "/b", dir) : new ProcessBuilder("ls", "-1", dir); pb.redirectErrorStream(true); Process process = pb.start(); try (BufferedReader reader = new BufferedReader( new InputStreamReader(process.getInputStream(), StandardCharsets.ISO_8859_1))) { String line; while ((line = reader.readLine()) != null) { System.out.println("正确文件名: " + line); // 根据文件名构建Path并执行stat操作 Path path = Paths.get(dir, line); BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class); System.out.println("文件大小: " + attrs.size()); } } }
关键说明
- Java的
Path对象内部通过操作系统的文件标识(而非解码后的字符串)定位文件,因此即使显示的文件名有?,文件操作依然有效。 - Unix属性视图方案依赖JDK的UnixFileSystemProvider,仅在Unix-like系统可用;Windows环境建议使用系统命令方案。
内容的提问来源于stack exchange,提问作者Rk1659
相关产品推荐
相关产品推荐

