java - 使用 Java 读取非常大的文件

标签 java servlets memory io nio

我正在 Servlet 中读取一个 77MB 的文件,将来这将是 150GB。该文件不是使用任何类型的 nio 包编写的,它只是使用 BufferedWriter 编写的。

现在这就是我需要做的。

  1. 逐行读取文件。每行都是文本的“哈希码”。分成3个字符(3个字符代表1个单词) 可以长也可以短,我不知道。

  2. 读完一行后,将其转换成真实的单词。我们有单词和哈希的映射,因此我们可以找到单词。

到目前为止,我使用BufferedReader 来读取文件。对于 150GB 这样的大文件来说它很慢而且不好。即使是这个 77MB 的文件,也需要数小时才能完成整个过程。因为我们不能让用户等待数小时,所以应该在几秒钟内。因此,我们决定将文件加载到内存中。首先,我们考虑将每一行加载到一个 LinkedList 中,这样内存就可以保存它。但是要知道,内存是存不下这么大的数额的。经过大搜索后,我决定将将文件映射到内存作为答案。内存比磁盘快得多,所以我们也可以超快地读取文件。

代码:

public class MapRead {

    public MapRead()
    {
        try {
            File file = new File("E:/Amazon HashFile/Hash.txt");
            FileChannel c = new RandomAccessFile(file,"r").getChannel();

            MappedByteBuffer buffer = c.map(FileChannel.MapMode.READ_ONLY, 0,c.size()).load();

            for(int i=0;i<buffer.limit();i++)
            {
                System.out.println((char)buffer.get());
            }

            System.out.println(buffer.isLoaded());
            System.out.println(buffer.capacity());



        } catch (IOException ex) {
            Logger.getLogger(MapRead.class.getName()).log(Level.SEVERE, null, ex);
        }
    }


}

但我看不到任何“超快”的东西。我需要逐行。我有几个问题要问。

  1. 你读了我的描述,你知道我需要做什么。我已经为此迈出了第一步,那对吗?

  2. 我的Map方法正确吗?我的意思是,这与以正常方式阅读没有区别。那么这是否首先将“整个”文件保存在内存中? (假设使用一种称为映射的技术)那么我们必须编写另一个代码来访问该内存吗?

  3. 如何以超“快”的速度逐行阅读? (如果我必须先将整个文件加载/映射到内存中数小时,然后在几秒钟内以超高速访问它,我也完全可以接受)

  4. 在 Servlet 中读取文件好吗? (因为它正在被多人访问,而且一次只会打开一个IO流。在这种情况下,这个servlet会同时被数千人访问)

更新

这是我使用 SO 用户 Luiggi Mendoza 的回答更新代码时的样子。

