0

0

如何安全高效地验证字典键值对是否满足用户自定义的多条件表达式

心靈之曲

心靈之曲

发布时间:2026-03-15 10:49:12

|

787人浏览过

|

来源于php中文网

原创

如何安全高效地验证字典键值对是否满足用户自定义的多条件表达式

本文介绍一种基于正则解析与操作符映射的安全、可扩展方案,用于批量校验字典中键值对是否同时满足用户输入的类 Python 表达式条件(如 'a > 5, 0 < (cd) < 6, ef < 35'),避免 eval() 风险,支持多种比较运算符、嵌套不等式及字符串/数值/布尔/None 类型自动推断。

本文介绍一种基于正则解析与操作符映射的安全、可扩展方案,用于批量校验字典中键值对是否同时满足用户输入的类 python 表达式条件(如 `'a > 5, 0

在实际数据处理场景中,常需根据用户动态输入的条件字符串(如 'a > 5, 0 < (cd) < 6, ef < 35')判断一个字典是否整体满足所有约束。虽然可手动拆分、提取键名与操作符并逐条执行判断,但这种方式易出错、难以维护,且无法优雅支持链式比较(如 0 < x < 6)或混合类型(字符串、浮点数、布尔值、None)。更关键的是,直接使用 eval() 执行用户输入存在严重安全风险,绝不推荐

以下提供一个轻量、健壮、无外部依赖(仅标准库)的解决方案,核心思想是:将条件字符串结构化解析 → 映射为安全的操作函数 → 按顺序组合求值

✅ 核心实现逻辑

我们使用 re 进行词法切分,operator 模块提供标准比较函数,并通过 cast() 自动识别并转换字面量类型(整数、浮点、布尔、带引号字符串、None)。关键设计包括:

Cutout.Pro抠图
Cutout.Pro抠图

AI批量抠图去背景

下载
  • 操作符预注册:支持 ==, !=, <, <=, >, >= 六种常见比较;
  • 智能键值获取:若切分出的 token 是字典中存在的 key(如 'a'),则取 data['a'];否则尝试解析为字面量(如 '3.14' → 3.14, '"hello"' → 'hello');
  • 链式比较支持:通过 v[i:i+2] 将 0 < (cd) < 6 拆解为 (0 < data['(cd)']) and (data['(cd)'] < 6),天然兼容任意长度的连续不等式;
  • 类型安全转换:cast() 函数严格匹配数字、布尔字面量('true'/'false')、单/双引号字符串及 None,拒绝非法输入。

? 完整可运行代码

import operator
import re

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

# 2. 构建正则:匹配任意已注册操作符(转义防特殊字符干扰)
_oper = '|'.join(map(re.escape, operators.keys()))
oper_re = re.compile(fr'\s*({_oper})\s*').split

# 3. 数值与字符串匹配正则
number_re = re.compile(r'-?\d*(\.\d+)?').fullmatch
string_re = re.compile(r'("|\')(?P<str>.*)\1').fullmatch

def cast(value: str) -> float | int | bool | str | None:
    """安全解析字面量字符串为对应 Python 值"""
    v = value.strip()
    if not v:
        return None
    lower_v = v.lower()
    if lower_v in ('true', 'false'):
        return lower_v == 'true'
    if lower_v == 'none':
        return None
    if m := string_re(v):
        return m.group('str')
    if m := number_re(v):
        return float(v) if '.' in v else int(v)
    raise ValueError(f"无法解析字面量: {value}")

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

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

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

    Raises:
        ValueError: 条件语法错误(如操作符数量不匹配)或字面量无法解析
    """
    facts = []  # 存储每个原子比较的结果(True/False)

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

        values, ops = [], []
        # 按操作符切分,交替提取值和操作符
        tokens = oper_re(cond)
        for token in tokens:
            token = token.strip()
            if not token:
                continue
            if token in operators:
                ops.append(operators[token])
            else:
                # 尝试从字典取值,失败则解析为字面量
                val = data.get(token)
                if val is None:
                    val = cast(token)
                values.append(val)

        # 验证结构:n 个操作符必须对应 n+1 个值(如 a<b<c → 2 op, 3 val)
        if len(values) != len(ops) + 1:
            raise ValueError(f"条件格式错误: '{cond}' —— 值与操作符数量不匹配")

        # 执行所有二元比较:v0 op0 v1, v1 op1 v2, ...
        for i in range(len(ops)):
            try:
                result = ops[i](values[i], values[i + 1])
            except TypeError as e:
                raise TypeError(f"类型不匹配无法比较 '{values[i]}' {list(operators.keys())[list(operators.values()).index(ops[i])]} '{values[i+1]}'") from e
            facts.append(result)

    return all(facts)

# ✅ 使用示例
if __name__ == "__main__":
    test_data = {
        '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, ef == 35',
        'mn is None, ij == "hello", kl == False',
        'ab < 5, gh > 10',
    ]

    for case in test_cases:
        try:
            result = check_conditions(test_data, case)
            print(f"✅ '{case}' → {result}")
        except Exception as e:
            print(f"❌ '{case}' → 错误: {e}")

⚠️ 注意事项与最佳实践

  • 安全性优先:本方案完全规避 eval() 和 exec(),所有用户输入均经显式解析与类型校验,杜绝远程代码执行(RCE)风险。
  • 键名限制:字典键若含空格、括号、点号等(如 '(cd)'),需确保用户输入的条件中完全一致(包括括号),否则会当作字面量解析失败。
  • 链式比较语义:0 < x < 6 被解释为 (0 < x) and (x < 6),符合直觉;但 a != b != c 不等价于 (a != b) and (b != c)(数学上可能不成立),请按需调整逻辑。
  • 扩展性提示:如需支持 in、not in、is、is not 等操作符,只需在 operators 字典中添加对应 operator 函数(如 operator.contains)并增强 cast() 对容器字面量的支持。
  • 性能考量:对于高频调用场景,可将 oper_re、number_re 等正则编译为模块级常量,避免重复编译;复杂条件建议预编译为检查函数缓存。

该方案在简洁性、安全性与表达力之间取得良好平衡,适用于配置驱动型校验、低代码规则引擎、数据质量检查等生产环境,是替代 eval() 的专业级实践范本。

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

热门AI工具

更多
DeepSeek
DeepSeek

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

豆包大模型
豆包大模型

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

WorkBuddy
WorkBuddy

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

腾讯元宝
腾讯元宝

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

文心一言
文心一言

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

讯飞写作
讯飞写作

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

即梦AI
即梦AI

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

ChatGPT
ChatGPT

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

相关专题

更多
java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1570

2023.10.24

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1570

2023.10.24

Go语言中的运算符有哪些
Go语言中的运算符有哪些

Go语言中的运算符有:1、加法运算符;2、减法运算符;3、乘法运算符;4、除法运算符;5、取余运算符;6、比较运算符;7、位运算符;8、按位与运算符;9、按位或运算符;10、按位异或运算符等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

241

2024.02.23

php三元运算符用法
php三元运算符用法

本专题整合了php三元运算符相关教程,阅读专题下面的文章了解更多详细内容。

170

2025.10.17

登录token无效
登录token无效

登录token无效解决方法:1、检查token的有效期限,如果token已经过期,需要重新获取一个新的token;2、检查token的签名,如果签名不正确,需要重新获取一个新的token;3、检查密钥的正确性,如果密钥不正确,需要重新获取一个新的token;4、使用HTTPS协议传输token,建议使用HTTPS协议进行传输 ;5、使用双因素认证,双因素认证可以提高账户的安全性。

6656

2023.09.14

登录token无效怎么办
登录token无效怎么办

登录token无效的解决办法有检查Token是否过期、检查Token是否正确、检查Token是否被篡改、检查Token是否与用户匹配、清除缓存或Cookie、检查网络连接和服务器状态、重新登录或请求新的Token、联系技术支持或开发人员等。本专题为大家提供token相关的文章、下载、课程内容,供大家免费下载体验。

844

2023.09.14

token怎么获取
token怎么获取

获取token值的方法:1、小程序调用“wx.login()”获取 临时登录凭证code,并回传到开发者服务器;2、开发者服务器以code换取,用户唯一标识openid和会话密钥“session_key”。想了解更详细的内容,可以阅读本专题下面的文章。

1092

2023.12.21

token什么意思
token什么意思

token是一种用于表示用户权限、记录交易信息、支付虚拟货币的数字货币。可以用来在特定的网络上进行交易,用来购买或出售特定的虚拟货币,也可以用来支付特定的服务费用。想了解更多token什么意思的相关内容可以访问本专题下面的文章。

2210

2024.03.01

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

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

49

2026.03.13

热门下载

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

精品课程

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

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