当前位置: 首页 > 知识库问答 >
问题:

尝试从大型文本文件读/写时发生OutOfMemoryError

闻人凯泽
2023-03-14

我正试图读/写一个巨大的文本文件。但当我尝试这样做时,我得到了错误:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Unknown Source)
    at java.lang.AbstractStringBuilder.expandCapacity(Unknown Source)
    at java.lang.AbstractStringBuilder.append(Unknown Source)
    at java.lang.StringBuilder.append(Unknown Source)
    at ReadWriteTextFile.getContents(ReadWriteTextFile.java:52)
    at ReadWriteTextFile.main(ReadWriteTextFile.java:148)

我的代码如下:

import java.io.*;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

public class ReadWriteTextFile {

  /**
  * Fetch the entire contents of a text file, and return it in a String.
  * This style of implementation does not throw Exceptions to the caller.
  *
  * @param aFile is a file which already exists and can be read.
  */    
  static public String getContents(File aFile) {
    //...checks on aFile are elided
      StringBuilder contents = new StringBuilder(); 
      int maxlines = 1000; //counts max lines t read/write to the file
      BufferedReader input = null;
      BufferedWriter bw = null;

    try {
      //use buffering, reading one line at a time
      //FileReader always assumes default encoding is OK!
      input =  new BufferedReader(new FileReader(aFile));
      try {
          String line = null; //not declared within while loop
        /*
        * readLine is a bit quirky :
        * it returns the content of a line MINUS the newline.
        * it returns null only for the END of the stream.
        * it returns an empty String if two newlines appear in a row.
        */
        //for (int i = 0; i < 100; i++){
        //int count = 0;//initiates the line counter
      while (( line = input.readLine()) != null){

          int count = 0;//initiates the line counter    
          String modified1 = line.substring(2,17);
          String modified2 = line.substring(18,33);
          String modified3 = line.substring(40);        
          String result = "empty";
          result = modified1 + ",," +modified2 + modified3;
          System.out.println (result);          

//        contents.append(line);
//        contents.append(System.getProperty("line.separator"));
          //int count = 0;//initiates the line counter
          try {

              contents.append(line);
              contents.append(System.getProperty("line.separator"));
          String content = result;

          File file = new File("C:\\temp\\out.txt");//output path

          // if file doesnt exists, then create it
          if (!file.exists()) {
          file.createNewFile();
          }
          for ( int i = 0; i < 1000; i++){
              if (count++ % maxlines == 0) {
          FileWriter fw = new FileWriter(file.getAbsoluteFile(),true);
          bw = new BufferedWriter(fw);      
              bw.write(content);
          bw.newLine(); 
          }
          bw.close();
          }

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

        //}
        }
      }
      finally {
          input.close();
          bw.close();

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

    return contents.toString();
  }


/**
  * Change the contents of text file in its entirety, overwriting any
  * existing text.
  *
  * This style of implementation throws all exceptions to the caller.
  *
  * @param aFile is an existing file which can be written to.
  * @throws IllegalArgumentException if param does not comply.
  * @throws FileNotFoundException if the file does not exist.
  * @throws IOException if problem encountered during write.
  */
  static public void setContents(File aFile, String aContents)
                                 throws FileNotFoundException, IOException {
    if (aFile == null) {
        throw new IllegalArgumentException("File should not be null.");
    }
    if (!aFile.exists()) {
        throw new FileNotFoundException ("File does not exist: " + aFile);
    }
    if (!aFile.isFile()) {
        throw new IllegalArgumentException("Should not be a directory: " + aFile);
    }
    if (!aFile.canWrite()) {
        throw new IllegalArgumentException("File cannot be written: " + aFile);
    }

    //use buffering
    Writer output = new BufferedWriter(new FileWriter(aFile, true));
    try {
      //FileWriter always assumes default encoding is OK!
        output.write( aContents );
    }
    finally {
      output.close();
    }

  }

  /** Simple test harness.   */
  public static void main (String... aArguments) throws IOException {
      File testFile = new File("C:\\temp\\in.txt");//input path
      System.out.println("\n" + getContents(testFile));

  }

}

我尝试添加一个计数器(count),这样它可以在读取一定数量的行后刷新缓冲区。它不起作用。我知道计数器工作不正常。在执行“while”循环的特殊次数后,它不会变为零。我在while循环之前和之后添加了一个“for”循环来清空计数器,但效果不太好。

有什么建议吗?

共有3个答案

施华奥
2023-03-14

要在Java中读取一个巨大的文件,你应该用户java.util.scanner或apache公共资源LineIterator。两种方法都不会将整个文件加载到内存中,而是逐行读取文件。我能够读取文件的大小大于1gb使用LineIterator。请访问此链接了解更多详情http://www.baeldung.com/java-read-lines-large-file和示例。

益稳
2023-03-14

我尝试添加一个计数器(count),这样它可以在读取一定数量的行后刷新缓冲区。它不起作用。我知道计数器工作不正常。在执行“while”循环的特殊次数后,它不会变为零。我在while循环之前和之后添加了一个“for”循环来清空计数器,但效果不太好。

有什么建议吗?

内存溢出错误是因为您的文件是如此之大,该文件的所有内容不能读取到您的本地变量内容中的函数getContents(File aFile)

冲洗缓冲区与此无关。使用PrintWriter而不是BufferedWriter可能有助于清理代码。通过使用PrintWriter,您不必执行以下操作:

bw.write(content);
bw.newLine(); 

您可以将此更改为:

printWriter.println(content);

您还忘了告诉我们您的用例。最后,您所做的就是打印文件的所有内容。你可以一行一行地做这件事。

孙弘博
2023-03-14

尝试使用FileInputStream而不是BufferedReader/Writer。当我使用FileInputStream时,我可以在几秒钟内复制一个虚拟日志文件,它有超过3600万行,大小几乎为500MB。

FileInputStream in = new FileInputStream(from); //Read data from a file
FileOutputStream out = new FileOutputStream(to); //Write data to a file
byte[] buffer = new byte[4096]; //Buffer size, Usually 1024-4096
int len;
while ((len = in.read(buffer, 0, buffer.length)) > 0) {
    out.write(buffer, 0, len);
}
//Close the FileStreams
in.close();
out.close();

如果希望逐行读取文件而不是字节块,可以使用BufferedReader,但方式不同。

// Removed redundant exists()/createNewFile() calls altogether
String line;
BufferedReader br = new BufferedReader(new FileReader(aFile));
BufferedWriter output = new BufferedWriter(new FileWriter(file, true));
while ((line = br.readLine()) != null) {
      String modified1 = line.substring(2,17);
      String modified2 = line.substring(18,33);
      String modified3 = line.substring(40); 
      String result = "empty";
      result = modified1 + ",," +modified2 + modified3;
      System.out.println (result);
      output.append(result + "\n");//Use \r\n for Windows EOL
}
//Close Streams
br.close();
output.close();

正如EJP所说,不要将整个文件读入内存——这根本不是一件明智的事情。最好的办法是一行一行地读取每一行,或者一次读取一个文件的块,尽管为了准确起见,逐行读取可能是最好的。

当((line=br.readLine())!=null),您应该执行在内存中只加载一行时加载整个文件所需的操作。(例如检查一行是否包含u或从中获取文本)。

为了避免OOM异常,您可以尝试做的另一件事是使用多个字符串。

if(contents.length() => (Integer.MAX_VALUE-5000)) { //-5000 to give some headway when checking
    . . .
}
 类似资料:
  • 我试图将json对象写入文件/从文件中读取json对象。json结构很复杂,是动态生成的。 对于较小的json对象,我只需将json转换为string,然后进行string写/读操作。当json太大时,这会导致内存不足问题。 如何将JSONObject直接流到文件中,并直接从文件中流回JSONObject?

  • 问题内容: 我正在尝试更改文本文件中的某些行,而不影响其他行。这就是文本文件“ text.txt”中的内容 我的目标是更改第4行和第5行,但其余部分保持不变。 即使代码有效,我想知道是否有更好,更有效的方法?是否可以仅通过行号读取文件? 问题答案: 您没有什么可以改善的。但是您必须将所有行都写入 一个新文件 ,无论已更改还是未更改。较小的改进将是: 使用该语句; 避免将行存储在列表中; 子句中不带

  • 此代码检查一个5.1GB的大型文本文件,并检查是否有出现少于100次的单词。然后将5.1GB重写到输出文本文件中,并将这些单词替换为unk。主要问题是创建output.txt需要很长时间。我怀疑方法write_text()打开数据集文件和输出文件的方式会导致问题。 这个脚本背后的目标是:我有一个预构建的vocab和一个文本。这篇课文可能有新单词不在我的词汇表中,所以我想把它们添加到我的词汇表中。但

  • 问题内容: 我正在测试的Web应用程序在删除记录时需要确认。 我创建了一个测试,以输入删除此记录的有效理由。 执行此操作的代码如下: 文本框的XPath如下: 每当运行测试时,都会返回以下异常。 以下是Web应用程序上元素的HTML: 问题答案: 按照该HTML已经共享了与元素 属性 为 删除的原因 是 角 元素,因为你必须发送短信,你必须诱使 WebDriverWait 的 元素可以点击 如下:

  • 问题内容: 我不知道为什么出现java.io.EOFException。从服务器获取二进制流后,我想写一个文件。 这是我的代码 堆栈跟踪 问题答案: 它说,DataInputStream.readByte API不会在EOS上返回-1 返回:此输入流的下一个字节为带符号的8位字节。 抛出:EOFException-如果此输入流已到达末尾。 假定使用DataInputStream.readByte时