package com.taobao.loganalyzer.input.tanxclick.common; /** * 日志解析工具,传入一条日志,利用特定的分隔符进行字段分割 * @author liyuntao * */ public class LogParser { /** * 字段分隔符CTRL+A */ public static final char CONTROL_A = '\u0001'; /** * 字段分隔符CTRL+B */ public static final char CONTROL_B = '\u0002'; /** * 字段分隔符CTRL+C */ public static final char CONTROL_C = '\u0003'; /** * 字段分隔符CTRL+D */ public static final char CONTROL_D = '\u0004'; private String log = null; private int pos = 0; private int length = 0; private boolean error = false; private String errMsg = null; private char matchedChar = 0; /** * 构造一个从日志某一特定位置开始解析的LogParser * @param log 需要解析的日志 * @param pos 解析的开始位置 */ public LogParser(String log, int pos) { this.log = log; this.pos = pos; this.length = log.length(); } /** * 构造一个从日志起始位置开始解析的LogParser * @param log 需要解析的日志 */ public LogParser(String log) { this(log, 0); } /** * 判断当前字符是否是CTRL+A * @return true为是,false为不是 */ public boolean isCurCharCA() { if (pos >= length) return false; return log.charAt(pos) == CONTROL_A; } /** * 获取当前字符 * @return 返回当前位置解析的字符 */ public char curChar() { if (pos >= length) return 0; return log.charAt(pos); } /** * 获取{@link #getNextField(char[] ch)}中匹配的字符 * @return 匹配的字符 */ public char getMatchedChar() { return matchedChar; } /** * 将解析的指针指向下一个字符,并判断该字符是否存在 * @return true为有,false为没有 */ public boolean hasNextChar() { return ++pos < length; } /** * 未进行解析的日志中是否还有ch存在 * @param ch 需要判断是否存在的字符 * @return true为有,false为没有 */ public boolean hasNextField(char ch) { return !(log.indexOf(ch, pos)==-1); } /** * 未进行解析的日志中是否还有CTRL+A存在 * @return true为有,false为没有 */ public boolean hasNextCA() { return hasNextField(CONTROL_A); } /** * 跳过下一个以ch为分隔符的字段 * @param ch 分隔符 * @return true为成功,false为解析失败 */ public boolean skipNextField(char ch) { int index = pos; pos = log.indexOf(ch, pos); if (pos != -1) { pos ++; // skip the ch return true; } if (error == false) { error = true; errMsg = new StringBuilder().append("error: expected ").append(ch) .append(" from ").append(index).toString(); } pos = length + 1; // error happens return false; } /** * 跳过下一个以CTRL+A分隔的字段 * @return true为成功,false为解析失败 */ public boolean skipNextFieldCA() { return skipNextField(CONTROL_A); } /** * 获取下一个以ch中的任一个元素为分隔符的字段,可通过{@link #getMatchedChar()}获取匹配的分隔符 * @param ch 分隔符的数组 * @return 下一个以ch中的任一个元素为分隔符的字段 */ public String getNextField(char[] ch) { int index = pos; int i = 0; char c; matchedChar = 0; while (pos < length) { c = log.charAt(pos); for (i = 0; i < ch.length && ch[i] != c; i++); if (i == ch.length) pos ++; else { matchedChar = c; return log.substring(index, pos++); // skip the ch } } if (error == false) { error = true; errMsg = new StringBuilder().append("error: expected ").append(ch) .append(" from ").append(index).toString(); } pos = length + 1; // error happens return null; } /** * 获取下一个以ch分隔的字段 * @param ch 分隔符 * @return 下一个以ch分隔的字段,null为解析出错 */ public String getNextField(char ch) { int index = pos; pos = log.indexOf(ch, pos); if (pos != -1) return log.substring(index, pos++); // skip the ch if (error == false) { error = true; errMsg = new StringBuilder().append("error: expected ").append(ch) .append(" from ").append(index).toString(); } pos = length + 1; // error happens return null; } /** * 获取下一个以CTRL+A分隔的字段 * @return 下一个以CTRL+A分隔的字段 */ public String getNextCA() { String field = getNextField(new char[]{CONTROL_A, CONTROL_B}); if (getMatchedChar() != CONTROL_A) { if (error == false) { error = true; errMsg = new StringBuilder().append("error: expected ").append(CONTROL_B) .append(" from ").append(pos).toString(); } pos = length + 1; // error happens return null; } return field; } /** * 获取下一个以CTRL+B分隔的字段 * @return 下一个以CTRL+B分隔的字段 */ public String getNextCB() { String field = getNextField(new char[]{CONTROL_A, CONTROL_B}); if (getMatchedChar() != CONTROL_B) { if (error == false) { error = true; errMsg = new StringBuilder().append("error: expected ").append(CONTROL_B) .append(" from ").append(pos).toString(); } pos = length + 1; // error happens return null; } return field; } /** * 获取下一个以CTRL+C分隔的字段 * @return 下一个以CTRL+C分隔的字段 */ public String getNextCC() { String field = getNextField(new char[]{CONTROL_A, CONTROL_B, CONTROL_C}); if (getMatchedChar() != CONTROL_C) { if (error == false) { error = true; errMsg = new StringBuilder().append("error: expected ").append(CONTROL_C) .append(" from ").append(pos).toString(); } pos = length + 1; // error happens return null; } return field; } /** * 获取下一个以CTRL+D分隔的字段 * @return 下一个以CTRL+D分隔的字段 */ public String getNextCD() { String field = getNextField(new char[]{CONTROL_A, CONTROL_B, CONTROL_C, CONTROL_D}); if (getMatchedChar() != CONTROL_D) { if (error == false) { error = true; errMsg = new StringBuilder().append("error: expected ").append(CONTROL_D) .append(" from ").append(pos).toString(); } pos = length + 1; // error happens return null; } return field; } /** * 获取剩下的所有未解析的日志 * @return 为解析的日志,null为已经解析出错 */ public String getAllRemained() { if (pos < length) return log.substring(pos); return null; } /** * 获取当前解析的整条日志 * @return 解析的日志 */ public String getLog() { return log; } /** * 获取出错时的相关状态信息 * @return 出错信息 */ public String getErrMsg() { return errMsg; } /** * 当前解析的状态,是否已经发现日志不符合格式 * @return true为解析出错,false为解析正常 */ public boolean isError() { return error; } }