QLExpress 字符串能力解析:机制、用法与工程实践

目录

[一、QLExpress 中的字符串模型与语义定位](#一、QLExpress 中的字符串模型与语义定位)

(一)字符串是"表达式层"的基础类型

[(二)+ 运算符的动态分派机制](#(二)+ 运算符的动态分派机制)

(三)案例展示

二、各部分详细讲解和指导说明

(一)字符串拼接:从简单到复杂的表达能力

[1. 基本拼接:表达式级字符串构造](#1. 基本拼接:表达式级字符串构造)

[2. 条件拼接:字符串即业务判断结果](#2. 条件拼接:字符串即业务判断结果)

[3. 循环拼接:构建动态文本结构](#3. 循环拼接:构建动态文本结构)

(二)字符串格式化:没有模板,但能力并不弱

[1. QLExpress 的格式化哲学](#1. QLExpress 的格式化哲学)

[2. 数值格式化的常用模式](#2. 数值格式化的常用模式)

保留小数位

百分比表示

[3. 千分位与补零的表达式实现](#3. 千分位与补零的表达式实现)

(三)字符串验证:规则引擎的天然适配点

[1. 空值、空串、空白的标准写法](#1. 空值、空串、空白的标准写法)

[2. 长度与格式验证](#2. 长度与格式验证)

[3. 内容验证与弱正则策略](#3. 内容验证与弱正则策略)

[4. 综合校验:字符串即校验报告](#4. 综合校验:字符串即校验报告)

(四)字符串处理:真正的"业务文本工具箱"

[1. 清理与规范化](#1. 清理与规范化)

[2. 分割、重组与抽取](#2. 分割、重组与抽取)

[3. 子串提取与定位](#3. 子串提取与定位)

[4. 字符统计与中英文处理](#4. 字符统计与中英文处理)

三、工程实践建议

[(一)适合放在 QLExpress 的字符串逻辑](#(一)适合放在 QLExpress 的字符串逻辑)

[(二)不适合放在 QLExpress 的场景](#(二)不适合放在 QLExpress 的场景)

(三)最佳实践总结

四、结语

参考链接


干货分享,感谢您的阅读!

在绝大多数业务系统中,字符串逻辑几乎无处不在:规则命中说明、校验失败原因、动态标签拼接、策略结果描述、配置化文本生成......

这些看似"简单"的字符串操作,往往正是规则系统中最频繁、最容易失控、也最容易被低估的一类逻辑

在很多团队的实践中,一旦字符串处理能力不足,规则脚本就会迅速演变为"半成品程序":

要么依赖大量 Java 扩展函数,要么在规则中堆叠复杂的模板与正则,最终导致规则可读性下降、维护成本上升、线上风险放大

QLExpress 在这方面的设计取向非常明确:不引入复杂模板机制,但提供足够强的表达式级字符串能力,让字符串自然融入规则语义本身。

字符串在 QLExpress 中不是"被动拼接的文本",而是:

  • 可以参与运算符分派的基础类型

  • 可以承载业务判断结果的表达式输出

  • 可以在循环与条件中逐步构建的动态结构

  • 可以直接作为规则执行结果返回给上层系统

正是基于这种定位,QLExpress 的字符串能力在规则引擎、风控系统、审核策略、动态配置等场景中表现出极强的工程适配性。

我们将围绕 QLExpress 的字符串能力,从表达式语义、执行模型、常见操作模式、复杂处理技巧以及工程实践边界五个层面展开,结合完整可运行示例,系统性地展示如何在真实业务中正确、稳定、高效地使用 QLExpress 处理字符串逻辑。

一、QLExpress 中的字符串模型与语义定位

(一)字符串是"表达式层"的基础类型

在 QLExpress 中:

  • 字符串字面量使用单引号 'text'

  • 字符串变量来自 Context

  • 字符串与数值、布尔、对象可直接混合运算

  • + 运算符在运行期具备动态重载语义

这意味着:

字符串并不是"模板替换工具",而是参与运算的核心数据类型。

(二)+ 运算符的动态分派机制

QLExpress 在执行阶段,会根据左右操作数的运行时类型决定行为:

场景 行为
任一操作数为 String 执行字符串拼接
均为数值类型 执行数值运算
混合对象 调用 toString()

因此以下写法在 QLExpress 中是天然成立的:

java 复制代码
'年龄:' + age '年薪:' + salary * 12

这也是 QLExpress 非常适合业务表达式而非纯计算规则的重要原因。

(三)案例展示

我这里给出的 StringOperationsDemo 具体代码演示如下:

java 复制代码
package org.zyf.javabasic.qlexpress.basic.strings;

import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;

/**
 * @program: zyfboot-javabasic
 * @description: QLExpress字符串操作演示 - 全面展示字符串处理和操作技巧
 * @author: zhangyanfeng
 * @create: 2025-12-25 23:50
 **/
public class StringOperationsDemo {

    private ExpressRunner runner;

    public StringOperationsDemo() {
        this.runner = new ExpressRunner();
        System.out.println("✅ QLExpress字符串操作演示引擎初始化完成");
    }

    /**
     * 演示字符串拼接操作
     */
    public void demonstrateStringConcatenation() {
        System.out.println("\n=== QLExpress字符串拼接演示 ===\n");

        try {
            DefaultContext<String, Object> context = new DefaultContext<>();

            // 准备测试数据
            context.put("firstName", "张");
            context.put("lastName", "彦峰");
            context.put("age", 30);
            context.put("city", "北京");
            context.put("salary", 15000.50);

            // 1. 基本字符串拼接
            System.out.println("🔗 1. 基本字符串拼接演示:");

            Object concat1 = runner.execute("firstName + lastName", context, null, true, false);
            Object concat2 = runner.execute("'姓名:' + firstName + lastName", context, null, true, false);
            Object concat3 = runner.execute("firstName + lastName + ',年龄:' + age", context, null, true, false);

            System.out.printf("   firstName + lastName = %s%n", concat1);
            System.out.printf("   '姓名:' + firstName + lastName = %s%n", concat2);
            System.out.printf("   firstName + lastName + ',年龄:' + age = %s%n%n", concat3);

            // 2. 复杂字符串拼接
            System.out.println("🎯 2. 复杂字符串拼接演示:");

            Object complex1 = runner.execute("'员工信息:' + firstName + lastName + '(' + age + '岁),居住在' + city", context, null, true, false);
            Object complex2 = runner.execute("'薪资:¥' + salary + '元/月,年薪:¥' + (salary * 12) + '元'", context, null, true, false);

            System.out.printf("   员工基本信息: %s%n", complex1);
            System.out.printf("   薪资信息: %s%n%n", complex2);

            // 3. 条件拼接
            System.out.println("❓ 3. 条件拼接演示:");

            Object conditional1 = runner.execute("firstName + lastName + (age >= 18 ? '(成年)' : '(未成年)')", context, null, true, false);
            Object conditional2 = runner.execute("'居住地:' + (city != null ? city : '未知')", context, null, true, false);

            System.out.printf("   条件年龄拼接: %s%n", conditional1);
            System.out.printf("   条件城市拼接: %s%n%n", conditional2);

            // 4. 循环拼接
            System.out.println("🔄 4. 循环拼接演示:");

            String loopScript =
                    "result = '';" +
                            "for (i = 1; i <= 5; i++) {" +
                            "    if (i > 1) result = result + ', ';" +
                            "    result = result + '第' + i + '项';" +
                            "}" +
                            "return result;";

            Object loopResult = runner.execute(loopScript, context, null, true, false);
            System.out.printf("   循环拼接结果: %s%n%n", loopResult);

        } catch (Exception e) {
            System.err.println("❌ 字符串拼接演示失败: " + e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * 演示字符串格式化操作
     */
    public void demonstrateStringFormatting() {
        System.out.println("\n=== QLExpress字符串格式化演示 ===\n");

        try {
            DefaultContext<String, Object> context = new DefaultContext<>();

            // 准备测试数据
            context.put("name", "张彦峰");
            context.put("score", 85.678);
            context.put("count", 1234);
            context.put("ratio", 0.75);

            // 1. 数字格式化
            System.out.println("🔢 1. 数字格式化演示:");

            Object formatScore = runner.execute("'分数:' + Math.round(score * 100) / 100.0", context, null, true, false);
            Object formatPercent = runner.execute("'比例:' + Math.round(ratio * 10000) / 100.0 + '%'", context, null, true, false);

            System.out.printf("   保留两位小数: %s%n", formatScore);
            System.out.printf("   百分比格式: %s%n%n", formatPercent);

            // 2. 数字补零格式化
            System.out.println("0️⃣ 2. 数字补零格式化演示:");

            String padZeroScript =
                    "num = 5;" +
                            "padded = num < 10 ? \"0\" + num : String.valueOf(num);" +
                            "return '编号:' + padded;";

            Object padded = runner.execute(padZeroScript, context, null, true, false);
            System.out.printf("   补零格式化: %s%n%n", padded);

            // 3. 千分位格式化(简单实现)
            System.out.println("💰 3. 千分位格式化演示:");

            String thousandScript =
                    "numStr = String.valueOf(count);" +
                            "len = numStr.length();" +
                            "result = '';" +
                            "for (i = 0; i < len; i++) {" +
                            "    if (i > 0 && (len - i) % 3 == 0) {" +
                            "        result = result + ',';" +
                            "    }" +
                            "    result = result + numStr.charAt(i);" +
                            "}" +
                            "return '金额:¥' + result;";

            Object thousand = runner.execute(thousandScript, context, null, true, false);
            System.out.printf("   千分位格式: %s%n%n", thousand);

            // 4. 字符串模板格式化
            System.out.println("📝 4. 字符串模板演示:");
            System.out.println("   模板演示已跳过");

        } catch (Exception e) {
            System.err.println("❌ 字符串格式化演示失败: " + e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * 演示字符串验证操作
     */
    public void demonstrateStringValidation() {
        System.out.println("\n=== QLExpress字符串验证演示 ===\n");

        try {
            DefaultContext<String, Object> context = new DefaultContext<>();

            // 准备测试数据
            context.put("email", "zhangyanfeng@example.com");
            context.put("phone", "13812345678");
            context.put("idCard", "110101199001011234");
            context.put("password", "Abc123456!");
            context.put("emptyStr", "");
            context.put("nullStr", null);
            context.put("spaceStr", "   ");

            // 1. 基本验证
            System.out.println("✅ 1. 基本字符串验证演示:");

            Object isEmpty1 = runner.execute("emptyStr == null || emptyStr.length() == 0", context, null, true, false);
            Object isEmpty2 = runner.execute("nullStr == null", context, null, true, false);
            Object isBlank = runner.execute("spaceStr.trim().length() == 0", context, null, true, false);

            System.out.printf("   空字符串检查: %s%n", isEmpty1);
            System.out.printf("   null字符串检查: %s%n", isEmpty2);
            System.out.printf("   空白字符串检查: %s%n%n", isBlank);

            // 2. 长度验证
            System.out.println("📏 2. 字符串长度验证演示:");

            Object phoneLength = runner.execute("phone.length() == 11", context, null, true, false);
            Object passwordLength = runner.execute("password.length() >= 8 && password.length() <= 20", context, null, true, false);
            Object idCardLength = runner.execute("idCard.length() == 18", context, null, true, false);

            System.out.printf("   手机号长度验证: phone=%s, 长度正确=%s%n", context.get("phone"), phoneLength);
            System.out.printf("   密码长度验证: 长度合规=%s%n", passwordLength);
            System.out.printf("   身份证长度验证: 长度正确=%s%n%n", idCardLength);

            // 3. 格式验证
            System.out.println("🎯 3. 字符串格式验证演示:");

            Object emailFormat = runner.execute("email.contains(\"@\") && email.contains(\".\")", context, null, true, false);
            Object phoneFormat = runner.execute("phone.startsWith(\"1\") && phone.length() == 11", context, null, true, false);

            System.out.printf("   邮箱格式验证: email=%s, 格式正确=%s%n", context.get("email"), emailFormat);
            System.out.printf("   手机号格式验证: 格式正确=%s%n%n", phoneFormat);

            // 4. 内容验证
            System.out.println("🔍 4. 字符串内容验证演示:");

            Object hasUpperCase = runner.execute("!password.equals(password.toLowerCase())", context, null, true, false);
            Object hasLowerCase = runner.execute("!password.equals(password.toUpperCase())", context, null, true, false);
            Object hasDigit = runner.execute("password.replaceAll(\"[^0-9]\", \"\").length() > 0", context, null, true, false);
            Object hasSpecial = runner.execute("password.replaceAll(\"[a-zA-Z0-9]\", \"\").length() > 0", context, null, true, false);

            System.out.printf("   密码包含大写字母: %s%n", hasUpperCase);
            System.out.printf("   密码包含小写字母: %s%n", hasLowerCase);
            System.out.printf("   密码包含数字: %s%n", hasDigit);
            System.out.printf("   密码包含特殊字符: %s%n%n", hasSpecial);

            // 5. 综合验证
            System.out.println("🏆 5. 综合验证演示:");

            String validateScript =
                    "emailValid = email != null && email.contains(\"@\") && email.contains(\".\") && email.length() > 5;" +
                            "phoneValid = phone != null && phone.length() == 11 && phone.startsWith(\"1\");" +
                            "passwordValid = password != null && password.length() >= 8 && " +
                            "                !password.equals(password.toLowerCase()) && " +
                            "                !password.equals(password.toUpperCase()) && " +
                            "                password.replaceAll(\"[^0-9]\", \"\").length() > 0;" +
                            "return \"邮箱:\" + (emailValid ? \"✓\" : \"✗\") + \" 手机:\" + (phoneValid ? \"✓\" : \"✗\") + \" 密码:\" + (passwordValid ? \"✓\" : \"✗\");";

            Object validation = runner.execute(validateScript, context, null, true, false);
            System.out.printf("   综合验证结果: %s%n%n", validation);

        } catch (Exception e) {
            System.err.println("❌ 字符串验证演示失败: " + e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * 演示字符串处理操作
     */
    public void demonstrateStringProcessing() {
        System.out.println("\n=== QLExpress字符串处理演示 ===\n");

        try {
            DefaultContext<String, Object> context = new DefaultContext<>();

            // 准备测试数据
            context.put("text", "Hello QLExpress World! 你好,QLExpress世界!");
            context.put("messyText", "  Hello   World  \n\t  ");
            context.put("mixedCase", "hElLo WoRlD");
            context.put("csvData", "apple,banana,orange;grape|watermelon");

            // 1. 字符串清理
            System.out.println("🧹 1. 字符串清理演示:");

            Object trimmed = runner.execute("messyText.trim()", context, null, true, false);
            Object normalized = runner.execute("messyText.trim().replaceAll(\"\\\\s+\", \" \")", context, null, true, false);
            Object cleaned = runner.execute("text.replaceAll(\"[!,]\", \"\")", context, null, true, false);

            System.out.printf("   原始文本: '%s'%n", context.get("messyText"));
            System.out.printf("   去除首尾空格: '%s'%n", trimmed);
            System.out.printf("   规范化空格: '%s'%n", normalized);
            System.out.printf("   清理标点符号: '%s'%n%n", cleaned);

            // 2. 大小写转换
            System.out.println("🔄 2. 大小写转换演示:");

            Object upperCase = runner.execute("mixedCase.toUpperCase()", context, null, true, false);
            Object lowerCase = runner.execute("mixedCase.toLowerCase()", context, null, true, false);

            // 首字母大写
            String capitalizeScript =
                    "str = mixedCase.toLowerCase();" +
                            "return str.substring(0, 1).toUpperCase() + str.substring(1);";

            Object capitalized = runner.execute(capitalizeScript, context, null, true, false);

            System.out.printf("   原始文本: %s%n", context.get("mixedCase"));
            System.out.printf("   全部大写: %s%n", upperCase);
            System.out.printf("   全部小写: %s%n", lowerCase);
            System.out.printf("   首字母大写: %s%n%n", capitalized);

            // 3. 字符串分割和重组
            System.out.println("✂️ 3. 字符串分割和重组演示:");

            String splitScript =
                    "parts1 = csvData.split(\",\");" +
                            "parts2 = csvData.split(\"[,;|]\");" +
                            "return \"按逗号分割: \" + parts1.length + \"个, 按多字符分割: \" + parts2.length + \"个\";";

            Object splitResult = runner.execute(splitScript, context, null, true, false);
            System.out.printf("   分割结果: %s%n", splitResult);

            String joinScript =
                    "words = text.split(\" \");" +
                            "result = \"\";" +
                            "for (i = 0; i < words.length; i++) {" +
                            "    if (i > 0) result = result + \" | \";" +
                            "    result = result + words[i];" +
                            "}" +
                            "return result;";

            Object joinResult = runner.execute(joinScript, context, null, true, false);
            System.out.printf("   重新连接: %s%n%n", joinResult);

            // 4. 字符串提取
            System.out.println("🔍 4. 字符串提取演示:");

            Object firstWord = runner.execute("text.substring(0, text.indexOf(\" \"))", context, null, true, false);
            Object lastWord = runner.execute("text.substring(text.lastIndexOf(\" \") + 1)", context, null, true, false);
            Object middlePart = runner.execute("text.substring(6, 15)", context, null, true, false);

            System.out.printf("   提取第一个单词: %s%n", firstWord);
            System.out.printf("   提取最后一个词: %s%n", lastWord);
            System.out.printf("   提取中间部分: %s%n%n", middlePart);

            // 5. 字符统计
            System.out.println("📊 5. 字符统计演示:");

            String countScript =
                    "totalLength = text.length();" +
                            "englishCount = text.replaceAll(\"[^a-zA-Z]\", \"\").length();" +
                            "chineseCount = text.replaceAll(\"[^\\\\u4e00-\\\\u9fa5]\", \"\").length();" +
                            "spaceCount = text.replaceAll(\"[^ ]\", \"\").length();" +
                            "return \"总字符:\" + totalLength + \", 英文:\" + englishCount + \", 中文:\" + chineseCount + \", 空格:\" + spaceCount;";

            Object countResult = runner.execute(countScript, context, null, true, false);
            System.out.printf("   字符统计: %s%n%n", countResult);

        } catch (Exception e) {
            System.err.println("❌ 字符串处理演示失败: " + e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * 运行所有演示
     */
    public void runAllDemonstrations() {
        System.out.println("🚀 开始QLExpress字符串操作完整演示...\n");

        demonstrateStringConcatenation();
        demonstrateStringFormatting();
        demonstrateStringValidation();
        demonstrateStringProcessing();

        System.out.println("✅ QLExpress字符串操作演示完成!");
        System.out.println("\n📚 学习要点:");
        System.out.println("   1. 字符串拼接: 使用 + 操作符,支持与各种类型混合");
        System.out.println("   2. 字符串格式化: 数字格式化、模板替换、条件拼接");
        System.out.println("   3. 字符串验证: 长度检查、格式验证、内容验证");
        System.out.println("   4. 字符串处理: 清理、转换、分割、提取、统计");
        System.out.println("   5. 正则表达式: replaceAll方法支持正则表达式替换");
        System.out.println("   6. 中文处理: 支持中文字符的各种操作");
    }

    /**
     * 主方法 - 运行演示
     */
    public static void main(String[] args) {
        StringOperationsDemo demo = new StringOperationsDemo();
        demo.runAllDemonstrations();
    }
}

基本运行验证如下:

python 复制代码
✅ QLExpress字符串操作演示引擎初始化完成
🚀 开始QLExpress字符串操作完整演示...


=== QLExpress字符串拼接演示 ===

🔗 1. 基本字符串拼接演示:
   firstName + lastName = 张彦峰
   '姓名:' + firstName + lastName = 姓名:张彦峰
   firstName + lastName + ',年龄:' + age = 张彦峰,年龄:30

🎯 2. 复杂字符串拼接演示:
   员工基本信息: 员工信息:张彦峰(30岁),居住在北京
   薪资信息: 薪资:¥15000.5元/月,年薪:¥180006.0元

❓ 3. 条件拼接演示:
   条件年龄拼接: 张彦峰(成年)
   条件城市拼接: 居住地:北京

🔄 4. 循环拼接演示:
   循环拼接结果: 第1项, 第2项, 第3项, 第4项, 第5项


=== QLExpress字符串格式化演示 ===

🔢 1. 数字格式化演示:
   保留两位小数: 分数:85.68
   百分比格式: 比例:75.0%

0️⃣ 2. 数字补零格式化演示:
   补零格式化: 编号:05

💰 3. 千分位格式化演示:
   千分位格式: 金额:¥1,234

📝 4. 字符串模板演示:
   模板演示已跳过

=== QLExpress字符串验证演示 ===

✅ 1. 基本字符串验证演示:
   空字符串检查: true
   null字符串检查: true
   空白字符串检查: true

📏 2. 字符串长度验证演示:
   手机号长度验证: phone=13812345678, 长度正确=true
   密码长度验证: 长度合规=true
   身份证长度验证: 长度正确=true

🎯 3. 字符串格式验证演示:
   邮箱格式验证: email=zhangyanfeng@example.com, 格式正确=true
   手机号格式验证: 格式正确=true

🔍 4. 字符串内容验证演示:
   密码包含大写字母: true
   密码包含小写字母: true
   密码包含数字: true
   密码包含特殊字符: true

🏆 5. 综合验证演示:
   综合验证结果: 邮箱:✓ 手机:✓ 密码:✓


=== QLExpress字符串处理演示 ===

🧹 1. 字符串清理演示:
   原始文本: '  Hello   World  
	  '
   去除首尾空格: 'Hello   World'
   规范化空格: 'Hello World'
   清理标点符号: 'Hello QLExpress World! 你好QLExpress世界'

🔄 2. 大小写转换演示:
   原始文本: hElLo WoRlD
   全部大写: HELLO WORLD
   全部小写: hello world
   首字母大写: Hello world

✂️ 3. 字符串分割和重组演示:
   分割结果: 按逗号分割: 3个, 按多字符分割: 5个
   重新连接: Hello | QLExpress | World! | 你好,QLExpress世界!

🔍 4. 字符串提取演示:
   提取第一个单词: Hello
   提取最后一个词: 你好,QLExpress世界!
   提取中间部分: QLExpress

📊 5. 字符统计演示:
   字符统计: 总字符:38, 英文:28, 中文:4, 空格:3

✅ QLExpress字符串操作演示完成!

📚 学习要点:
   1. 字符串拼接: 使用 + 操作符,支持与各种类型混合
   2. 字符串格式化: 数字格式化、模板替换、条件拼接
   3. 字符串验证: 长度检查、格式验证、内容验证
   4. 字符串处理: 清理、转换、分割、提取、统计
   5. 正则表达式: replaceAll方法支持正则表达式替换
   6. 中文处理: 支持中文字符的各种操作

Process finished with exit code 0

在代码中覆盖的场景包括:

  • 拼接(Concatenation)

  • 格式化(Formatting)

  • 校验(Validation)

  • 清洗 / 规范化(Processing)

  • 分割与重组(Split & Join)

这正是规则工程中最典型的字符串生命周期

二、各部分详细讲解和指导说明

(一)字符串拼接:从简单到复杂的表达能力

1. 基本拼接:表达式级字符串构造

QLExpress 的字符串拼接具有如下特征:

  • 不依赖 StringBuilder

  • 不需要显式类型转换

  • 表达式天然可读

java 复制代码
firstName + lastName '姓名:' + firstName + lastName

推荐场景

  • 输出规则结果

  • 动态提示信息

  • 简单文本生成

2. 条件拼接:字符串即业务判断结果

借助三目表达式,字符串可以直接承载业务含义:

java 复制代码
name + (age >= 18 ? '(成年)' : '(未成年)')

这是 QLExpress 在规则引擎、风控策略、审核逻辑中极为常见的写法:

  • 表达式即结论

  • 无需再写 if/else

3. 循环拼接:构建动态文本结构

QLExpress 支持完整的 for / if 语法,因此字符串可以在循环中逐步构建:

java 复制代码
result = '';
for (i = 1; i <= 5; i++) {
    if (i > 1) result = result + ', ';
    result = result + '第' + i + '项';
}
return result;

工程意义

  • 可动态生成列表描述

  • 可构造规则命中详情

  • 避免 Java 侧重复封装逻辑

(二)字符串格式化:没有模板,但能力并不弱

1. QLExpress 的格式化哲学

QLExpress 并未内置类似 String.format 的模板引擎,其设计理念是:

用表达式而不是模板描述格式逻辑

这使得字符串格式化具备以下特征:

  • 强逻辑可控

  • 无隐式规则

  • 可随业务变化自由扩展

2. 数值格式化的常用模式

保留小数位
java 复制代码
Math.round(score * 100) / 100.0
百分比表示
java 复制代码
Math.round(ratio * 10000) / 100.0 + '%'

这些写法虽然"原始",但:

  • 可预测

  • 易调试

  • 不依赖额外函数注册

3. 千分位与补零的表达式实现

在没有格式化函数的情况下,通过字符串操作即可完成:

  • 补零:三目 + 拼接

  • 千分位:字符遍历 + 条件插入

这类写法非常适合:

  • 金额展示

  • 编号规则

  • 报表字段

(三)字符串验证:规则引擎的天然适配点

1. 空值、空串、空白的标准写法

java 复制代码
str == null
str.length() == 0
str.trim().length() == 0

这类判断在 QLExpress 中无需任何扩展,可直接使用 Java String API。

2. 长度与格式验证

典型验证逻辑:

java 复制代码
phone.length() == 11
email.contains('@') && email.contains('.')

在规则系统中,这比正则更:

  • 直观

  • 易维护

  • 可被非开发人员理解

3. 内容验证与弱正则策略

QLExpress 支持 replaceAll,从而实现"字符集合检测":

java 复制代码
password.replaceAll('[^0-9]', '').length() > 0

这种方式的优势在于:

  • 不依赖复杂正则

  • 错误风险低

  • 表达式可拆解

4. 综合校验:字符串即校验报告

QLExpress 非常适合返回"校验结果文本":

java 复制代码
"邮箱:" + (emailValid ? "✓" : "✗")

在实际系统中,这种写法常用于:

  • 表单规则校验

  • 审核失败原因拼接

  • 决策可解释性输出

(四)字符串处理:真正的"业务文本工具箱"

1. 清理与规范化

QLExpress 可直接调用:

  • trim

  • replaceAll

  • toUpperCase / toLowerCase

非常适合做:

  • 用户输入清洗

  • 日志内容标准化

  • 文本归一处理

2. 分割、重组与抽取

字符串拆分在 QLExpress 中是一等能力

java 复制代码
csvData.split('[,;|]')

配合循环即可实现:

  • CSV 解析

  • 多分隔符兼容

  • 动态重组输出

3. 子串提取与定位

java 复制代码
text.substring(0, text.indexOf(' '))

这类操作在以下场景非常常见:

  • 协议字段解析

  • 文本规则匹配

  • 简单语义拆解

4. 字符统计与中英文处理

QLExpress 支持 Unicode 正则

java 复制代码
text.replaceAll('[^\\u4e00-\\u9fa5]', '').length()

这使其可以胜任:

  • 中英文混排统计

  • 内容合规检测

  • 文本质量评估

三、工程实践建议

(一)适合放在 QLExpress 的字符串逻辑

  • 展示类文本拼接

  • 规则解释性输出

  • 校验与判断描述

  • 可配置业务文案

(二)不适合放在 QLExpress 的场景

  • 高性能大文本处理

  • 复杂模板渲染(HTML / JSON)

  • 超复杂正则解析

(三)最佳实践总结

  1. 让字符串表达业务含义,而不是技术细节

  2. 避免在表达式中写"不可读"的正则

  3. 复杂处理可拆为多个表达式变量

  4. 返回字符串结果,而不是状态码

四、结语

回顾全文可以看到,QLExpress 的字符串能力并不是简单意义上的"拼接 API 集合",而是一套与表达式执行模型深度绑定的业务建模工具

通过对字符串拼接、格式化、校验、清理、分割、提取与统计等场景的系统演示,可以明确几个关键结论:

第一,字符串是 QLExpress 规则表达力的重要组成部分

借助 + 运算符的动态分派机制、三目表达式与完整控制流程,字符串可以直接承载业务含义,而非仅作为计算结果的展示层。

第二,QLExpress 更适合"规则型字符串逻辑",而非复杂文本算法

它非常擅长表达"条件 + 文本""校验 + 结果说明""循环 + 动态拼接"等规则语义,但并不追求替代模板引擎或文本处理框架。

第三,字符串即规则输出,是规则工程中的重要设计理念

无论是校验报告、命中原因,还是策略执行结果,QLExpress 都天然适合直接返回结构化或半结构化的字符串结果,减少规则层与业务层之间的二次转换成本。

第四,良好的边界意识,决定规则系统的长期可维护性

将"业务含义明确、逻辑可解释、变更频繁"的字符串逻辑放入 QLExpress;

将"复杂模板渲染、重正则计算、跨语言格式标准"留在应用层处理,是一条被反复验证的工程实践路径。

当你以"规则建模工具"的视角,而非"脚本语言"的视角去使用 QLExpress 时,会发现它在字符串处理上的克制设计,恰恰是其能够长期稳定服务复杂业务系统的重要原因之一。

参考链接

相关推荐
biyezuopinvip5 小时前
基于uni-app和Express的问答对战小程序的设计与实现(论文)
小程序·uni-app·毕业设计·论文·express·毕业论文·问答对战小程序的设计与实现
天意pt15 小时前
Idempotency 幂等性 - 点赞和投票功能
前端·javascript·express
SunkingYang2 天前
QT中QStringList如何查找指定字符串,有哪些方式?
qt·字符串·查找·子串·qstringlist
闻缺陷则喜何志丹6 天前
【回文 字符串】3677 统计二进制回文数字的数目|2223
c++·算法·字符串·力扣·回文
水冗水孚7 天前
告别黑盒!手写Windows版简易NodeMON,学习文件监听代码修改与进程服务重启知识
node.js·express
Tisfy7 天前
LeetCode 0712.两个字符串的最小ASCII删除和:反向思维保留最大(动态规划)
算法·leetcode·动态规划·字符串·dp·子序列
天意pt8 天前
Blog-SSR 系统操作手册(v1.0.0)
前端·vue.js·redis·mysql·docker·node.js·express
漫游嵌入式11 天前
《PCI EXPRESS体系结构导读》---(5)PCI总线Device号的分配
express·pcie·pci
漫游嵌入式11 天前
《PCI EXPRESS体系结构导读》---(4)PCI总线Bus号初始化
express·pcie·pci