如何找出BufferedReader#readLine()使用的换行符是哪个?

14

我正在通过 BufferedReader 读取文件

String filename = ...
br = new BufferedReader( new FileInputStream(filename));
while (true) {
   String s = br.readLine();
   if (s == null) break;
   ...
}

我需要知道行是否由'\n'或'\r\n'分隔,有没有办法可以找出来?

我不想打开FileInputStream以便最初扫描它。理想情况下,我想问BufferedReader,因为它必须知道。

我很乐意覆盖BufferedReader以进行修改,但我真的不想两次打开文件流。

谢谢,

注意:当前换行符(通过System.getProperty("line.separator")返回)不能用于此,因为该文件可能已由另一个操作系统上的其他应用程序编写。

9个回答

14
为了与 BufferedReader 类保持一致,您可以使用以下处理 \n、\r、\n\r 和 \r\n 结尾分隔符的方法:
public static String retrieveLineSeparator(File file) throws IOException {
    char current;
    String lineSeparator = "";
    FileInputStream fis = new FileInputStream(file);
    try {
        while (fis.available() > 0) {
            current = (char) fis.read();
            if ((current == '\n') || (current == '\r')) {
                lineSeparator += current;
                if (fis.available() > 0) {
                    char next = (char) fis.read();
                    if ((next != current)
                            && ((next == '\r') || (next == '\n'))) {
                        lineSeparator += next;
                    }
                }
                return lineSeparator;
            }
        }
    } finally {
        if (fis!=null) {
            fis.close();
        }
    }
    return null;
}

谢谢,它运行得很好,但是FileInputStream对象没有正确关闭。 - serg.nechaev
你应该添加一个 next != current 的检查,否则如果文件以空行开头,你可能会得到 \n\n 或 \r\r 作为分隔符。 - M. Schenk
感谢serh.nechaev和M. Schenk,我已经考虑了你们的修改。 - Antoine

