You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Java正则从字符串读取函数式命令?有哪些替代方案?

嵌套命令结构的解析方案

为什么正则表达式不适用

正则表达式基于有限状态机,只能处理非嵌套的结构化文本。对于你描述的多层嵌套{}结构(比如go(){do(){go}}),正则无法跟踪嵌套的层级深度,根本无法正确解析这种上下文无关的语法结构。所以别在正则上浪费时间,正经的解析得用语法分析方法。

推荐实现:递归下降解析器

这种嵌套结构属于上下文无关文法(CFG),递归下降解析器是最适合的轻量实现方式,手写成本不高,逻辑清晰,完全匹配你的需求。

步骤1:定义文法规则

先把你的命令结构转换成明确的文法规则,比如:

Program → 命令+ (多个命令组成程序)
命令 → 标识符 ( 参数列表? ) { 命令+ }? (命令可以带参数和可选的嵌套命令体)
参数列表 → 参数 (, 参数)* (多个参数用逗号分隔)
参数 → 布尔值 | 数字 | 命令 (参数可以是基本类型,也可以是嵌套的命令)
布尔值 → "true" | "false"
数字 → 整数或浮点数
标识符 → 字母下划线开头的字母数字下划线组合

步骤2:词法分析(Tokenizer)

先把输入字符串拆分成一个个最小的语法单元(Token),比如标识符、括号、逗号、布尔值、数字等。这一步可以用正则辅助拆分单个Token(注意不是解析整体嵌套)。

示例Python代码:

import re

# 定义Token规则:(正则模式, Token类型)
TOKEN_PATTERNS = [
    (r'[a-zA-Z_][a-zA-Z0-9_]*', 'IDENTIFIER'),
    (r'true|false', 'BOOLEAN'),
    (r'\d+\.?\d*|\.\d+', 'NUMBER'),
    (r'\(', 'LPAREN'),
    (r'\)', 'RPAREN'),
    (r'\{', 'LBRACE'),
    (r'\}', 'RBRACE'),
    (r',', 'COMMA'),
    (r'\s+', None),  # 忽略空白字符
]

def tokenize(input_str):
    tokens = []
    pos = 0
    input_len = len(input_str)
    while pos < input_len:
        match = None
        for pattern, token_type in TOKEN_PATTERNS:
            regex_match = re.match(pattern, input_str[pos:])
            if regex_match:
                if token_type:
                    tokens.append((token_type, regex_match.group(0)))
                pos += regex_match.end()
                break
        if not match:
            raise ValueError(f"无效字符在位置 {pos}: {input_str[pos]}")
    return tokens

步骤3:语法分析(Parser)

基于Token流,用递归的方式按照文法规则构建抽象语法树(AST),每个节点对应你说的继承自同一抽象类的类实例。

示例Python代码:

from abc import ABC, abstractmethod

# 抽象命令类,所有命令都继承它
class BaseCommand(ABC):
    pass

# 具体命令实现类
class Command(BaseCommand):
    def __init__(self, name: str, args: list, body: list[BaseCommand] = None):
        self.name = name
        self.args = args  # 参数列表:元素可以是基本类型或BaseCommand实例
        self.body = body  # 命令体:嵌套的命令列表

class CommandParser:
    def __init__(self, tokens):
        self.tokens = tokens
        self.current_idx = 0

    def _peek(self):
        """查看当前Token,不移动指针"""
        if self.current_idx < len(self.tokens):
            return self.tokens[self.current_idx]
        return None

    def _consume(self):
        """移动到下一个Token"""
        self.current_idx += 1

    def parse_program(self) -> list[BaseCommand]:
        """解析整个程序,返回命令列表"""
        commands = []
        while self._peek():
            commands.append(self._parse_command())
        return commands

    def _parse_command(self) -> Command:
        """解析单个命令"""
        # 解析命令名(标识符)
        if self._peek()[0] != 'IDENTIFIER':
            raise SyntaxError("命令必须以标识符开头")
        cmd_name = self._peek()[1]
        self._consume()

        # 解析左括号
        if self._peek()[0] != 'LPAREN':
            raise SyntaxError(f"命令 {cmd_name} 后必须跟 (")
        self._consume()

        # 解析参数列表
        args = []
        if self._peek() and self._peek()[0] != 'RPAREN':
            args = self._parse_arg_list()

        # 解析右括号
        if self._peek()[0] != 'RPAREN':
            raise SyntaxError("参数列表必须以 ) 结束")
        self._consume()

        # 解析可选的命令体
        body = None
        if self._peek() and self._peek()[0] == 'LBRACE':
            self._consume()
            body = []
            while self._peek() and self._peek()[0] != 'RBRACE':
                body.append(self._parse_command())
            if not self._peek() or self._peek()[0] != 'RBRACE':
                raise SyntaxError("命令体必须以 } 结束")
            self._consume()

        return Command(cmd_name, args, body)

    def _parse_arg_list(self) -> list:
        """解析参数列表"""
        args = [self._parse_arg()]
        while self._peek() and self._peek()[0] == 'COMMA':
            self._consume()
            args.append(self._parse_arg())
        return args

    def _parse_arg(self):
        """解析单个参数"""
        token = self._peek()
        if not token:
            raise SyntaxError("参数不能为空")
        
        if token[0] == 'BOOLEAN':
            self._consume()
            return token[1] == 'true'
        elif token[0] == 'NUMBER':
            self._consume()
            num_str = token[1]
            return float(num_str) if '.' in num_str else int(num_str)
        elif token[0] == 'IDENTIFIER':
            # 参数是嵌套命令,递归解析
            return self._parse_command()
        else:
            raise SyntaxError(f"不支持的参数类型:{token}")

使用示例

input_str = "go(true, 4){do(), hello(){go(){}}}"
tokens = tokenize(input_str)
parser = CommandParser(tokens)
command_tree = parser.parse_program()

# 现在可以遍历command_tree,处理每个Command实例
for cmd in command_tree:
    print(f"命令名:{cmd.name},参数:{cmd.args}")
    if cmd.body:
        print(f"嵌套命令:{[c.name for c in cmd.body]}")

替代方案

如果不想手写解析器,可以用现成的工具降低开发成本:

  • 解析器生成器:比如ANTLR(跨语言)、PLY(Python版Lex-Yacc),只需定义文法规则,工具会自动生成词法和语法分析器,适合更复杂的语法扩展。
  • PEG解析库:比如Python的lark、parsimonious,PEG文法比CFG更灵活,语法定义更简洁,直接写文法就能生成解析器,上手快。

内容的提问来源于stack exchange,提问作者Mehranjp73

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 02:35:23