【问题标题】:Invalid characters while reading content of text file to String将文本文件的内容读取到字符串时出现无效字符
【发布时间】:2014-07-22 10:21:42
【问题描述】:

这是我的代码:

    StringBuffer fileData = new StringBuffer(1000);
    BufferedReader reader = new BufferedReader(new FileReader(file));
    char[] buf = new char[5000];
    int numRead=0;
    while((numRead=reader.read(buf)) != -1){
        String readData = String.valueOf(buf, 0, numRead);
        fileData.append(readData);
        buf = new char[1024];
    }
    reader.close();
    return fileData.toString();

当我在 Windows 中运行它时,一切都很好。

但是当我在 UNIX 中运行时,我看到了字符串的开头:

我»¿

可能是什么问题?

【问题讨论】:

  • 这是一个编码问题。
  • 那么解决方案是什么?文本文件是我的文件。
  • 您必须知道使用什么字符编码来存储文本文件中的数据。您必须以相同的字符编码打开文件。
  • 什么意思?我正在记事本中创建文件,想用上面的代码在 unix 中打开它。
  • 嗯,windows记事本会以系统设置指定的编码保存。您必须检查它是哪种编码。

标签: java string file encoding


【解决方案1】:

给定字符序列的hexdump 可能是ef bb bf。我说可能,因为我不得不猜测你的显示编码。

如果正确,您正在尝试将 UTF-8 编码文件 with BOM prefix 读取为 ISO-8859-X。这与您在使用 vi/vim 打开文件时没有看到这些字符的事实相一致。大多数(如果不是全部)支持 UTF-8 的文本编辑器都知道如何处理 BOM。

从 Java 中,您必须手动跳过它(虽然不知道为什么它可以在 Windows 上运行)。 另一种选择是将您的文本文件保存为 UTF-8 不带 BOM。

这已经讨论过了。例如:



由于这不是很清楚,我做了以下实验:我创建了两个文件,utf-8 编码并包含字符串 "L'élève va à l 'école." 这两个测试文件之间的唯一区别是一个具有 BOM 前缀。

然后,根据 OP 给出的代码和 Thomas Mueller 的建议,我编写了一个非常简单的 Java 应用程序来使用各种编码读取这些文件。代码如下:

public class EncodingTest {
    public static String read(String file, String encoding) throws IOException {
        StringBuffer fileData = new StringBuffer(1000);

        /* Only difference with OP code */
        /* I use *explicit* encoding while reading the file */
        BufferedReader reader = new BufferedReader(
                new InputStreamReader(new FileInputStream(file), encoding)
                );

        char[] buf = new char[5000];
        int numRead=0;
        while((numRead=reader.read(buf)) != -1){
            String readData = String.valueOf(buf, 0, numRead);
            fileData.append(readData);
            buf = new char[1024];
        }
        reader.close();
        return fileData.toString();     
    }

    public static void main(String[] args) throws IOException {
        System.out.print(read("UTF-8-BOM-FILE", "UTF-8"));
        System.out.print(read("UTF-8-FILE", "UTF-8"));
        System.out.print(read("UTF-8-BOM-FILE", "ISO-8859-15"));
        System.out.print(read("UTF-8-FILE", "ISO-8859-15"));
    }
}

当我在控制台编码为 UTF8 的 Linux 系统上运行它时,我得到了以下结果:

$ java -cp bin EncodingTest
L'élève va à l'école.
L'élève va à l'école.
L'élÚve va à l'école.
L'élÚve va à l'école.

注意第三行如何以与 OP 给出的完全相同的顺序开始。那是在读取 utf8 编码文件,BOM 为 iso-8859-15

令人惊讶的是,前两行似乎是相同的,就像 Java 神奇地删除了 BOM 一样。我想这就是 Windows 上 OP 的附加内容。

但是,仔细检查表明:

$ java -cp bin EncodingTest | hexdump -C
00000000  ef bb bf 4c 27 c3 a9 6c  c3 a8 76 65 20 76 61 20  |...L'..l..ve va |
00000010  c3 a0 20 6c 27 c3 a9 63  6f 6c 65 2e 0a 4c 27 c3  |.. l'..cole..L'.|
00000020  a9 6c c3 a8 76 65 20 76  61 20 c3 a0 20 6c 27 c3  |.l..ve va .. l'.|
00000030  a9 63 6f 6c 65 2e 0a c3  af c2 bb c2 bf 4c 27 c3  |.cole........L'.|
00000040  83 c2 a9 6c c3 83 c5 a1  76 65 20 76 61 20 c3 83  |...l....ve va ..|
00000050  c2 a0 20 6c 27 c3 83 c2  a9 63 6f 6c 65 2e 0a 4c  |.. l'....cole..L|
00000060  27 c3 83 c2 a9 6c c3 83  c5 a1 76 65 20 76 61 20  |'....l....ve va |
00000070  c3 83 c2 a0 20 6c 27 c3  83 c2 a9 63 6f 6c 65 2e  |.... l'....cole.|
00000080  0a                                                |.|
00000081

请注意前三个字节:BOM 已发送到输出——但我的控制台以某种方式丢弃了它们。但是,从 Java 程序的角度来看,那些存在的字节——我可能应该手动处理它们。


那么,这一切的寓意是什么? OP 确实有两个 问题:一个 BOM 前缀的 UTF8 编码文件。该文件被读取为 iso-8859-X。

Yuris,为了解决这个问题,你必须在你的 Java 程序中明确使用正确的编码,并且要么丢弃前 3 个字节改变您的数据文件以删除 BOM。

【讨论】:

  • 嗯,如果是 BOM 问题(这是可能的),为什么它只出现在 Linux 中而不出现在 Windows 中?假设它是完全相同的文件...
  • @ThomasMueller 很容易确定这是否是 BOM 问题:OP 只需十六进制转储“有问题的”文本文件的前几个字节。关于 Windows,我承认这很奇怪。这里再次对 Windows 主机上的文件进行简单的十六进制转储可能会提供一些关于正在发生的事情的线索。
  • @ThomasMueller 更准确地说,我猜 OP 有 两个 问题。一个 BOM 前缀 UTF8 编码文件 尝试将其读取为 ISO-8859-X 编码。在 Windows 上,可能 OP 将其 BOM 前缀 UTF8 文件读取为 UTF8,因此前 3 个字节被转换为单个 unicode 字符 FEFF。也许那个角色被路上或展示时的某个电话默默地丢弃了?
  • 是的,这是有道理的。我将删除我的答案,因为您的答案同时解决了 BOM 和编码问题。
【解决方案2】:
byte[] content = Files.readAllBytes(file.toPath());
String s = new String(bytes, StandardCharsets.UTF_8);
s = s.replaceFirst("^\uFEFF", ""); // Remove starting BOM
return s;

BOM 是一个字节顺序标记,可选择用作第一个字符、零宽度空格,以将文件标记为 UTF-8(或 UTF-16LE、UTF-16BE)。

它的主要用途似乎是在 Windows 上让记事本不将文本与 ANSI 编码混淆。

FileReader 是一个不能设置编码的工具类。

可能是您的文件已经经历了错误的编码转换。也许一个 UTF-8 文本被粘贴到一个 ANSI 单字节编码文本中,或者其他什么。

【讨论】:

    猜你喜欢
    • 2014-07-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多