首页 > 解决方案 > Java - Reader 流中的动态字符串替换

问题描述

我在磁盘上有一个(文本)文件,我需要将它读入一个带有 Reader 对象的库。

在阅读此文件时,我想对数据执行正则表达式字符串替换。

我目前的解决方案是将整个文件作为一个字符串读入内存,进行字符串替换,然后为这个字符串创建一个 StringReader 并将其作为 Reader 传递回库中。

这可行,但是对于大文件(尤其是在多个线程中运行),性能是一个问题。

我想做的是让它一次读取文件中的每一行,替换这个子字符串,然后默默地返回给阅读器的消费者——但我想不出该怎么做。

有没有更好的方法来完成这项任务?

我正在使用 Java 7

下面是我当前解决方案的一个示例 - 从“文件”读取,用“b”替换所有“a”,然后将流传递给消费者。

public void loadFile(final File file) throws Exception
{
    final Pattern regexPattern = Pattern.compile("a");
    final String replacementString = "b";

    try (BufferedReader cleanedBufferedReader = new BufferedReader(new StringReader(replaceInBufferedReader(new BufferedReader(new FileReader(file)),
            regexPattern, replacementString))))
    {
        new StreamSource(cleanedBufferedReader).doSomething();
    }
}

private static String replaceInBufferedReader(final BufferedReader reader, final Pattern pattern, final String replacement) throws IOException
{
    final StringBuilder builder = new StringBuilder();
    String str;

    while ((str = reader.readLine()) != null)
    {
        builder.append(str).append(System.lineSeparator());
    }

    return pattern.matcher(builder.toString()).replaceAll(replacement);
}

标签: javaperformancestream

解决方案


您只想继承 BufferedReader。

class MyBufferedReader extends BufferedReader {

    MyBufferedReader(Reader r) {
        super(r);
    }

    @Override
    String readLine() {
        String line = super.readLine();
        // perform replacement here
        return line;
    }

}

像往常一样打开您的文件,但不是将其包装在 BufferedReader 中,而是将其包装在您的子类中。

try ( Reader r = ...;
          BufferedReader br = new MyBufferedReader(r)) {
     String line;
     while ((line = br.readLine()) != null) {
         // use returned line
     }
}

更新

下面是一个Reader允许您逐行替换输入流的方法,同时仍向Reader流的用户呈现一个界面。

在内部,原始流被包装在 a 中BufferedReader,并且一次读取一行。可以对已读取的行执行任何所需的转换。然后将转换后的线变成StringReader. 当流的用户调用任何read(...)操作时,请求被定向到缓冲StringReader来满足。如果StringReader字符用完,BufferedReader则加载并转换 的下一行,以继续为read(...).

abstract public class TranslatingReader extends Reader {

    private BufferedReader input;
    private StringReader output;

    public TranslatingReader(Reader in) {
        input = new BufferedReader(in);
        output = new StringReader("");
    }

    abstract public String translate(String line);

    @Override
    public int read(char[] cbuf, int off, int len) throws IOException {
        int read = 0;

        while (len > 0) {
            int nchars = output.read(cbuf, off, len);
            if (nchars == -1) {
                String line = input.readLine();
                if (line == null) {
                    break;
                }

                line = tranlate(line);

                line += "\n"; // Add the newline which was removed by readLine()
                output = new StringReader(line);
            } else {
                read += nchars;
                off += nchars;
                len -= nchars;
            }
        }

        if (read == 0)
            read = -1;

        return read;
    }

    @Override
    public void close() throws IOException {
        input.close();
        output.close();
    }
}

推荐阅读