public class BigFileProcessor implements Runnable {
    private final BlockingQueue<String> linesToProcess;
    public BigFileProcessor (BlockingQueue<String> linesToProcess) {
        this.linesToProcess = linesToProcess;
    }
    @Override
    public void run() {
        String line = "";
        try {
            while ( (line = linesToProcess.take()) != null) {

                System.out.println(line); //This is not happening
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


public class BigFileReader implements Runnable {
    private final String fileName;
    int a = 0;

    private final BlockingQueue<String> linesRead;
    public BigFileReader(String fileName, BlockingQueue<String> linesRead) {
        this.fileName = fileName;
        this.linesRead = linesRead;
    }
    @Override
    public void run() {
        try {

            //Scanner do not work. I had to use BufferedReader
            BufferedReader br = new BufferedReader(new FileReader(new File("E:/Amazon HashFile/Hash.txt")));
            String str = "";

            while((str=br.readLine())!=null)
            {
               // System.out.println(a);
                a++;
            }

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



public class BigFileWholeProcessor {
    private static final int NUMBER_OF_THREADS = 2;
    public void processFile(String fileName) {

        BlockingQueue<String> fileContent = new LinkedBlockingQueue<String>();
        BigFileReader bigFileReader = new BigFileReader(fileName, fileContent);
        BigFileProcessor bigFileProcessor = new BigFileProcessor(fileContent);
        ExecutorService es = Executors.newFixedThreadPool(NUMBER_OF_THREADS);
        es.execute(bigFileReader);
        es.execute(bigFileProcessor);
        es.shutdown();
    }
}



public class Main {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // TODO code application logic here
        BigFileWholeProcessor  b = new BigFileWholeProcessor ();
        b.processFile("E:/Amazon HashFile/Hash.txt");
    }
}

我正在尝试打印 BigFileProcessor 中的文件。我的理解是这样的;

  1. 用户输入文件名

  2. BigFileReader 逐行读取该文件

  3. 行之后,调用BigFileProcessor。这意味着,假设 BigFileReader 读取了第一行。现在 BigFileProcessor 被调用。现在,一旦 BigFileProcessor 完成了对该行的处理,现在 BigFileReader 将读取第 2 行。然后再次为该行调用 BigFileProcessor,等等。

可能是我对这段代码的理解不正确。我应该如何处理这条线?

最佳答案

我建议在这里使用多线程:

  • 一个线程将负责读取文件的每一行并将其插入到 BlockingQueue 中。以便进行处理。
  • 另一个线程将 take队列中的元素并处理它们。

要实现这种多线程工作,最好使用ExecutorService接口(interface)并传递 Runnable 实例,每个实例都应实现每个任务。请记住只有一个任务来读取文件。

如果队列有特定大小,您还可以设法停止读取,例如如果队列有 10000 个元素,则等到其大小降至 8000,然后继续读取并填充队列。

Reading files in Servlets is good ?

我建议永远不要在 servlet 中做繁重的工作。相反,触发一个异步任务,例如通过 JMS 调用,然后在此外部代理中您将处理您的文件。


解决问题的上述解释的简要示例:

public class BigFileReader implements Runnable {
    private final String fileName;
    private final BlockingQueue<String> linesRead;
    public BigFileReader(String fileName, BlockingQueue<String> linesRead) {
        this.fileName = fileName;
        this.linesRead = linesRead;
    }
    @Override
    public void run() {
        //since it is a sample, I avoid the manage of how many lines you have read
        //and that stuff, but it should not be complicated to accomplish
        Scanner scanner = new Scanner(new File(fileName));
        while (scanner.hasNext()) {
            try {
                linesRead.put(scanner.nextLine());
            } catch (InterruptedException ie) {
                //handle the exception...
                ie.printStackTrace();
            }
        }
        scanner.close();
    }
}

public class BigFileProcessor implements Runnable {
    private final BlockingQueue<String> linesToProcess;
    public BigFileProcessor (BlockingQueue<String> linesToProcess) {
        this.linesToProcess = linesToProcess;
    }
    @Override
    public void run() {
        String line = "";
        try {
            while ( (line = linesToProcess.take()) != null) {
                //do what you want/need to process this line...
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class BigFileWholeProcessor {
    private static final int NUMBER_OF_THREADS = 2;
    public void processFile(String fileName) {
        BlockingQueue<String> fileContent = new LinkedBlockingQueue<String>();
        BigFileReader bigFileReader = new BigFileReader(fileName, fileContent);
        BigFileProcessor bigFileProcessor = new BigFileProcessor(fileContent);
        ExecutorService es = Executors.newFixedThreadPool(NUMBER_OF_THREADS);
        es.execute(bigFileReader);
        es.execute(bigFileProcessor);
        es.shutdown();
    }
}

关于java - 使用 Java 读取非常大的文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22152704/

相关文章:

javax.net.ssl.SSLHandshakeException : No appropriate protocol (protocol is disabled or cipher suites are inappropriate

java - oc4j 企业管理器控制台 session 超时?

java - jni/java : thread safe publishing/sharing of effectively immutable native object

python - NumPy 张量点内存错误

java - java - 如何将整数列表与java流相加?

java - 我正在尝试使用 ServletConfig 接口(interface)加载我的 sql jdbc 驱动程序

java - 未找到 Servlet 请求的资源

java - 我可以在 Java 中扩展 App Engine 标准错误 JSON 吗?

windows - 内存分析——VAD标签和代码注入(inject)

c++ - 为同一个指针变量重复调用复制构造函数内存泄漏?