我正在将大文件的一部分读入char[]
缓冲区中。我想对该缓冲区进行后处理(意味着:提取两个引号中包含的所有内容),然后将其余部分写入新文件。
sample.txt:
this is my "sample" string;
this is "another;
sample "finished;
所需的输出:
this is my string;
this is finished;
代码
try (BufferedReader br = Files.newBufferedReader(Paths.get("sample.txt"))) {
BufferedWriter writer;
char[] buffer = new char[1024 * 1024 * 128];
int cnt = 0;
while ((cnt = br.read(buffer)) > 0) {
//TODO in a for or while loop?
writer.write(buffer, index, len);
}
}
问题:我现在如何找到索引,以便可以在这些索引之前和之后写出任何内容?
重要的是,缓冲区甚至可能不包含引号。在这种情况下,必须评估缓冲区之前是否包含一个开或关引号,并取决于此写操作还是忽略当前缓冲区。
旁注:出于性能方面的考虑(100GB文件),我想避免在写出内容之前先将内容转换为Strings
。因此,应避免使用reader.readLine()
之类的东西来转换为String
。我当然对此进行了基准测试,发现使用charbuffer
进行的读取是使用reader.readLine()
进行的两倍,这是由于字符串对话引起的!同样适用于Scanner
。
那我为什么认为有更快的方法呢?
因为将我的文件写出如下,比使用bufferedReader.readLine())
读文件快三倍:
int cnt = 0;
while ((cnt = br.read(buffer)) > 0) {
writer.write(buffer, 0, cnt);
}
因此,这简单地将输入写入输出而无需过滤器。 问题是,如何用charbuffer过滤掉引用的文本?
答案 0 :(得分:1)
您是否尝试过最简单的循环(旋转得很热,但希望它能被JIT优化)?
try (BufferedReader br = Files.newBufferedReader(Paths.get("sample.txt"))) {
int c;
boolean inQuotes = false;
while ((c = br.read()) != -1) {
if(c == '"')
inQuotes = !inQuotes;
else if(!inQuotes)
writer.write(c);
}
}
如果不需要多字节字符集支持,则可以改用BufferedInput/OutputStream
,这样可以避免byte -> char -> byte
的转换开销。
答案 1 :(得分:0)
假设您可以容忍在Java内存中实现整个文本文件,则可以只使用一次单行正则表达式替换来删除引用的术语:
String content = readFile("sample.txt", StandardCharsets.UTF_8);
content = content.replaceAll("(?s)\".*?\"", "");
Files.write(Paths.get("sample.txt"), content.getBytes());
如果您不能执行此操作,并且需要逐行阅读,则必须实现自己的解析逻辑才能知道双引号项何时开始以及何时结束。
答案 2 :(得分:0)
使用StreamTokenizer和quoteChar('"')
可能会更好。该类有点陈旧且过时,但是它确实允许您流式传输文件并为您处理令牌化(我想Scanner
也可以处理此令牌,但这可能会带来一些性能上的好处)。
代码将类似于以下内容(可能需要进行微调/附加配置)。
try(StreamTokenizer st = new StreamTokenizer(myBufferedReader)) {
st.quoteChar('"');
while(st.nextToken() != StreamTokenizer.TT_EOF) {
if(st.ttype == '"')
continue;
if(st.ttype == StreamTokenizer.TT_WORD) {
output.write(streamTokenizer.sval);
} else if(st.ttype == StreamTokenizer.TT_EOL) {
output.newLine();
}
}
}