传统的字符替换,我们通常会想到使用 String.replace(),但是这个有些问题。
String.replace()每次替换,都会产生新的字符串,替换5次就产生5个新的字符串。这样空间复杂度就是 O(N*M)。N是字符串大小,M是替换的次数。这种情况下,如果是替换数据量大的字符串就会有内存问题。同样,这种方式也不利于扩展。

这篇文章通过使用自己实现的 TokenReplacingReader 来解决这个问题。

TokenReplacingReader 从标准的 Java.io.Reader 中读取字符数据(继承),所有可以使用 Reader的地方,就都可以使用 TokenReplacingReader 。

接着我们需要一个Token解析接口,用来解析替换字符:ITokenResolver ,整体关系图如下:

0_1507800364462_49929b0a-8c6b-4b52-bcba-76a6b508daad-image.png

TokenReplacingReader 字符串替换用法:

public static void main(String[] args) throws IOException {

    Map<String, String> tokens = new HashMap<String, String>();
    tokens.put("token1", "value1");
    tokens.put("token2", "JJ ROCKS!!!");

    MapTokenResolver resolver = new MapTokenResolver(tokens);

    Reader source =
        new StringReader("1234567890${token1}abcdefg${token2}XYZ$000");

    Reader reader = new TokenReplacingReader(source, resolver);

    int data = reader.read();
    while(data != -1){
        System.out.print((char) data);
        data = reader.read();
    }
}

TokenReplacingReader 流替换,文件替换,字符数组替换以及字符串替换,就可以改成如下的方式:

ITokenResolver resolver = ... ; // get ITokenResolver instance.

Reader reader = new TokenReplacingReader(
        new InputStreamReader(inputStream), resolver);

Reader reader = new TokenReplacingReader(
        new FileReader(new File("c:\\file.txt"), resolver);

Reader reader = new TokenReplacingReader(
        new CharArrayReader(charArray), resolver);

Reader reader = new TokenReplacingReader(
        new StringReader("biiig string...."), resolver);

ITokenResolver 实现:

public interface ITokenResolver {
    public String resolveToken(String tokenName);
}

ITokenResolver 实现:

import java.util.HashMap;
import java.util.Map;

public class MapTokenResolver implements ITokenResolver {

    protected Map<String, String> tokenMap = new HashMap<String, String>();

    public MapTokenResolver(Map<String, String> tokenMap) {
        this.tokenMap = tokenMap;
    }

    public String resolveToken(String tokenName) {
        return this.tokenMap.get(tokenName);
    }

}

TokenReplacingReader实现:

import java.io.IOException;
import java.io.PushbackReader;
import java.io.Reader;
import java.nio.CharBuffer;

public class TokenReplacingReader extends Reader {

    protected PushbackReader pushbackReader = null;
    protected ITokenResolver tokenResolver = null;
    protected StringBuilder tokenNameBuffer = new StringBuilder();
    protected String tokenValue = null;
    protected int tokenValueIndex = 0;

    public TokenReplacingReader(Reader source, ITokenResolver resolver) {
        this.pushbackReader = new PushbackReader(source, 2);
        this.tokenResolver = resolver;
    }

    public int read(CharBuffer target) throws IOException {
        throw new RuntimeException("Operation Not Supported");
    }

    public int read() throws IOException {
        if (this.tokenValue != null) {
            if (this.tokenValueIndex < this.tokenValue.length()) {
                return this.tokenValue.charAt(this.tokenValueIndex++);
            }
            if (this.tokenValueIndex == this.tokenValue.length()) {
                this.tokenValue = null;
                this.tokenValueIndex = 0;
            }
        }

        int data = this.pushbackReader.read();
        if (data != '$') return data;

        data = this.pushbackReader.read();
        if (data != '{') {
            this.pushbackReader.unread(data);
            return '$';
        }
        this.tokenNameBuffer.delete(0, this.tokenNameBuffer.length());

        data = this.pushbackReader.read();
        while (data != '}') {
            this.tokenNameBuffer.append((char) data);
            data = this.pushbackReader.read();
        }

        this.tokenValue = this.tokenResolver
                .resolveToken(this.tokenNameBuffer.toString());

        if (this.tokenValue == null) {
            this.tokenValue = "${" + this.tokenNameBuffer.toString() + "}";
        }
        if (this.tokenValue.length() == 0) {
            return read();
        }
        return this.tokenValue.charAt(this.tokenValueIndex++);


    }

    public int read(char cbuf[]) throws IOException {
        return read(cbuf, 0, cbuf.length);
    }

    public int read(char cbuf[], int off, int len) throws IOException {
        int charsRead = 0;
        for (int i = 0; i < len; i++) {
            int nextChar = read();
            if (nextChar == -1) {
                if (charsRead == 0) {
                    charsRead = -1;
                }
                break;
            }
            charsRead = i + 1;
            cbuf[off + i] = (char) nextChar;
        }
        return charsRead;
    }

    public void close() throws IOException {
        this.pushbackReader.close();
    }

    public long skip(long n) throws IOException {
        throw new RuntimeException("Operation Not Supported");
    }

    public boolean ready() throws IOException {
        return this.pushbackReader.ready();
    }

    public boolean markSupported() {
        return false;
    }

    public void mark(int readAheadLimit) throws IOException {
        throw new RuntimeException("Operation Not Supported");
    }

    public void reset() throws IOException {
        throw new RuntimeException("Operation Not Supported");
    }
}

原文:
http://tutorials.jenkov.com/java-howto/replace-strings-in-streams-arrays-files.html