[英]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();
}
}
我希望你的文件不是單片的,因為你使用的是字符閱讀 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.