【问题标题】:Given InputStream replace character and produce OutputStream给定 InputStream 替换字符并生成 OutputStream
【发布时间】:2017-05-09 05:04:51
【问题描述】:

我需要通过替换某些字符将大量文件转换为 CSV。

鉴于 InputStream 返回 OutputStream 并将所有字符 c1 替换为 c2,我正在寻找可靠的方法。

这里的技巧是并行读取和写入,我无法将整个文件放入内存中。

如果我想同时读写,是否需要在单独的线程中运行它?

非常感谢您的建议。

【问题讨论】:

  • 一个 InputStream 给你字节。如果你知道你的编码,你可以使用 Reader 来获取字符。然后,您可以查看每个经过的字符并根据需要进行替换。
  • 是的。谢谢你。我在想是否有任何可用的预构建解决方案?
  • 也许吧。到目前为止,您搜索了什么?

标签: java inputstream outputstream


【解决方案1】:

要将数据从输入流复制到输出流,您在读取数据时写入数据,一次读取一个字节(或字符)或一行。

这是一个读取文件的示例,将所有“x”字符转换为“y”。

BufferedInputStream in = new BufferedInputStream(new FileInputStream("input.dat"));
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream("output.dat"));
int ch;
while((ch = in.read()) != -1) {
        if (ch == 'x') ch = 'y';
        out.write(ch);
}
out.close();
in.close();

或者如果可以使用阅读器并一次处理一行,那么可以使用这种方法:

BufferedReader reader = new BufferedReader(new FileReader("input.dat"));
PrintWriter writer = new PrintWriter(
      new BufferedOutputStream(new FileOutputStream("output.dat")));
String str;
while ((str = reader.readLine()) != null) {
    str = str.replace('x', 'y');     // replace character at a time
    str = str.replace("abc", "ABC"); // replace string sequence
    writer.println(str);
}
writer.close();
reader.close();

BufferedInputStreamBufferedReader 提前读取并将 8K 字符保留在缓冲区中以提高性能。可以处理非常大的文件,而一次只在内存中保留 8K 的字符。

【讨论】:

  • 好的,太好了,谢谢!但是我如何并行读写呢?我无法将整个文件放入内存中。
  • 如果一次处理一个字节或一次处理文件,那么 Java 不会将整个文件放入内存中。上面的 BufferedInputStream 和 BufferedReader 在读取时保留了一个小的内存缓存,因此在读取时只存储了 8K 的文件。不需要并行化该方法,除非文件大小为数 TB,并且想要将文件分成块。
  • 您可以创建一个读取器/写入器作业类来处理特定文件并创建 n 个线程,其中每个线程一次处理一个文件并重复直到完成。
【解决方案2】:
            FileWriter writer = new FileWriter("Report.csv");
            BufferedReader reader = new BufferedReader(new InputStreamReader(YOURSOURCE, Charsets.UTF_8));
            String line;
            while ((line = reader.readLine()) != null) {
                line.replace('c1', 'c2');
                writer.append(line);
                writer.append('\n');
            }
            writer.flush();
            writer.close();

【讨论】:

    【解决方案3】:

    您可以在这里找到相关答案:Filter (search and replace) array of bytes in an InputStream

    我在那个线程中接受了@aioobe 的回答,并用 Java 构建了替换输入流模块,你可以在我的 GitHub gist 中找到它:https://gist.github.com/lhr0909/e6ac2d6dd6752871eb57c4b083799947

    把源代码也放在这里:

    import java.io.FilterInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.Iterator;
    import java.util.LinkedList;
    import java.util.Queue;
    
    /**
     * Created by simon on 8/29/17.
     */
    public class ReplacingInputStream extends FilterInputStream {
    
        private Queue<Integer> inQueue, outQueue;
        private final byte[] search, replacement;
    
        public ReplacingInputStream(InputStream in, String search, String replacement) {
            super(in);
    
            this.inQueue = new LinkedList<>();
            this.outQueue = new LinkedList<>();
    
            this.search = search.getBytes();
            this.replacement = replacement.getBytes();
        }
    
        private boolean isMatchFound() {
            Iterator<Integer> iterator = inQueue.iterator();
    
            for (byte b : search) {
                if (!iterator.hasNext() || b != iterator.next()) {
                    return false;
                }
            }
    
            return true;
        }
    
        private void readAhead() throws IOException {
            // Work up some look-ahead.
            while (inQueue.size() < search.length) {
                int next = super.read();
                inQueue.offer(next);
    
                if (next == -1) {
                    break;
                }
            }
        }
    
        @Override
        public int read() throws IOException {
            // Next byte already determined.
    
            while (outQueue.isEmpty()) {
                readAhead();
    
                if (isMatchFound()) {
                    for (byte a : search) {
                        inQueue.remove();
                    }
    
                    for (byte b : replacement) {
                        outQueue.offer((int) b);
                    }
                } else {
                    outQueue.add(inQueue.remove());
                }
            }
    
            return outQueue.remove();
        }
    
        @Override
        public int read(byte b[]) throws IOException {
            return read(b, 0, b.length);
        }
    
        // copied straight from InputStream inplementation, just needed to to use `read()` from this class
        @Override
        public int read(byte b[], int off, int len) throws IOException {
            if (b == null) {
                throw new NullPointerException();
            } else if (off < 0 || len < 0 || len > b.length - off) {
                throw new IndexOutOfBoundsException();
            } else if (len == 0) {
                return 0;
            }
    
            int c = read();
            if (c == -1) {
                return -1;
            }
            b[off] = (byte)c;
    
            int i = 1;
            try {
                for (; i < len ; i++) {
                    c = read();
                    if (c == -1) {
                        break;
                    }
                    b[off + i] = (byte)c;
                }
            } catch (IOException ee) {
            }
            return i;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2018-12-12
      • 2018-07-19
      • 1970-01-01
      • 2020-09-28
      • 2010-09-08
      • 2011-01-06
      • 2018-02-25
      • 2012-08-03
      相关资源
      最近更新 更多