Java Scanner(File) 行为不端,但 Scanner(FIleInputStream) 始终适用于同一个文件

Posted

技术标签:

【中文标题】Java Scanner(File) 行为不端,但 Scanner(FIleInputStream) 始终适用于同一个文件【英文标题】:Java Scanner(File) misbehaving, but Scanner(FIleInputStream) always works with the same file 【发布时间】:2012-03-18 13:11:00 【问题描述】:

我在使用 Scanner 时遇到了奇怪的行为。当我使用 Scanner(FileInputStream) 构造函数时,它将适用于我正在使用的一组特定文件,但不适用于 Scanner(File) 构造函数。

案例一:Scanner(File)

Scanner s = new Scanner(new File("file"));
while(s.hasNextLine()) 
    System.out.println(s.nextLine());

结果:无输出

案例2:Scanner(FileInputStream)

Scanner s = new Scanner(new FileInputStream(new File("file")));
while(s.hasNextLine()) 
    System.out.println(s.nextLine());

结果:文件内容输出到控制台。

输入文件是一个包含单个类的 java 文件。

我以编程方式(在 Java 中)仔细检查了:

文件存在, 可读, 并且具有非零文件大小。

通常Scanner(File) 在这种情况下对我有用,我不知道为什么现在不行。

【问题讨论】:

这是唯一的代码,还是围绕着这一切发生了其他事情?这个 sn-p 似乎不完整,因为至少会发生一些异常处理。你能提供给我们完整的代码吗? 有趣的问题。请在您的文件中发布您的实际代码和粘贴箱。另外,Charset.defaultCharset() 在您的系统上的输出是什么? @Perception:我也想过,但 Scanner 的来源似乎暗示他们在这两种情况下都使用默认字符集,如果不使用会明确指定它的构造函数。 @kashiko:啊,另一个非常重要的后续问题:文件的大小是多少? 我已经更新了我的原始帖子以从我的源文件中复制代码。就像测试一样,我正在读取文件并将其输出到终端。该文件是一个开源项目的java源文件。我的字符集是 UTF-8。文件大小为 18357 字节。 【参考方案1】:

hasNextLine() 调用findWithinHorizon(),后者又调用findPatternInBuffer(),搜索定义为.*(\r\n|[\n\r\u2028\u2029\u0085])|.+$ 的行终止符模式的匹配项

奇怪的是,如果文件包含(与文件大小无关)例如 0x0A 行终止符,则 findPatternInBuffer 两种方式都可以构造 Scanner(使用 FileInputStream 或通过 File);但是如果文件包含一个非 ascii 字符(即 >= 7f),则使用 FileInputStream 返回 true,而使用 File 返回 false。

非常简单的测试用例:

创建一个只包含字符“a”的文件

# hexedit file     
00000000   61 0A                                                a.

# java Test.java
using File: true
using FileInputStream: true

现在用 hexedit 将文件编辑为:

# hexedit file
00000000   61 0A 80                                             a..

# java Test.java
using File: false
using FileInputStream: true

在测试 java 代码中,除了问题中已经存在的内容外,别无其他:

import java.io.*;
import java.lang.*;
import java.util.*;
public class Test 
    public static void main(String[] args) 
        try 
                File file1 = new File("file");
                Scanner s1 = new Scanner(file1);
                System.out.println("using File: "+s1.hasNextLine());
                File file2 = new File("file");
                Scanner s2 = new Scanner(new FileInputStream(file2));
                System.out.println("using FileInputStream: "+s2.hasNextLine());
         catch (IOException e) 
                e.printStackTrace();
        
    

所以,事实证明这是一个字符集问题。事实上,将测试更改为:

 Scanner s1 = new Scanner(file1, "latin1");

我们得到:

# java Test 
using File: true
using FileInputStream: true

【讨论】:

有趣。在查看Scanner 委托人时,如果未指定,他们似乎都假设默认字符集,但正如您所指出的那样,在运行时存在差异。也许内部使用的通道可能会强制使用不同的通道,更深一层?我在想……有机会我会试试看。【参考方案2】:

通过查看Oracle/Sun JDK's 1.6.0_23 implementation of Scanner,Scanner(File) 构造函数调用FileInputStream,即meant for raw binary data

这表明调用一个或另一个构造函数时使用的缓冲和解析技术有所不同,这将直接影响您的代码调用hasNextLine()

Scanner(InputStream) 使用 InputStreamReaderScanner(File) 使用 InputStream 传递给 ByteChannel (并且可能一次性读取整个文件,因此在您的情况下推进光标)。

【讨论】:

Java(File) 和 Java(FileInputStream) 的合约读取相同,因此从 API 用户的角度来看,它们应该产生相同的行为。我之前使用过 Java(File) 没有这个问题。 Yanick:谢谢,这是一个有趣的问题。但似乎还有更多……(不过,有时你可以从 JDK 的代码中挖掘出一些东西……当我注意到ArrayList 有多个定义时,有一个“什么??”的时刻,例如(不,它们并不完全相同)。

以上是关于Java Scanner(File) 行为不端,但 Scanner(FIleInputStream) 始终适用于同一个文件的主要内容,如果未能解决你的问题,请参考以下文章

行为不端的积分函数

阻止行为不端的用户 facebook sdk ios

PHP / AJAX 并发会话仅在 Chrome 中行为不端

闪亮的反应式下拉菜单行为不端(带有数字输入)

在嵌入式linux上使用python可以套接字,绑定行为不端

Javascript replace()在iPhone上行为不端用于长字符串