import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* 高性能自定义格式解析器(无正则表达式)
* - 支持任意键名,自动识别 "-key" 后缀进行列表项属性映射
* - 去除 key 和 value 的首尾空格,但 value 内部空格保留
* - 键值对分隔符为 ":= ",列表使用 ":[" ... "]" 包围,列表项使用 "{" ... "}"
* - 所有预定义分隔符(:=, :[, :], :{, :}, :,, :|)前后均可出现空格,解析时自动忽略
* - 提供完整的格式校验,非法输入会抛出 IllegalArgumentException
*/
public class CustomParser {
private final String input;
private int pos;
private final int len;
public CustomParser(String input) {
this.input = input;
this.pos = 0;
this.len = input.length();
}
// 跳过当前位置的空白字符
private void skipSpaces() {
while (pos < len && Character.isWhitespace(input.charAt(pos))) {
pos++;
}
}
// 断言当前位置为指定字符串,否则抛出异常
private void expect(String expected) {
skipSpaces();
if (!input.startsWith(expected, pos)) {
throw new IllegalArgumentException(
String.format("Expected '%s' at position %d, but found '%s'",
expected, pos, pos < len ? input.substring(pos, Math.min(pos + 10, len)) : "<EOF>")
);
}
pos += expected.length();
}
// 检查是否到达结尾或只有空白
private boolean isEndOrWhitespace() {
skipSpaces();
return pos >= len;
}
public Map<String, Object> parse() {
Map<String, Object> result = new LinkedHashMap<>();
while (pos < len) {
skipSpaces();
if (pos >= len) break;
// 查找键值分隔符 :=
int eqIdx = input.indexOf(":=", pos);
if (eqIdx < 0) {
throw new IllegalArgumentException("Missing ':=' at position " + pos);
}
// 提取 key 并去除首尾空格,不允许为空
String key = input.substring(pos, eqIdx).trim();
if (key.isEmpty()) {
throw new IllegalArgumentException("Empty key at position " + pos);
}
pos = eqIdx + 2;
// 解析值(简单值或列表)
Object value = parseValue();
result.put(key, value);
// 跳过键值对分隔符 :|(允许前后空格)
skipSpaces();
if (pos < len) {
if (input.startsWith(":|", pos)) {
pos += 2;
} else {
// 如果还有剩余字符,但不是 :|,则视为非法
throw new IllegalArgumentException(
String.format("Expected ':|' at position %d, but found '%s'",
pos, pos < len ? input.substring(pos, Math.min(pos + 10, len)) : "<EOF>")
);
}
}
}
// ========== 后处理:将所有 "-key" 后缀的键与其前缀进行映射 ==========
List<String> keySuffixKeys = new ArrayList<>();
for (String k : result.keySet()) {
if (k.endsWith("-key")) {
keySuffixKeys.add(k);
}
}
for (String keyWithSuffix : keySuffixKeys) {
String prefix = keyWithSuffix.substring(0, keyWithSuffix.length() - 4);
Object prefixValue = result.get(prefix);
if (prefixValue instanceof List) {
List<?> rawList = (List<?>) prefixValue;
if (!rawList.isEmpty() && rawList.get(0) instanceof List) {
Object keyObj = result.get(keyWithSuffix);
if (!(keyObj instanceof List)) {
throw new IllegalArgumentException("Invalid format for key list: " + keyWithSuffix);
}
List<?> keyList = (List<?>) keyObj;
List<String> fieldNames;
if (keyList.isEmpty()) {
fieldNames = Collections.emptyList();
} else if (keyList.get(0) instanceof List) {
// 只取第一个列表作为字段名定义(规范应只有一项)
fieldNames = (List<String>) keyList.get(0);
} else {
throw new IllegalArgumentException("Invalid field name list in " + keyWithSuffix);
}
List<Map<String, Object>> mappedList = new ArrayList<>();
for (Object item : rawList) {
if (!(item instanceof List)) {
throw new IllegalArgumentException("Invalid list item format for " + prefix);
}
List<?> itemValues = (List<?>) item;
if (fieldNames.size() != itemValues.size()) {
throw new IllegalArgumentException(
String.format("Value count mismatch for key '%s': expected %d fields but got %d",
prefix, fieldNames.size(), itemValues.size())
);
}
Map<String, Object> map = new LinkedHashMap<>();
for (int i = 0; i < fieldNames.size(); i++) {
map.put(fieldNames.get(i), itemValues.get(i));
}
mappedList.add(map);
}
result.put(prefix, mappedList);
}
}
}
return result;
}
/**
* 解析一个值(简单字符串或列表)
*/
private Object parseValue() {
skipSpaces();
if (pos < len && input.startsWith(":[", pos)) {
return parseList();
} else {
// 普通值,直到 :| 或结尾
int end = input.indexOf(":|", pos);
if (end < 0) {
end = len;
}
String value = input.substring(pos, end).trim();
pos = end;
return value;
}
}
/**
* 解析列表,返回 List<List<String>>
* 每个子列表代表一个项的所有值(已去除首尾空格,内部空格保留)
*/
private List<List<String>> parseList() {
// 跳过 ":["
pos += 2;
// 跳过空格,然后查找 "]"
skipSpaces();
int posEnd = input.indexOf(":]", pos);
if (posEnd < 0) {
throw new IllegalArgumentException("Missing ':]' at position " + pos);
}
String listData = input.substring(pos, posEnd);
List<List<String>> lists = parseListData(listData);
pos = posEnd + 2; // 跳过 :]
return lists;
}
/**
* 解析列表数据(不含外层 :[ 和 :])
* 严格校验:必须包含一个或多个项,每个项以 ":{ " 开始,以 ":}" 结束。
*/
private List<List<String>> parseListData(String s) {
List<List<String>> result = new ArrayList<>();
if (s == null || s.isEmpty()) {
// 空列表是合法的,返回空结果
return result;
}
int i = 0;
int len = s.length();
while (i < len) {
// 跳过项前的空格
while (i < len && Character.isWhitespace(s.charAt(i))) i++;
if (i >= len) break;
// 必须出现项开始标记 :{
if (!s.startsWith(":{", i)) {
throw new IllegalArgumentException(
String.format("Expected ':{' at position %d (relative to list content), found '%s'",
i, i < len ? s.substring(i, Math.min(i + 10, len)) : "<EOF>")
);
}
i += 2; // 跳过 :{
// 跳过项内字段前的空格
while (i < len && Character.isWhitespace(s.charAt(i))) i++;
// 查找项结束标记 :}
int endMark = s.indexOf(":}", i);
if (endMark < 0) {
throw new IllegalArgumentException("Missing ':}' at position " + i + " in list content");
}
// 解析该行内的字段
List<String> fields = new ArrayList<>();
int fieldStart = i;
while (fieldStart < endMark) {
// 跳过字段前的空格
while (fieldStart < endMark && Character.isWhitespace(s.charAt(fieldStart))) {
fieldStart++;
}
if (fieldStart >= endMark) break;
// 查找字段分隔符 :,
int commaIdx = s.indexOf(":,", fieldStart);
if (commaIdx < 0 || commaIdx >= endMark) {
// 最后一个字段
String field = s.substring(fieldStart, endMark).trim();
fields.add(field);
break;
} else {
String field = s.substring(fieldStart, commaIdx).trim();
// 允许空字段(即字段内容为空),但分隔符必须存在
fields.add(field);
fieldStart = commaIdx + 2; // 跳过 :,
}
}
result.add(fields);
i = endMark + 2; // 跳过 :}
}
// 如果还有剩余字符(非空格),则视为非法
while (i < len && Character.isWhitespace(s.charAt(i))) i++;
if (i < len) {
throw new IllegalArgumentException(
String.format("Unexpected characters after last item at position %d: '%s'",
i, s.substring(i, Math.min(i + 10, len)))
);
}
return result;
}
// ========== 测试 ==========
public static void main(String[] args) {
String input = " bank:= 012:| details := :[ :{2:,0.063:,GA:} :{2:,0.063:,GA:}:]:|details-key:=:[:{stockValue:,stockItemValue:,moneyType:}:]:|bank2:= 012:|";
Map<String, Object> result = new CustomParser(input).parse();
for (Map.Entry<String, Object> entry : result.entrySet()) {
System.out.println(entry.getKey() + "->" + entry.getValue());
}
}
}
public class StateMachineParser {
private final String input;
private int pos;
private final int len;
private char ch;
public StateMachineParser(String input) {
this.input = input;
this.len = input.length();
this.pos = 0;
this.ch = len > 0 ? input.charAt(0) : 0;
}
private void nextChar() {
pos++;
ch = pos < len ? input.charAt(pos) : 0;
}
private void skipWhitespace() {
while (pos < len && Character.isWhitespace(ch)) {
nextChar();
}
}
private boolean matchNextTwo(char c1, char c2) {
return pos + 1 < len && ch == c1 && input.charAt(pos + 1) == c2;
}
private void error(String msg) {
throw new RuntimeException("Syntax error at position " + pos + ": " + msg);
}
public Map<String, Object> parse() {
Map<String, Object> result = new LinkedHashMap<>();
final int START = 0, KEY = 1, AFTER_KEY = 2,
SIMPLE_VALUE = 3, LIST_START = 4, LIST_ITEM = 5;
int state = START;
StringBuilder keyBuf = new StringBuilder();
StringBuilder valueBuf = new StringBuilder();
List<List<String>> currentList = null;
List<String> currentItem = null;
String currentKey = null;
while (pos < len) {
switch (state) {
case START:
skipWhitespace();
if (pos >= len) break;
keyBuf.setLength(0);
state = KEY;
break;
case KEY:
if (matchNextTwo(':', '=')) {
currentKey = keyBuf.toString().trim();
if (currentKey.isEmpty()) {
error("Key cannot be empty before ':='");
}
nextChar(); nextChar();
state = AFTER_KEY;
} else {
if (ch == ':' && pos + 1 < len && input.charAt(pos + 1) != '=') {
error("Unexpected ':' in key (expected ':=')");
}
keyBuf.append(ch);
nextChar();
}
break;
case AFTER_KEY:
skipWhitespace();
if (pos >= len) {
error("Unexpected end after key '" + currentKey + "'");
}
if (matchNextTwo(':', '[')) {
nextChar(); nextChar();
currentList = new ArrayList<>();
state = LIST_START;
} else {
valueBuf.setLength(0);
state = SIMPLE_VALUE;
}
break;
case SIMPLE_VALUE:
if (matchNextTwo(':', '|')) {
String val = valueBuf.toString().trim();
result.put(currentKey, val);
nextChar(); nextChar();
state = START;
} else if (matchNextTwo(':', '[') || matchNextTwo(':', '{') ||
matchNextTwo(':', ']') || matchNextTwo(':', '}') ||
matchNextTwo(':', ',')) {
error("Unexpected sequence ':" + input.charAt(pos + 1) +
"' inside simple value");
} else {
valueBuf.append(ch);
nextChar();
if (pos >= len) {
result.put(currentKey, valueBuf.toString().trim());
state = START;
}
}
break;
case LIST_START:
skipWhitespace();
if (pos >= len) {
error("List for key '" + currentKey + "' started but never closed with ':]'");
}
if (matchNextTwo(':', ']')) {
nextChar(); nextChar();
result.put(currentKey, currentList);
skipWhitespace();
if (pos < len) {
if (matchNextTwo(':', '|')) {
nextChar(); nextChar();
state = START;
} else {
error("Expected ':|' after list closing ':]' because more data follows");
}
} else {
state = START;
}
} else if (matchNextTwo(':', '{')) {
nextChar(); nextChar();
currentItem = new ArrayList<>();
state = LIST_ITEM;
} else {
error("Expected ':{' or ':]' in list for key '" + currentKey +
"', found '" + (ch != 0 ? ch : "EOF") + "'");
}
break;
case LIST_ITEM:
valueBuf.setLength(0);
while (pos < len) {
if (matchNextTwo(':', ',')) {
currentItem.add(valueBuf.toString().trim());
nextChar(); nextChar();
break;
} else if (matchNextTwo(':', '}')) {
currentItem.add(valueBuf.toString().trim());
nextChar(); nextChar();
currentList.add(currentItem);
state = LIST_START;
break;
} else if (matchNextTwo(':', '[') || matchNextTwo(':', ']') ||
matchNextTwo(':', '{') || matchNextTwo(':', '|')) {
error("Unexpected sequence ':" + input.charAt(pos + 1) +
"' inside list item (expected ':,' or ':}')");
} else {
valueBuf.append(ch);
nextChar();
}
}
if (pos >= len && state == LIST_ITEM) {
error("List item not terminated with ':}' for key '" + currentKey + "'");
}
break;
default:
throw new IllegalStateException("Invalid state: " + state);
}
}
// ---------- 后处理:严格校验并合并 "-key" 与列表 ----------
List<String> suffixKeys = new ArrayList<>();
for (String k : result.keySet()) {
if (k.endsWith("-key")) suffixKeys.add(k);
}
for (String keyWithSuffix : suffixKeys) {
String prefix = keyWithSuffix.substring(0, keyWithSuffix.length() - 4);
Object prefixVal = result.get(prefix);
Object suffixVal = result.get(keyWithSuffix);
// 必须两者都是 List
if (!(prefixVal instanceof List) || !(suffixVal instanceof List)) {
continue; // 按理不会发生,但可跳过
}
List<?> rawList = (List<?>) prefixVal;
List<?> keyList = (List<?>) suffixVal;
// 情况1:数据非空,但字段定义列表为空 -> 报错
if (!rawList.isEmpty() && keyList.isEmpty()) {
throw new RuntimeException("Key definition for '" + prefix + "' is empty but data exists");
}
// 情况2:数据为空,则无需合并,直接移除 -key 条目(可选)
if (rawList.isEmpty()) {
result.remove(keyWithSuffix);
continue;
}
// 此时 rawList 非空,keyList 也非空
// 检查 rawList 内部元素是否为 List
if (!(rawList.get(0) instanceof List)) {
throw new RuntimeException("Data list for '" + prefix + "' is not a list of lists");
}
// 检查 keyList 内部元素是否为 List
if (!(keyList.get(0) instanceof List)) {
throw new RuntimeException("Key definition list for '" + prefix + "' is not a list of lists");
}
@SuppressWarnings("unchecked")
List<String> fieldNames = (List<String>) keyList.get(0);
// 字段名列表不能为空
if (fieldNames.isEmpty()) {
throw new RuntimeException("Field name list for '" + prefix + "' is empty");
}
@SuppressWarnings("unchecked")
List<List<String>> rows = (List<List<String>>) rawList;
// 校验每一行的字段数
for (int i = 0; i < rows.size(); i++) {
List<String> row = rows.get(i);
if (row.size() != fieldNames.size()) {
throw new RuntimeException("Data row " + i + " has " + row.size() +
" fields, but key definition has " + fieldNames.size() +
" fields for key '" + prefix + "'");
}
}
// 转换
List<Map<String, Object>> mappedList = new ArrayList<>(rows.size());
for (List<String> row : rows) {
Map<String, Object> map = new LinkedHashMap<>();
for (int i = 0; i < fieldNames.size(); i++) {
map.put(fieldNames.get(i), row.get(i));
}
mappedList.add(map);
}
result.put(prefix, mappedList);
result.remove(keyWithSuffix); // 移除原始的 -key 条目
}
return result;
}
// ========== 测试 ==========
public static void main(String[] args) {
// 原测试(合法)
String input1 = " bank:= 012:|details:=:[:{2:,0.063:,GA:}:{2:,0.063:,GA:}:]:|details-key:=:[:{stockValue:,stockItemValue:,moneyType:}:]";
for (Map.Entry<String, Object> entry : new StateMachineParser(input1).parse().entrySet()) {
System.out.println(entry.getKey() + "->" + entry.getValue());
}
}
}