为什么 FileInputStream read() 方法在进入无限循环时会错误地读取问号 (ascii: 63)?

Why is FileInputStream read() method wrongly reading question mark (ascii: 63) when put into infinite loop?

提问人:Navin Israni 提问时间:5/25/2016 更新时间:11/17/2023 访问量:1363

问:

网站上有一些类似的问题,但都用于不同的场景。所以,我在这里问它:

package Assign6B;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class FileOpsDemo {
    public static void main(String[] args) throws IOException 
    {

        FileInputStream inputFile = null;
        FileOutputStream outputFile = null;

        try
        {
            inputFile = new FileInputStream("s:/inputFile.txt");
            outputFile = new FileOutputStream("s:/outputFile.txt");
            char c;
            while(( c = (char) inputFile.read()) != -1)
            {
                System.out.println((char)c);
                outputFile.write(c);
            }

            System.out.println("File transfer complete!");
        }

        finally
        {
            if (inputFile != null)
                inputFile.close();

            if (outputFile != null)
                outputFile.close();
        }
    }
}

这是我的代码。在 while 循环条件下,首先我将其设置为通过 read() 将 int 输出类型转换为字符。结果是它进入了一个无限循环,所有字符都被转换为“?(ASCII:63)。然后我意识到我的 char 转换错误并改变了它。

但是当我将 while 条件更改为“=-2”(没有字符转换)时(这种情况永远不会发生,因此将其置于无限循环中)。在这里,即使没有字符转换,文件的第一个(比如 10 个)有效字符仍然被转换为“?”。(在它到达 EOF 后,所有无效的字符都变成“?”——我假设这是给定的)。

为什么会这样?至少应该正确读取文件的有效字符,直到它遇到 EOF 并开始以无效字符为食!

Java IO 文件输入流

评论


答:

0赞 Chewy 5/25/2016 #1

只需更改这部分代码 - 一旦转换为字符,就无法成功将其与整数进行比较,因此永远不会满足 while 退出条件。

int c;
while ((c = inputFile.read()) != -1) {
    System.out.println((char) c);
    outputFile.write(c);
}

此外,使用 java 8 java.nio 和 java.io 包要简单得多

public static void main(String[] args) throws IOException {
    List<String> lines = Files.readAllLines(Paths.get("s:/inputFile.txt"));
    Files.write(Paths.get("s:/outputFile.txt"), lines);
}
0赞 Robert 5/25/2016 #2

键入 to char 的结果是糟糕的样式。字符应该只从 Reader 读取 - 在你的例子中,你可以使用 InputStreamReaderin.read()

    inputFile = new FileInputStream("s:/inputFile.txt");
    outputFile = new FileOutputStream("s:/outputFile.txt");
    Reader inputReader = InputStreamReader(inputFile, StandardCharsets.UTF_8);
    Writer outputWriter = OutputStreamWriter(outputFile, StandardCharsets.UTF_8);
    char[] cbuf = new char[4096];
    int read;
    while( (read = inputReader.read(cbuf)) >= 0)
    {
        System.out.println(new String(cbuf, 0, read));
        outputWriter.write(cbuf, 0, read);
    }

此外,此示例不会逐字节复制(大幅提高速度),而是将 UTF-8 作为字符集。

评论

0赞 Stephen C 5/25/2016
“将 in.read() 的结果转换为 char 是糟糕的样式。”- 不仅仅是糟糕的风格。根据实际的文件编码,它可能只是不正确。
0赞 Stephen C 5/25/2016
此外,硬写 UTF-8 可能是一个坏主意。一个更安全的假设是使用平台默认字符集进行读取和写入。
2赞 Stephen C 5/25/2016 #3

为什么会这样?

问题出在这一行:

 while(( c = (char) inputFile.read()) != -1)

您正在执行以下操作:

  1. 从文件中读取字节。这为您提供了一个字节,该字节范围为 0 到 255,或 -1。int

  2. 您正在将该值强制转换为 .对于字节,它给出的值介于 0 到 255 之间。因为演员会给你.charchar-1'\uffff'

  3. 将该值分配给 。c

  4. 然后,根据 测试该值。这就是它出错的地方。在返回的情况下,您现在将评估此 .LHS 转换为值 ... ...这与.他们是不同的。-1read-1'\uffff' == -1int0x0000ffff0xffffffff

然后你打印......当它被转换为默认字符集中的字符输出时。'uffff''?'


代码中有两个主要错误。首先,转换 -> -> 不起作用;见上文。intcharint

其次,也是更重要的一点:

  • 您不应该尝试使用 (面向字节的)将数据读取为字符,并且InputStream

  • 您不应尝试将字符数据写入 .OutputStream

根据您在这里实际尝试实现的目标,您应该:

  • 读取和写入字节...没有虚假的“转换”到中间,或者char

  • 使用 A 和 正确执行平台默认字符集的转换。FileReaderFileWriter

(关于缓冲、选择备用字符集等,还有其他一些观点可以提出,但这个答案已经太长了。

评论

0赞 Navin Israni 5/26/2016
当 FIS 读取“-1”EOF时,此逻辑有效。但是我将所有字符输出为 -1(因此是?),甚至是文件的有效字符......为什么它读取文件有效字符的 EOF..还是我在这里遗漏了什么?
0赞 Stephen C 5/26/2016
我认为你误解了事情。1) a 不可能是,因为是>>无符号<<类型。2) 当 a 转换为 on 输出时,这意味着字符值(无论它是什么)在正在使用的字符编码方案中没有有效的编码。你无法推断它的实际价值是什么。char-1charchar?