0

0

如何安全高效地解析并验证字典中键值对是否满足用户自定义的复合比较条件

花韻仙語

花韻仙語

发布时间:2026-03-15 14:17:29

|

708人浏览过

|

来源于php中文网

原创

如何安全高效地解析并验证字典中键值对是否满足用户自定义的复合比较条件

本文介绍一种基于正则表达式与 operator 模块的安全解析方案,无需 eval,可处理多操作符链式比较(如 0 < (cd) < 6)、混合数据类型(数值、字符串、布尔、None)及嵌套括号键名,适用于动态搜索场景。

本文介绍一种基于正则表达式与 operator 模块的安全解析方案,无需 eval,可处理多操作符链式比较(如 `0

在实际开发中,常需根据用户输入的自然语言风格条件(如 'a > 5, 0 < (cd) < 6, ef < 35')动态校验字典数据是否全部满足。直接使用 eval() 虽简洁但存在严重安全风险(任意代码执行),而手动逐字符解析又易出错、难以维护。本文提供一个安全、健壮、可扩展的解决方案:利用 re 拆分操作符、operator 统一调用比较逻辑,并支持链式比较与多类型自动推导。

核心设计思路

  • 零 eval 风险:完全避免 eval() 或 exec(),所有操作符映射到 operator 模块的纯函数;
  • 支持链式比较:如 35 == ef != 34 > 12 < 20 被自动拆解为 (35 == ef) and (ef != 34) and (34 > 12) and (12 < 20);
  • 智能类型推导:自动识别整数、浮点数、字符串(带引号)、布尔值(true/false)和 None;
  • 兼容特殊键名:支持含括号、符号等非标准标识符的键(如 '(cd)'),通过 dict.get(key) 安全访问;
  • 错误防御强:非法操作符、不匹配的值-操作符数量、无法解析的字面量均抛出明确异常。

完整实现代码

import operator
import re

# 支持的操作符及其对应函数
OPERATORS = {
    '<=': operator.le,
    '>=': operator.ge,
    '>':  operator.gt,
    '<':  operator.lt,
    '==': operator.eq,
    '!=': operator.ne,
}

# 构建正则表达式:匹配任意已注册操作符(转义特殊字符)
_oper_pattern = '|'.join(re.escape(op) for op in OPERATORS.keys())
OPER_REGEX = re.compile(rf'\s*({_oper_pattern})\s*')
NUMBER_REGEX = re.compile(r'-?\d*\.?\d+(?:[eE][+-]?\d+)?')  # 支持科学计数法
STRING_REGEX = re.compile(r'''(["'])(?P<content>.*?)\1''')

def cast_literal(value: str) -> int | float | str | bool | None:
    """将字符串字面量转换为对应 Python 类型"""
    v = value.strip()
    if not v:
        return None
    # 布尔值
    if v.lower() in ('true', 'false'):
        return v.lower() == 'true'
    # None
    if v.lower() == 'none':
        return None
    # 字符串(带引号)
    if match := STRING_REGEX.match(v):
        return match.group('content')
    # 数字(整数/浮点数/科学计数法)
    if NUMBER_REGEX.fullmatch(v):
        return float(v) if '.' in v or 'e' in v.lower() else int(v)
    # 默认返回原字符串(如未加引号的标识符,后续由 dict.get 处理)
    return v

def check_conditions(data: dict, conditions: str) -> bool:
    """
    校验字典 data 是否满足所有 conditions 中的逻辑条件

    Args:
        data: 待校验的字典
        conditions: 逗号分隔的条件字符串,如 'a > 5, (cd) < 6, ef == 35'

    Returns:
        bool: 所有条件均成立返回 True,否则 False

    Raises:
        ValueError: 条件格式非法(如操作符与值数量不匹配)
        KeyError: 条件中引用了字典不存在的键(且未加引号)
    """
    results = []

    for cond in conditions.split(','):
        cond = cond.strip()
        if not cond:
            continue

        # 按操作符分割(保留操作符)
        parts = [p.strip() for p in OPER_REGEX.split(cond) if p.strip()]

        # 提取值与操作符
        values, ops = [], []
        for part in parts:
            if part in OPERATORS:
                ops.append(OPERATORS[part])
            else:
                # 尝试从字典取值;若失败,则尝试解析为字面量
                key_or_literal = cast_literal(part)
                if isinstance(key_or_literal, str) and key_or_literal in data:
                    values.append(data[key_or_literal])
                else:
                    values.append(key_or_literal)

        # 验证结构:n 个操作符需 n+1 个值
        if len(values) != len(ops) + 1:
            raise ValueError(f"Invalid condition format: '{cond}' → "
                           f"expected {len(ops)+1} values, got {len(values)}")

        # 执行链式比较:v0 op0 v1, v1 op1 v2, ..., v_{n-1} op_{n-1} v_n
        chain_ok = True
        for i in range(len(ops)):
            try:
                if not ops[i](values[i], values[i + 1]):
                    chain_ok = False
                    break
            except TypeError as e:
                raise TypeError(f"Type error in '{cond}': {e}") from e

        results.append(chain_ok)

    return all(results)

# 使用示例
if __name__ == "__main__":
    sample_dict = {
        'a': 25,
        'ab': 3.3,
        '(cd)': 4,
        'ef': 35,
        'gh': 12.2,
        'ij': "hello",
        'kl': False,
        'mn': None
    }

    # 测试用例(全部应返回 True)
    test_cases = [
        'a > 5',                          # 单条件
        '0 < (cd) < 6',                   # 链式比较(注意:此处 0 和 6 是字面量)
        '35 == ef != 34 > 12 < 20',       # 复杂链式
        'ij == "hello"',                  # 字符串比较
        'kl == false, mn == none',        # 布尔与 None
        'a > 5, (cd) < 6, ef < 35',       # 多条件组合 → 注意:ef < 35 为 False,整体为 False
    ]

    for i, case in enumerate(test_cases, 1):
        result = check_conditions(sample_dict, case)
        print(f"Test {i}: '{case}' → {result}")

关键注意事项

  • ? 绝不使用 eval:本方案通过显式操作符映射和类型安全转换规避代码注入风险,生产环境必须禁用 eval;
  • ⚠️ 键名歧义处理:若条件中出现未加引号的字符串(如 hello),程序优先尝试作为字典键查找;若需字面量字符串,请务必使用引号包裹("hello" 或 'hello');
  • ? 链式比较语义:a < b < c 等价于 (a < b) and (b < c),符合 Python 原生语义,无需额外处理;
  • ? 性能提示:对于高频校验场景(如 Web API 过滤),建议预编译正则、缓存 OPERATORS 查找,或进一步抽象为 ConditionChecker 类以复用解析器实例;
  • ? 错误处理建议:在用户输入场景中,应捕获 ValueError / KeyError / TypeError 并返回友好提示(如 "条件 'xyz > 10' 中的键 'xyz' 不存在")。

该方案已在实际配置驱动型搜索系统中稳定运行,兼顾安全性、可读性与扩展性。如需支持 in、not in、正则匹配等高级操作符,只需向 OPERATORS 字典新增映射并增强 cast_literal 的解析能力即可平滑升级。

灵机语音
灵机语音

灵机语音

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
js正则表达式
js正则表达式

php中文网为大家提供各种js正则表达式语法大全以及各种js正则表达式使用的方法,还有更多js正则表达式的相关文章、相关下载、相关课程,供大家免费下载体验。

531

2023.06.20

正则表达式不包含
正则表达式不包含

正则表达式,又称规则表达式,,是一种文本模式,包括普通字符和特殊字符,是计算机科学的一个概念。正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串,通常被用来检索、替换那些符合某个模式的文本。php中文网给大家带来了有关正则表达式的相关教程以及文章,希望对大家能有所帮助。

258

2023.07.05

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

767

2023.07.05

java正则表达式匹配字符串
java正则表达式匹配字符串

在Java中,我们可以使用正则表达式来匹配字符串。本专题为大家带来java正则表达式匹配字符串的相关内容,帮助大家解决问题。

219

2023.08.11

正则表达式空格
正则表达式空格

正则表达式空格可以用“s”来表示,它是一个特殊的元字符,用于匹配任意空白字符,包括空格、制表符、换行符等。本专题为大家提供正则表达式相关的文章、下载、课程内容,供大家免费下载体验。

357

2023.08.31

Python爬虫获取数据的方法
Python爬虫获取数据的方法

Python爬虫可以通过请求库发送HTTP请求、解析库解析HTML、正则表达式提取数据,或使用数据抓取框架来获取数据。更多关于Python爬虫相关知识。详情阅读本专题下面的文章。php中文网欢迎大家前来学习。

293

2023.11.13

正则表达式空格如何表示
正则表达式空格如何表示

正则表达式空格可以用“s”来表示,它是一个特殊的元字符,用于匹配任意空白字符,包括空格、制表符、换行符等。想了解更多正则表达式空格怎么表示的内容,可以访问下面的文章。

245

2023.11.17

正则表达式中如何匹配数字
正则表达式中如何匹配数字

正则表达式中可以通过匹配单个数字、匹配多个数字、匹配固定长度的数字、匹配整数和小数、匹配负数和匹配科学计数法表示的数字的方法匹配数字。更多关于正则表达式的相关知识详情请看本专题下面的文章。php中文网欢迎大家前来学习。

548

2023.12.06

TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

69

2026.03.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号