在读取文本文件时检查行中是否有不可打印字符

48

我的程序必须逐行读取文本文件。 文件采用UTF-8编码。 我不确定文件是否正确 - 可能包含无法打印的字符。 是否可能在不涉及字节级别的情况下进行检查? 谢谢。


你想检查单行还是整个文件? - Eran Zimmerman Gonen
保证换行符是正确的吗? - Tarnschaf
检查单行。是的,换行符正确。 - user710818
你是指在特定字体中无法打印的字符吗?有些字符在任何字体中都没有定义。这可能是同样的情况。 - Peter Lawrey
8个回答

120

使用 FileInputStream 打开文件,然后使用带有 UTF-8 CharsetInputStreamReader 从流中读取字符,并使用 BufferedReader 读取行,例如通过 BufferedReader#readLine,这将给您一个字符串。 一旦您获得了字符串,就可以检查不是您认为可打印的字符。

例如(没有错误检查),使用 try-with-resources(在较新版本的Java中):

String line;
try (
    InputStream fis = new FileInputStream("the_file_name");
    InputStreamReader isr = new InputStreamReader(fis, Charset.forName("UTF-8"));
    BufferedReader br = new BufferedReader(isr);
) {
    while ((line = br.readLine()) != null) {
        // Deal with the line
    }
}

2
或者,为了减少一个步骤,可以使用FileReader打开文件,并使用BufferedReader逐行读取。 - Warren Dew
1
@stviper:现在已经是2015年了,我更新了它以使用try-with-resources,更加简洁。 :-) - T.J. Crowder
1
感谢您的编辑,但只有“isr”位是正确的; ()应该是(),而不是{},最后一个分号不是必需的(但允许使用,因此我已将其留下 - 更符合它上面的行)。 - T.J. Crowder

49

虽然使用 BufferedReaderInputStreamReader 手动实现不难,但我会使用 Guava 库:

List<String> lines = Files.readLines(file, Charsets.UTF_8);

你可以随意处理那些行。

注意:这将一次性将整个文件读入内存。在大多数情况下,这实际上是可以的 - 而且肯定比逐行读取并在读取每行时处理每行要简单。如果是一个巨大的文件,您可能需要按照T.J. Crowder的答案进行操作。


4
Guava还提供了一种带有回调函数的方法:Files.readLines(File file, Charset charset, LineProcessor<T> callback)。 - Vlagorce
如果目的是逐行处理,使用BufferedReader非常简单。为了读取行而添加另一个库依赖项也是过度设计,因为核心Java库已经支持该功能。 - user172818
5
不,这不是那么简单的... 至少如果你没有使用Java 7及其try-with-resources语句的话。此外,我会对任何非平凡的Java程序没有多个地方可以从Guava中受益感到惊讶。这是一个很棒的库,我离不开它。 - Jon Skeet

42

刚刚发现在Java NIO (java.nio.file.*)中你可以轻松地编写:

List<String> lines=Files.readAllLines(Paths.get("/tmp/test.csv"), StandardCharsets.UTF_8);
for(String line:lines){
  System.out.println(line);
}

不再使用FileInputStreamBufferedReader,而是...


只是想补充一下,java.nio.file.* 自 JDK 7 起就可用。 - Jifeng Zhang
3
值得一提的是 Files.readAllLines 的文档:该方法适用于简单情况,方便一次性读取所有行。它不适用于读取大文件。 - Remi Mélisson
@fabian 你说得对,我一直在使用它 :) - McIntosh

15

如果您想检查一个字符串是否包含不可打印的字符,您可以使用正则表达式

[^\p{Print}]

然而,这包括您的非打印字符集中的空格和制表符字符,因为它们会影响单词在页面中的位置。 - bernard paulus

11

不行 - 删除这个 - 你正在使用默认编码 - 这会让你陷入痛苦的世界。 - Mr_and_Mrs_D

5
我可以找到以下几种方法来做。
private static final String fileName = "C:/Input.txt";

public static void main(String[] args) throws IOException {
    Stream<String> lines = Files.lines(Paths.get(fileName));
    lines.toArray(String[]::new);

    List<String> readAllLines = Files.readAllLines(Paths.get(fileName));
    readAllLines.forEach(s -> System.out.println(s));

    File file = new File(fileName);
    Scanner scanner = new Scanner(file);
    while (scanner.hasNext()) {
        System.out.println(scanner.next());
    }

2

@T.J.Crowder的回答适用于Java 6。在Java 7中,@McIntosh的回答是正确的,尽管它使用了Charset作为UTF-8的名称,但这种做法已被不推荐使用。

List<String> lines = Files.readAllLines(Paths.get("/tmp/test.csv"),
    StandardCharsets.UTF_8);
for(String line: lines){ /* DO */ }

这让我想起了Skeet上面发布的Guava方式 - 当然,同样的注意事项也适用。也就是说,对于大文件(Java 7):

BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8);
for (String line = reader.readLine(); line != null; line = reader.readLine()) {}

针对Java 6有效的答案仍然适用于Java 7。 - user207421
当有更好的方法时,@user207421并不是真的这样做。 - Mr_and_Mrs_D

0
如果文件中的每个字符都是用UTF-8正确编码的,您使用UTF-8编码的读取器读取该文件时不会遇到任何问题。由您来检查文件中的每个字符,并确定其是否可打印。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接