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

Java-动态字符串替换在一个阅读器流

拓拔嘉运
2023-03-14

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

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

我目前的解决方案是将整个文件作为一个String读取到内存中,进行String替换,然后为该String创建一个StringReader,并将其作为Reader传回库。

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

我想做的是让它一次读取文件中的每一行,在这个子字符串中替换,然后静静地返回给阅读器的使用者——但我想不出如何做到这一点。

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

我正在使用Java7

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

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);
}

共有3个答案

吉和同
2023-03-14

没有额外覆盖的另一个想法是使用扫描仪和您的模式作为自定义分隔符。这不会一次读取整个文件,而是在每次迭代中只读取给定模式下的部分。非常有效的记忆。可以是这样的(您可以根据自己的需要进行增强):

关于性能的PS:我认为这种方法甚至比逐行盲读更有效!有些情况下,例如:

  • 在多行中没有子段落,但仍在阅读它们

随意看看这个替代解决方案↓

    private static String replaceInBufferedReader(String pathToFile){

    File some = new File("some.txt");
    StringBuilder sb = new StringBuilder();
    String replacementString = "b";
    String delimiter = "x";    // you can use pattern or regex

    try {
        // set Scanner's delimiter to the pattern you wanna replace 
        Scanner sc = new Scanner(some).useDelimiter(delimiter);        

        while (sc.hasNext()) {
            sb.append(sc.next()).append(replacementString);
        }
        sc.close();
    }
    catch (FileNotFoundException e) {
        e.printStackTrace();
    }
    return sb.toString();  // or maybe save to new file
}

我用一个8MB的文本文件对它进行了测试,这对它来说是小菜一碟。我用一个Writer将它保存回一个新文件,而不是返回sb。toString()

...
try {
    Files.write(Paths.get("some2.txt"),
            sb.toString().getBytes(),
            StandardOpenOption.CREATE);
    }
    catch (IOException e) {
        e.printStackTrace();
    }
龙永逸
2023-03-14

我希望您的文件不是单片的,因为您使用的是字符读取器。如果数据不是单片的,它必须有一些分隔符,将文件分割成记录。通常这些分隔符是换行符和/或回车符,以形成“文本行”记录。

根据分隔符将数据拆分为记录,并通过正则表达式传递每条记录。对于文本行,您可以使用BufferedReader。readLine()

须新
2023-03-14

您只需要创建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
     }
}

使现代化

下面是一个读卡器,它允许您逐行替换输入流,同时仍然向流的用户提供读卡器界面。

在内部,原始流被包装在一个BufferedReader中,并一次读取一行。可以对已读取的行执行任何期望的转换。然后将转换后的行转换为StringReader。当流的用户调用任何read(…) 操作,请求被定向到缓冲的StringReader以满足。如果StringReader的字符不足,则加载并转换BufferedReader的下一行,以继续为读取(…)提供输入

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();
    }
}

 类似资料:
  • 问题内容: 假设我们有类似的东西: 我想将“ someText”替换为其他内容。考虑到我不知道someText可能是什么(任何字符串)并且我所知道的是它将被&firstString =和&endString =包围的事实,最佳方法是什么? 编辑:对不起,看起来这还不够清楚。我不知道“ someText”可能是什么,我仅有的信息是它将位于&firstString =和&endString =之间 我

  • 问题内容: 在python中,字符串可变吗?该行引发错误 TypeError:’str’对象不支持项目分配 我可以看到原因(因为我可以编写someString [3] =“ test”,这显然是非法的),但是在python中有没有这样做的方法? 问题答案: Python字符串是不可变的,这意味着它们不支持项目或切片分配。您将必须使用ie或其他合适的方法来构建新的字符串。

  • 问题内容: 我有以下数据框 我想更换和使用,所以最终的数据帧 我尝试了以下操作,但没有成功: 问题答案: 解决方案与通过: 如果需要将列中的所有值设置为一些:

  • 问题内容: 在JavaScript中有一个简单的等效项吗? 这是使用PHP的,它允许您使用单词数组来查找和替换。我可以使用JavaScript / jQuery做类似的事情吗? 问题答案: 您可以使用自己的函数来扩展String对象,该函数可以满足您的需要(如果缺少功能,则很有用): 对于全局替换,您可以使用正则表达式: 要使用该功能,它将类似于您的PHP示例:

  • 问题内容: 假设我有以下代码: 这段代码运行后,价值会 如果我以相反的顺序替换它们,则会发生类似的问题: 的值将是 我的目标是把成我怎么能做到呢? 问题答案: 使用Apache Commons StringUtils中的方法:

  • 我有一个PHP脚本这是一个字符串替换函数,它接受数组中的字符,如果在字符串中找到任何字符,就替换它们。是否有与该函数等价的java函数。我找到了一些方法,但有些是使用循环,有些是重复语句,但在Java中没有找到类似的单行解决方案。