簡體   English   中英

Java - Reader流中的動態字符串替換

[英]Java - Dynamic String replacement inside a Reader stream

我在磁盤上有一個(文本)文件,我需要將其讀入一個帶有Reader對象的庫中。

在讀取此文件時,我想對數據執行正則表達式字符串替換。

我目前的解決方案是將整個文件作為一個String讀入內存,執行String替換,然后為此String創建一個StringReader並將其作為Reader傳遞回庫中。

這適用於大文件(特別是在多個線程中運行),性能是一個問題。

我想做的是讓它一次從文件中讀取每一行,替換這個子串,然后默默地返回給Reader的消費者 - 但我想不出怎么做。

有沒有更好的方法來完成這項任務?

我使用的是Java 7

我當前解決方案的一個示例如下:從'file'讀取,用'b'替換所有'a',然后將Stream傳遞給消費者。

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

您只想將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接口。

在內部,原始流包裝在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();
    }
}

[編輯] OP編輯了這個問題,因此不再相關

我希望你的文件不是單片的,因為你使用的是字符閱讀 Reader 閱讀 Reader 如果數據不是單片的,那么它必須有一些分隔符將文件分成記錄。 通常這些分隔符是換行符和/或回車符以形成“文本行”記錄。

根據分隔符將數據拆分為記錄,並通過正則表達式傳遞每條記錄。 如果是文本行,您可以使用BufferedReader.readLine()

沒有額外覆蓋的另一個想法是將Scanner與您的模式一起用作自定義分隔符。 這不會立即讀取整個文件,而是在每次迭代時只讀取給定模式的部分。 非常記憶有效。 可能是這樣的(你可以根據自己的需要增強它):

PS關於#performance:我認為這種方法甚至可以比逐行盲讀更高效 有些情況例如:

  • 多行沒有減法,仍在讀取!
  • 文本文件已經(奇怪地)保存為一個大的單行! (沒有\\n s。這可能是由於錯誤導出到文件或在信息檢索期間)

隨意看看這個替代解決方案↓

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM