如何在 Java 中读取同一个文件两次?

How can i read the same file two times in Java?

我想计算文件的行,在第二遍中我想获取每一行并对其进行操作。它没有编译错误,但它不能进入​​第二个 while ((line = br.readLine()) != null) 。 有没有不同的方法来获取文件的行(电影)并存储在数组中?

        BufferedReader br = null;

        try { // try to read the file
            br = new BufferedReader(new FileReader("movies.txt"));
            String line;
            int numberOfMovies = 0;
            while ((line = br.readLine()) != null) {
                numberOfMovies++;
            }
            Movie[] movies = new Movie[numberOfMovies]; // store in a Movie
                                                        // array every movie of
                                                        // the file
            String title = "";
            int id = 0;
            int likes = 0;
            int icounter = 0; // count to create new movie for each line
            while ((line = br.readLine()) != null) {
                line = line.trim();
                line = line.replaceAll("/t", "");
                line = line.toLowerCase();
                String[] tokens = line.split(" "); // store every token in a
                                                    // string array
                id = Integer.parseInt(tokens[0]);
                likes = Integer.parseInt(tokens[tokens.length]);
                for (int i = 1; i < tokens.length; i++) {
                    title = title + " " + tokens[i];
                }
                movies[icounter] = new Movie(id, title, likes);
                icounter++;
            }


        } catch (IOException e) {
            e.printStackTrace();
        }

如果您使用相同的 Reader,一旦您到达第二个循环,所有内容都已读取。

关闭第一个 Reader,然后创建另一个以供第二次阅读。

这里有两件事:

  1. InputStreams 和 Readers 是一次性结构:一旦你读完它们,你要么需要 explicitly rewind 它们(如果它们支持倒带),或者您需要关闭它们(始终关闭您的流和阅读器!)并打开一个新的。

  2. 但是在这种情况下,这两个过程完全没有必要,只需使用动态增长的结构来收集您的 Movie 对象而不是数组:例如 ArrayList

首先,不需要读取文件两次。

其次,你为什么不使用 java.nio.file.Files class 来读取你的文件。

它有一个方法 readAllLines(Path path, Charset cs) 可以返回一个 List<String>

然后如果你想知道有多少行只需要在列表上调用size()方法就可以使用列表构造Movie个对象

List<Movie> movieList = new ArrayList<>();

for (String line : Files.readAllLines(Paths.get("movies.txt"), Charset.defaultCharset())) {

     // Construct your Movie object from each individual line and add to the list of Movies

     movieList.add(new Movie(id, title, likes));
}

Files class 的使用也减少了样板代码,因为它将在完成读取后处理关闭资源,这意味着您不需要 finally 块来关闭任何东西。

您正在 运行 浏览带有 BufferedReader 的文件,直到下一行指向 null。由于你的 BufferedReadernull,它甚至不会进入第二个 while((line = br.readline) != null),因为第一个读取行是 null.

尝试获取新的 BufferedReader。像这样:

...
int id = 0;
int likes = 0;
int icounter = 0;
br = new BufferedReader(new FileReader("movies.txt")) //Re-initialize the br to point 
                                                      //onto the first line again
while ((line = br.readLine()) != null)
...

编辑: 先关闭reader..

最简单的方法是再次重置 br

try { // try to read the file 
    br = new BufferedReader(new FileReader("movies.txt"));
    String line; int numberOfMovies = 0;
    while (br.hasNextLine()){
        numberOfMovies++;
    }
    br.close();
    Movie[] movies = new Movie[numberOfMovies];
    // store in a Movie
    // array every movie of
    // the file
    String title = "";
    int id = 0;
    int likes = 0;
    int icounter = 0;
    // count to create new movie for each line
    br = new BufferedReader(new FileReader("movies.txt"));
    while ((br.hasNextLine()) {
        line = line.trim();
        line = line.replaceAll("/t", "");
        line = line.toLowerCase();
        String[] tokens = line.split(" ");
        // store every token in a
        // string array
        id = Integer.parseInt(tokens[0]);
        likes = Integer.parseInt(tokens[tokens.length]);
        for (int i = 1; i < tokens.length; i++) {
            title = title + " " + tokens[i];
        }
        movies[icounter] = new Movie(id, title, likes);
        icounter++;
    }
} catch (IOException e) { e.printStackTrace(); }

我将 br.nextLine() != null 更改为 br.hasNextLine() 因为它更短更适合这种情况。而且它不会消耗一行。

这是 post 上已有的几个其他答案的组合,但这就是我将如何重写您的代码以填充列表的方式。这双重解决了以下问题 1) 需要读取文件两次 2) 在使用 Java8 Streams 时删除使用 BufferedReader 的样板以使 List 的初始化尽可能简洁:

private static class Movie {
    private Movie(int id, String title, int likes) {
        //TODO: set your instance state here
    }
}

private static Movie movieFromFileLine(String line) {
    line = line.trim();
    line = line.replaceAll("/t", "");
    line = line.toLowerCase();
    String[] tokens = line.split(" "); // store every token in a

    String title = "";
    int id = Integer.parseInt(tokens[0]);
    int likes = Integer.parseInt(tokens[tokens.length]);
    for (int i = 1; i < tokens.length; i++) {
        title = title + " " + tokens[i];
    }
    return new Movie(id, title, likes);
}

public static void main(String[] args) throws IOException {
    List<Movie> movies = Files.readAllLines(Paths.get("movies.txt"), Charset.defaultCharset()).stream().map
            (App::movieFromFileLine).collect(Collectors.toList());
    //TODO: Make some magic with your list of Movies
}

对于您绝对需要读取源文件(文件、URL 或其他)两次的情况,您需要注意内容很可能在第一次和第二次之间发生变化阅读并准备好处理这些差异。

如果您可以合理地假设源代码的内容适合内存并且您的代码完全期望在 Readers/InputStreams 的多个实例上运行,您可以首先考虑使用适当的 IOUtils.copy 方法从 commons-io 读取源的内容并将其复制到 ByteArrayOutputStream 以创建一个可以一遍又一遍地重新读取的 byte[]。