7
阅读了java文档(我承认我是一个Pythonista),似乎没有一种清晰的方法来确定特定文件中使用的行尾编码。
我能推荐的最好的方法是使用BufferedReader.read()并迭代文件中的每个字符。类似这样:
String filename = ...
br = new BufferedReader( new FileInputStream(filename));
while (true) {
   String l = "";
   Char c = " ";
   while (true){
        c = br.read();
        if not c == "\n"{
            // do stuff, not sure what you want with the endl encoding
            // break to return endl-free line
        }
        if not c == "\r"{
            // do stuff, not sure what you want with the endl encoding
            // break to return endl-free line
            Char ctwo = ' '
            ctwo = br.read();
            if ctwo == "\n"{
                // do extra stuff since you know that you've got a \r\n
            }
        }
        else{
            l = l + c;
        }
   if (l == null) break;
   ...
   l = "";
}

3

BufferedReader.readLine()方法并没有提供任何方式来确定行结束符是什么。如果你需要知道,你需要自己读取字符并找到行结束符。

你可能会对内部的LineBuffer类感兴趣,它来自于Guava库(以及它所使用的公共LineReader类)。LineBuffer提供了一个回调方法void handleLine(String line, String end),其中end是行结束符。你可以根据此来构建自己想要的功能。一个API可能看起来像这样:public Line readLine(),其中Line是一个包含行文本和行结束符的对象。


@gshauger:你可以这样说很多问题,但这并不意味着使用一个库不是更好的选择。在LineBuffer的情况下,它本身就是内部的,所以添加整个库也没有帮助……不过他可以复制那个文件。 - ColinD
1
我不会对很多问题这样说...只有那些不需要不必要的依赖关系的问题...而这正是你所推荐的。此外,这不是你第一次不必要地使用Guava库了。 - gshauger
@gshauger:当别人已经编写了代码,可以避免你自己编写时,有时使用它是很有用的,特别是考虑到像这样的小问题很少是孤立存在的。我碰巧非常熟悉Guava,所以当我认为使用它比仅使用JDK多做额外工作更容易或更合适时,我倾向于建议使用它的解决方案。你对库的明显厌恶并不影响我的答案的有效性。(我主要是建议OP可能想引用现有的一些代码来完成他想要的事情。) - ColinD
@gshauger:我不喜欢编写和维护已经由其他人编写、测试和维护的大量代码,以及这对“正确设计的软件的质量、可扩展性、可部署性和可用性”的影响。我确实同意应该谨慎选择依赖项,但个人认为Guava具有极高的功率重量比,大多数Java项目都可以从中受益。不过,最终决定权在于OP,我只是提供了一个他们可能没有意识到的选项。 - ColinD

2

BufferedReader 不接受 FileInputStreams

不,您无法找到 BufferedReader 读取的文件中使用的行终止符字符。该信息在读取文件时丢失。

很遗憾,下面所有的答案都是不正确的。

编辑:是的,您可以始终扩展 BufferedReader 以包括所需的其他功能。


2
答案是你无法找出行末的内容。
我正在寻找同一函数中可能导致行末的原因。在查看BufferedReader源代码后,我可以说BufferedReader.readLine会以'\r'或'\n'结束行,并跳过剩余的'\r'或'\n'。这是硬编码的,不考虑设置。

1
如果您正在将此文件读入 Swing 文本组件中,则可以使用 JTextComponent.read(...) 方法将文件加载到文档中。然后,您可以使用以下代码:
textComponent.getDocument().getProperty( DefaultEditorKit.EndOfLineStringProperty );

获取文件中实际使用的行尾符字符串。


@EJP 这适用于任何解决方案。 - camickr
1
不会的。你可以想象一个API,它可以读取一行文本,然后检索用于该行的终止符。 - user207421
@EJP,根据海报的评论,我认为他只是想知道文件是在Windows("\r\n")还是Unix("\n")上创建的,这种情况下他只关心第一个行分隔符。如果他关心每一行,那么是的,每一行都需要解析。 - camickr
@camrickr同意,但这适用于“问题”而不是“任何解决方案”。 - user207421
@EJP,是的,我指的是这个问题的任何解决方案,而不是一般的任何解决方案。 - camickr

1
也许你可以使用Scanner代替。
你可以将正则表达式传递给Scanner#useDelimiter()以设置自定义分隔符。
String regex="(\r)?\n";
String filename=....;
Scanner scan = new Scanner(new FileInputStream(filename));
scan.useDelimiter(Pattern.compile(regex));
while (scan.hasNext()) {
    String str= scan.next();
    // todo
}

您可以使用以下代码将BufferedReader转换为Scanner
 new Scanner(bufferedReader);

0

不确定是否有用,但有时我需要在读取文件后找出行分隔符。

在这种情况下,我使用以下代码:

/**
* <h1> Identify which line delimiter is used in a string </h1>
*
* This is useful when processing files that were created on different operating systems.
*
* @param str - the string with the mystery line delimiter.
* @return  the line delimiter for windows, {@code \r\n}, <br>
*           unix/linux {@code \n} or legacy mac {@code \r} <br>
*           if none can be identified, it falls back to unix {@code \n}
*/
public static String identifyLineDelimiter(String str) {
    if (str.matches("(?s).*(\\r\\n).*")) {     //Windows //$NON-NLS-1$
        return "\r\n"; //$NON-NLS-1$
    } else if (str.matches("(?s).*(\\n).*")) { //Unix/Linux //$NON-NLS-1$
        return "\n"; //$NON-NLS-1$
    } else if (str.matches("(?s).*(\\r).*")) { //Legacy mac os 9. Newer OS X use \n //$NON-NLS-1$
        return "\r"; //$NON-NLS-1$
    } else {
        return "\n";  //fallback onto '\n' if nothing matches. //$NON-NLS-1$
    }
}

-2

如果你使用的是Groovy,你可以简单地这样做:

def lineSeparator = new File('path/to/file').text.contains('\r\n') ? '\r\n' : '\n'

我唯一能猜测的是,用户在询问关于java的问题,根据该问题的标签看起来是这样。不过不能确定。 - eRaisedToX

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