首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

ReaderToken:支援多重分隔符與萬用字元的串流 Tokenizer 實作

     編輯:Java 相關知識

動機:標準庫的缺口

  • java.util.StringTokenizer:僅支援單一字串、單一分隔符集合,不支援 Reader 串流
  • String.split() / Pattern.split():同樣針對完整字串,需一次載入記憶體
  • 實際場景:大型檔案、網路串流、逐行解析,需邊讀邊切、多種分隔符並行、回傳分隔符本身

ReaderToken 核心設計

類別簽名

public class ReaderToken implements Iterator {
    private Reader reader;           // 輸入串流
    private String[] splits;         // 多組分隔符模式
    private StringBuffer hasRead;    // 已讀未切出的緩衝區
    private String[] splitsSf;       // 實際匹配到的分隔符文本(含萬用字元展開)
    private int currentSplit = -1;   // 當前匹配到的分隔符索引
}

建構子

public ReaderToken(Reader reader, String[] splits) {
    this.reader = reader;
    this.splits = splits;
    this.splitsSf = new String[splits.length];
    this.hasRead = new StringBuffer();
}

核心演算法:indexOf() 逐字元狀態機


private int indexOf() {
    currentSplit = -1;
    int[] pos = new int[splits.length]; // 各分隔符當前匹配位置
    Arrays.fill(pos, 0);
    Arrays.fill(splitsSf, "");

    try {
        while (true) {
            // 檢查是否有分隔符已完整匹配
            for (int j = 0; j < pos.length; j++) {
                if (pos[j] >= splits[j].length()) {
                    currentSplit = j;
                    return currentSplit;
                }
            }

            int read = reader.read();
            if (read == -1 || read == 65535) return currentSplit; // EOF

            char ch = (char) read;
            hasRead.append(ch);

            // 逐分隔符推進狀態機
            for (int i = 0; i < pos.length; i++) {
                char expected = splits[i].charAt(pos[i]);
                if (expected == ch) {
                    pos[i]++;
                    splitsSf[i] += ch;
                } else if (expected == '*') { // 萬用字元:匹配任意字元直到下一個指定字元
                    char next = splits[i].charAt(pos[i] + 1);
                    if (next == ch) {
                        pos[i] += 2;
                    }
                    splitsSf[i] += ch;
                } else if (expected == '?') { // 單一任意字元
                    pos[i]++;
                    splitsSf[i] += ch;
                } else { // 失配:重置該分隔符狀態
                    pos[i] = 0;
                    splitsSf[i] = "";
                }
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    return -1;
}

Iterator 介面實作


public boolean hasNext() {
    return indexOf() > -1;
}

public Object next() {
    int tokenEnd = hasRead.length() - splitsSf[currentSplit].length();
    StringBuffer token = new StringBuffer(hasRead.substring(0, tokenEnd));
    hasRead = new StringBuffer(hasRead.substring(tokenEnd)); // 保留分隔符後的剩餘字元
    return token.toString();
}

public void remove() { /* 不支援 */ }

public String getToken() { return splits[currentSplit]; }           // 原始模式
public String getTokenInReader() { return splitsSf[currentSplit]; } // 實際匹配文本
public Object getEnd() { return hasRead; }                          // 流結束殘留內容

使用範例


String[] patterns = { "a?a", "ERROR:*END" }; // ?=單一任意字元,*=貪婪匹配直到下一字元
String input = "cefaaasdbdERROR: something went wrong ENDmore data";
ReaderToken tokenizer = new ReaderToken(new StringReader(input), patterns);

while (tokenizer.hasNext()) {
    System.out.println("Token: " + tokenizer.next());
    System.out.println("Pattern: " + tokenizer.getToken());
    System.out.println("Matched: " + tokenizer.getTokenInReader());
}
System.out.println("Remainder: " + tokenizer.getEnd());

輸出

Token: cef
Pattern: a?a
Matched: aaa
Token: sdbd
Pattern: ERROR:*END
Matched: ERROR: something went wrong END
Remainder: more data

關鍵特性總結

特性說明
串流處理直接操作 Reader,無需載入完整內容至記憶體
多重分隔符建構子傳入 String[],並行匹配,最先完成者優先
萬用字元*(貪婪直到下一指定字元)、?(單一任意字元)
回傳分隔符getTokenInReader() 取得實際匹配文本,便於後續語意判斷
標準介面實作 Iterator,可直接用於 for (Object t : tokenizer)(需適配器)

擴展建議

  • 支援正則表達式:將 splits 改為 Pattern[],使用 Matcher 逐字元推進(較複雜)
  • 加入 mark/reset 支援:需 PushbackReader 或緩衝區管理
  • 效能優化:hasRead 使用 char[] 環狀緩衝區,避免頻繁 StringBuffer 複製
  • 錯誤處理:拋出自訂 TokenizeException 替代 printStackTrace()

結語

ReaderToken 以狀態機逐字元掃描實現串流分詞,彌補了 JDK 標準庫在「串流、多重分隔符、萬用字元、分隔符回傳」四大維度的缺口。程式碼簡潔、無第三方依賴,適合嵌入式解析器、日誌分析器、協議解碼器等場景直接使用或二次開發。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved