我正在 Servlet 中读取一个 77MB 的文件,将来这将是 150GB。该文件不是使用任何类型的 nio
包编写的,它只是使用 BufferedWriter
编写的。
现在这就是我需要做的。
逐行读取文件。每行都是文本的“哈希码”。分成3个字符(3个字符代表1个单词) 可以长也可以短,我不知道。
读完一行后,将其转换成真实的单词。我们有单词和哈希的映射,因此我们可以找到单词。
到目前为止,我使用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);
}
}
}
但我看不到任何“超快”的东西。我需要逐行。我有几个问题要问。
你读了我的描述,你知道我需要做什么。我已经为此迈出了第一步,那对吗?
我的Map方法正确吗?我的意思是,这与以正常方式阅读没有区别。那么这是否首先将“整个”文件保存在内存中? (假设使用一种称为
映射
的技术)那么我们必须编写另一个代码来访问该内存吗?如何以超“快”的速度逐行阅读? (如果我必须先将整个文件加载/映射到内存中数小时,然后在几秒钟内以超高速访问它,我也完全可以接受)
在 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
中的文件。我的理解是这样的;
用户输入文件名
BigFileReader
逐行读取该文件每 行之后,调用
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/