news 2026/10/21 10:31:02

三菱ST工单拼接:CONCAT与REPLACE实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
三菱ST工单拼接:CONCAT与REPLACE实战指南

1. 工单拼接场景下的字符串处理需求拆解

1.1 为什么工单系统离不开字符串拼接

做过产线工单系统的人都有一个共识:PLC 侧的数据从来不是给人看的。设备状态、批次号、时间戳、工位编号,这些数据在寄存器里各占各的地址,各用各的数据类型,彼此之间毫无关联。但操作员需要看到的是一行能读懂的工单信息,比如“WO-20240513-A线-03工位-合格”。这中间的转换工作,就是字符串拼接要解决的核心问题。

在三菱 ST(Structured Text)编程环境里,字符串处理不像高级语言那么自由。没有动态内存分配,没有丰富的标准库,字符串长度还要提前声明。很多从 C# 或 Python 转过来做 PLC 的工程师,第一次写工单拼接逻辑时都会踩坑——要么拼接结果被截断,要么数值转字符串后带了一堆空格,要么 REPLACE 函数用错了位置参数导致整个字符串乱掉。

这篇文章围绕 CONCAT 和 REPLACE 这两个最常用的字符串函数展开,把工单拼接场景下的选型逻辑、参数细节、实操步骤和排查经验一次讲透。不管你是刚接触三菱 ST 的新手,还是已经写过一些工单逻辑但总觉得不够优雅的老手,都能从中找到可以直接复用的方案。

1.2 CONCAT 与 REPLACE 的核心差异

先给结论:CONCAT 负责“从无到有”,REPLACE 负责“从有到优”。

CONCAT 的本质是把两个或多个字符串按顺序首尾相接,生成一个新的字符串。它不关心原字符串里有什么内容,只负责把它们拼在一起。在工单场景里,CONCAT 适合做初始拼接——把工单号、产线编号、工位号、状态码这些独立字段拼成一条完整记录。

REPLACE 的本质是在一个已有字符串中,把指定的子串替换成新的内容。它关心的是“找到什么”和“换成什么”。在工单场景里,REPLACE 适合做模板填充或格式修正——比如先定义一个工单模板“WO-XXXX-XX-XX”,然后用实际数据逐段替换占位符。

两者的核心差异可以用一个生活类比来理解:CONCAT 像把几块积木首尾粘起来搭一座桥,REPLACE 像在已经搭好的桥上把某一段换成不同颜色的积木。前者关注组合,后者关注修改。

对比维度CONCATREPLACE
核心功能多个字符串顺序拼接替换指定子串
输入数量2 个及以上字符串源字符串、被替换串、替换串
结果长度各输入长度之和通常与源字符串等长或按替换内容变化
典型场景初始工单信息组装模板占位符填充、格式修正
常见坑点结果超长被截断位置参数理解错误

1.3 工单拼接的典型数据流

一条完整的工单信息在 PLC 内部的流转路径通常是这样的:底层设备通过输入模块把状态字写入数据寄存器,这些寄存器里存的是 INT 或 DINT 类型的数值。工单号可能来自上位机下发的字符串数组,产线编号和工位号可能是固定的常量字符串,时间戳需要从时钟寄存器读取后转换。

这些数据源的类型不统一,长度不统一,格式也不统一。CONCAT 和 REPLACE 要做的,就是把这些“散装”数据整合成一条格式规范的工单字符串,再写入 HMI 或上传给 MES 系统。

理解这个数据流很重要,因为它决定了你在哪个环节用 CONCAT、在哪个环节用 REPLACE。我的经验是:先用 CONCAT 把各字段拼成原始串,再用 REPLACE 做格式微调。这个顺序反过来会非常别扭,后面会详细说明原因。

2. CONCAT 函数深度解析与实操要点

2.1 CONCAT 的参数结构与返回值机制

三菱 ST 中 CONCAT 的标准调用形式是CONCAT(IN1, IN2),返回一个字符串。在 GX Works3 环境下,这个函数的参数类型通常是 STRING 或 STRING 数组元素。需要注意的是,三菱的 CONCAT 一次只接受两个输入参数,如果你要拼接四个字段,就需要嵌套调用或者连续调用。

嵌套调用的写法是CONCAT(CONCAT(IN1, IN2), CONCAT(IN3, IN4)),这种写法在逻辑上没问题,但可读性很差,调试时很难一眼看出拼接顺序。连续调用的写法是分多步执行,每一步把结果存入一个临时字符串变量,下一步再拿这个临时变量和下一个字段拼接。我个人的习惯是连续调用,虽然多写几行代码,但后期维护和排查方便得多。

返回值机制方面,CONCAT 返回的是一个新的字符串,不会修改任何输入参数。这一点和 REPLACE 不同,REPLACE 在某些实现中会直接修改源字符串。理解这个差异对内存规划很重要——每次 CONCAT 都会产生一个新的字符串占用,如果在一个扫描周期内做大量拼接操作,需要留意字符串变量的声明长度是否足够。

2.2 字符串长度声明与截断风险

这是 CONCAT 使用中最容易踩的坑,没有之一。三菱 ST 中声明字符串变量时,必须指定最大长度,比如strWorkOrder : STRING[50]。这个 50 是字符数上限,不是字节数。如果你拼接后的结果超过 50 个字符,多出来的部分会被直接截断,而且不会报错。

我见过一个真实的案例:某工位拼接后的工单信息理论长度是 42 个字符,工程师声明了 STRING[40],测试时用短工单号没发现问题,上线后遇到长工单号,结果状态码被截掉了,操作员看到的工单永远显示“合”而不是“合格”。这种问题排查起来非常费时间,因为逻辑本身没错,错在声明长度。

规避方法很简单:在声明字符串变量时,把所有可能拼接的字段最大长度加起来,再留 20% 的余量。比如工单号最长 20 字符、产线编号 5 字符、工位号 5 字符、状态码 5 字符、分隔符 6 字符,合计 41 字符,那声明 STRING[50] 就比较稳妥。

注意:三菱 ST 中字符串长度声明不足导致的截断是静默发生的,不会触发任何错误标志。建议在拼接完成后加一个长度校验逻辑,如果结果长度等于声明上限,就置一个警告位。

2.3 数值转字符串的预处理

CONCAT 只能拼接字符串,不能直接拼接数值。工单号里的序号、时间戳里的时分秒、工位编号,这些在 PLC 里往往是 INT 或 DINT 类型,必须先转成字符串才能参与拼接。

三菱 ST 提供了 INT_TO_STRING 和 DINT_TO_STRING 等转换函数。但这里有一个细节:转换后的字符串默认不带前导零。比如工位号 3 转换后是“3”而不是“03”。如果你的工单格式要求固定两位,就需要手动补零。

补零的常用做法是用 REPLACE 或者条件判断。比如先拼接一个“00”,然后用 REPLACE 把“00”中对应位置替换成实际数字。这个技巧在后面 REPLACE 章节会详细展开。另一种做法是用 RIGHT 函数截取“00”加数字后的右侧两位,但 RIGHT 函数在三菱 ST 中的可用性取决于具体型号,不是所有环境都支持。

我的建议是:在工单拼接逻辑设计阶段,就把所有需要固定位数的字段列出来,统一用 REPLACE 做补零处理,这样格式最可控。

2.4 CONCAT 嵌套调用的可读性优化

前面提到嵌套调用可读性差,这里展开说一下优化方案。假设你要拼接四个字段:工单号、产线号、工位号、状态码。嵌套写法是这样的:

strResult := CONCAT(CONCAT(CONCAT(strWO, strLine), strStation), strStatus);

这行代码能跑,但如果你第二天回来看,很难快速确认拼接顺序是不是自己想要的。而且如果中间某个字段需要加分隔符,嵌套层级会更深,代码会变得非常难读。

连续调用的写法:

strTemp1 := CONCAT(strWO, '-'); strTemp2 := CONCAT(strTemp1, strLine); strTemp3 := CONCAT(strTemp2, '-'); strTemp4 := CONCAT(strTemp3, strStation); strResult := CONCAT(strTemp4, strStatus);

这种写法代码行数多,但每一步的结果都清晰可见,调试时可以在任意一步设置监视点,查看中间结果。如果拼接逻辑出问题,能快速定位是哪个字段导致的。

折中方案是用一个临时字符串变量反复拼接:

strResult := strWO; strResult := CONCAT(strResult, '-'); strResult := CONCAT(strResult, strLine); strResult := CONCAT(strResult, '-'); strResult := CONCAT(strResult, strStation); strResult := CONCAT(strResult, strStatus);

这种写法兼顾了可读性和代码量,是我在工单拼接场景中最常用的方式。唯一需要注意的是,strResult 的声明长度要足够容纳最终结果。

3. REPLACE 函数深度解析与模板填充技巧

3.1 REPLACE 的参数语义与常见误解

三菱 ST 中 REPLACE 的标准调用形式是REPLACE(IN, L, P, S),其中 IN 是源字符串,L 是被替换的子串长度,P 是替换的起始位置,S 是替换后的新内容。返回替换完成后的字符串。

这里最容易误解的是 L 和 P 的配合关系。L 是从 P 位置开始要替换掉的字符数,不是替换后的字符数。举个例子,源字符串是“WO-XXXX-XX”,你想把从第 4 位开始的 4 个 X 替换成“2024”,那么 L=4,P=4,S=“2024”。替换后结果是“WO-2024-XX”。

很多新手会误以为 L 是替换内容的长度,写成 L=4、P=4、S=“2024” 碰巧对了,但如果替换内容长度不同就会出错。比如把“WO-XXXX-XX”中的“XXXX”替换成“202405”,如果还写 L=4,结果会变成“WO-202405-XX”吗?不会。因为 L=4 只替换掉 4 个字符,源字符串第 4 到第 7 位是“XXXX”,替换成“202405”后,结果是“WO-202405-XX”,看起来对了,但实际上第 8 位的“-”被挤到了后面,如果源字符串是“WO-XXXXXX-XX”,L=4 就只能替换掉前 4 个 X,剩下两个 X 还在。

所以正确的做法是:L 必须等于被替换子串的实际长度。如果占位符长度不固定,REPLACE 就不太适合,应该考虑用 CONCAT 重新拼接。

3.2 模板占位符的设计原则

用 REPLACE 做模板填充时,占位符的设计直接决定了替换逻辑的复杂度。我总结了几条原则:

第一,占位符长度要固定。比如用“XXXX”表示四位年份,用“XX”表示两位月份,不要用“X”表示一位、“XXX”表示三位这种变长设计。固定长度让 L 参数可以直接写常量,不需要动态计算。

第二,占位符字符要唯一。不要用“-”或“_”这种在正常数据中也会出现的字符做占位符,否则替换时可能误伤。用“X”或“#”这类在工单数据中不会自然出现的字符比较安全。

第三,占位符位置要可预测。模板设计好后,每个占位符的起始位置应该是固定的。如果模板中有可变长度的字段,要么把它放在最后,要么用固定长度填充。

一个典型的工单模板可以设计成:“WO-XXXX-XX-XX-XX”,分别对应工单年份、产线号、工位号、状态码。每个占位符长度固定,位置固定,替换逻辑就非常清晰。

3.3 多字段替换的执行顺序

当一个模板中有多个占位符需要替换时,执行顺序会影响结果。因为每次 REPLACE 都会改变字符串的长度(如果替换内容长度和被替换长度不同),后续占位符的位置可能会偏移。

最安全的做法是从右往左替换。因为替换右侧内容不会影响左侧占位符的位置。比如模板“WO-XXXX-XX-XX-XX”,先替换最右边的状态码占位符,再替换工位号,再替换产线号,最后替换年份。这样每次替换都不会影响还没替换的占位符位置。

如果从左往右替换,每次替换后都要重新计算后续占位符的位置,逻辑会复杂很多。我试过从左往右替换,代码里全是位置计算,后期改模板时非常痛苦。从那以后,我的工单模板替换逻辑一律从右往左。

提示:如果替换内容长度和被替换占位符长度完全一致,那么替换顺序无所谓。但在实际工单场景中,很难保证所有字段长度都刚好匹配,所以从右往左是更稳妥的默认策略。

3.4 REPLACE 与 CONCAT 的配合使用

单独用 REPLACE 做模板填充有一个前提:模板本身已经是一个完整的字符串。但模板从哪来?通常是在 PLC 初始化时用 CONCAT 拼出来的,或者直接用一个常量字符串赋值。

更常见的做法是混合使用:先用 CONCAT 把不需要格式化的字段拼在一起,再用 REPLACE 处理需要补零或格式修正的字段。比如工单号和时间戳用 CONCAT 直接拼,工位号和状态码需要固定两位,就用 REPLACE 在拼接结果上做修正。

这种混合方式的优势在于:CONCAT 处理简单拼接效率高,REPLACE 处理格式修正针对性强。两者各司其职,逻辑清晰,排查也方便。

4. 工单拼接完整实操流程

4.1 数据准备与变量声明

假设我们要拼接一条工单信息,包含以下字段:工单号(字符串,最长 20 字符)、产线号(整数,1-9)、工位号(整数,1-99)、状态码(整数,0-99)、时间戳(字符串,格式 HH:MM:SS)。

变量声明如下:

strWorkOrder : STRING[20]; (* 工单号 *) iLineNum : INT; (* 产线号 *) iStationNum : INT; (* 工位号 *) iStatus : INT; (* 状态码 *) strTimeStamp : STRING[8]; (* 时间戳 *) strLineStr : STRING[2]; (* 产线号字符串 *) strStationStr : STRING[2]; (* 工位号字符串 *) strStatusStr : STRING[2]; (* 状态码字符串 *) strResult : STRING[60]; (* 最终工单信息 *) strTemplate : STRING[60]; (* 工单模板 *)

这里 strResult 声明为 60 字符,是因为工单号 20 + 产线号 2 + 工位号 2 + 状态码 2 + 时间戳 8 + 分隔符 6 + 前缀 4 = 44 字符,留了足够余量。

4.2 数值转字符串与补零处理

产线号、工位号、状态码都是整数,需要转成字符串并补零到固定两位。以工位号为例:

(* 先转成字符串 *) strStationStr := INT_TO_STRING(iStationNum); (* 如果长度不足两位,前面补零 *) IF LEN(strStationStr) < 2 THEN strStationStr := CONCAT('0', strStationStr); END_IF;

这段逻辑很直白:转字符串后检查长度,不足两位就在前面拼一个“0”。产线号和状态码同理。这种补零方式比用 REPLACE 更直观,适合字段数量不多的情况。

如果字段很多,可以写一个通用的补零函数块,传入整数和目标长度,返回补零后的字符串。这样主逻辑会更简洁。

4.3 模板定义与 REPLACE 填充

定义工单模板:

strTemplate := 'WO-XXXX-XX-XX-XX-XX:XX:XX';

这个模板中,“XXXX”是工单号占位符(实际工单号可能更长,这里只是示意),“XX”分别对应产线号、工位号、状态码、时、分、秒。

从右往左替换:

(* 替换秒 *) strTemplate := REPLACE(strTemplate, 2, 22, RIGHT(strTimeStamp, 2)); (* 替换分 *) strTemplate := REPLACE(strTemplate, 2, 19, MID(strTimeStamp, 4, 2)); (* 替换时 *) strTemplate := REPLACE(strTemplate, 2, 16, LEFT(strTimeStamp, 2)); (* 替换状态码 *) strTemplate := REPLACE(strTemplate, 2, 13, strStatusStr); (* 替换工位号 *) strTemplate := REPLACE(strTemplate, 2, 10, strStationStr); (* 替换产线号 *) strTemplate := REPLACE(strTemplate, 2, 7, strLineStr); (* 替换工单号 *) strTemplate := REPLACE(strTemplate, 4, 4, strWorkOrder);

这里的位置参数需要根据模板实际结构调整。上面示例中模板长度和占位符位置是假设的,实际使用时需要先数清楚每个占位符的起始位置。

4.4 拼接结果校验与输出

拼接完成后,建议加一段校验逻辑:

(* 检查结果长度是否异常 *) IF LEN(strResult) >= 60 THEN bWarning := TRUE; (* 结果可能被截断 *) END_IF; (* 检查是否还有未替换的占位符 *) IF FIND(strResult, 'X') > 0 THEN bWarning := TRUE; (* 存在未替换的占位符 *) END_IF;

FIND 函数用于查找子串位置,如果结果中还能找到“X”,说明有占位符没被替换掉。这个校验在模板调整后特别有用,能快速发现遗漏。

校验通过后,把 strResult 写入 HMI 显示寄存器或上传给上位机。写入时注意目标寄存器的字符串长度限制,避免在输出环节被二次截断。

5. 常见问题与排查技巧实录

5.1 拼接结果乱码或显示异常

乱码问题通常有三个原因:字符串长度声明不足导致截断、数值转字符串时编码不对、HMI 显示控件的字符集设置不匹配。

排查顺序:先在 PLC 侧监视 strResult 的实际内容,确认 PLC 内部字符串是否正确。如果 PLC 侧正常但 HMI 显示乱码,问题在 HMI 控件设置。如果 PLC 侧就是乱码,检查 INT_TO_STRING 的转换结果是否正常,以及 CONCAT 的输入参数是否都是有效的字符串变量。

我遇到过一次乱码是因为字符串变量声明为 STRING[10],但实际拼接结果有 12 个字符,多出的部分被截断后,最后一个字符的编码不完整,导致 HMI 显示异常。把声明长度改大后问题消失。

5.2 REPLACE 替换位置偏移

位置偏移是 REPLACE 使用中最常见的问题。根本原因通常是替换内容长度和被替换占位符长度不一致,导致后续占位符位置发生变化。

解决方法有两个:一是确保替换内容长度和占位符长度一致,比如占位符是“XX”两位,替换内容也必须是两位,不足补零。二是从右往左替换,这样即使长度变化,也不会影响还没替换的左侧占位符。

如果模板中占位符长度无法统一,建议放弃 REPLACE 方案,改用 CONCAT 逐段拼接。虽然代码多一些,但位置逻辑简单,不容易出错。

5.3 CONCAT 嵌套层级过深导致编译错误

三菱 ST 对嵌套调用的层级有限制,具体层数取决于编译器和 PLC 型号。嵌套过深时编译会报错,提示表达式过于复杂。

解决方法就是前面说的连续调用,把嵌套拆成多步。每步的结果存入临时变量,下一步再拼接。这样既避免了嵌套限制,又提高了可读性。

5.4 工单号含特殊字符导致拼接异常

工单号中如果包含“-”或“_”等分隔符,而模板中也用这些字符做分隔,REPLACE 替换时可能会误匹配。比如工单号是“WO-2024”,模板中也有“WO-XXXX”,替换工单号时可能把模板中的“WO-”也替换掉。

规避方法:模板占位符不要用工单数据中可能出现的字符。用“#”或“@”这类在工单号中不会出现的字符做占位符,替换时就不会误匹配。

5.5 常见问题速查表

问题现象可能原因排查方法解决措施
结果被截断字符串声明长度不足监视结果长度是否等于声明上限增大声明长度,留 20% 余量
显示乱码编码不匹配或截断PLC 侧和 HMI 侧分别监视检查字符集设置,确保长度足够
替换位置偏移替换内容长度不一致逐步监视每次 REPLACE 结果从右往左替换,或统一长度
编译报错嵌套层级过深查看编译错误信息拆成连续调用
占位符未替换替换逻辑遗漏用 FIND 检查结果中是否还有占位符补充替换逻辑
数值转字符串带空格转换函数默认行为监视转换结果用 TRIM 或手动截取

6. 性能优化与代码维护建议

6.1 减少不必要的字符串操作

字符串操作在 PLC 中是比较耗资源的,尤其是在高速扫描周期中。每增加一次 CONCAT 或 REPLACE,都会增加扫描周期的负担。

优化原则:能一次拼接完成的,不要分多次。比如四个字段拼接,如果嵌套调用可读性可以接受,就尽量用嵌套而不是连续调用。另外,工单信息如果不需要每个扫描周期都更新,可以加一个触发条件,只在数据变化时执行拼接逻辑。

6.2 字符串变量的复用策略

在工单拼接逻辑中,临时字符串变量可以复用。比如 strTemp1 在第一步拼接后,第二步可以覆盖使用。这样能减少变量声明数量,降低内存占用。

但复用变量会降低代码可读性,因为同一个变量在不同步骤代表不同含义。我的建议是:在逻辑简单、步骤少的场景中复用,在逻辑复杂、步骤多的场景中为每个中间结果声明独立变量,方便调试。

6.3 模板与逻辑分离

把工单模板定义成一个独立的字符串常量或初始化变量,不要硬编码在拼接逻辑中。这样修改模板格式时只需要改一个地方,不需要在大量代码中搜索替换。

如果模板有多种格式(比如不同产线用不同模板),可以用数组或条件选择来管理。模板和拼接逻辑分离后,代码的维护成本会大幅降低。

6.4 版本兼容性注意事项

不同型号的三菱 PLC 对字符串函数的支持程度不同。有些型号支持 FIND、MID、LEFT、RIGHT 等函数,有些型号只支持基础的 CONCAT 和 REPLACE。在编写工单拼接逻辑前,先确认目标 PLC 型号支持的函数列表。

如果目标型号不支持某些函数,可以用基础函数组合实现。比如 LEFT 可以用 MID 替代,RIGHT 可以用 MID 加 LEN 计算位置来替代。虽然代码会复杂一些,但兼容性更好。

我在实际项目中遇到过因为 PLC 型号不支持 FIND 函数,导致占位符校验逻辑无法实现的情况。后来改用逐字符比较的方式替代,虽然效率低一些,但功能不受影响。所以前期确认函数支持列表这一步,能省掉很多后期返工的时间。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/21 10:27:34

STM32入门纯软件仿真:不买开发板也能跑通GPIO、串口与ADC

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/21 10:26:08

从零构建图编辑器核心:diagram-design的数据结构与Canvas渲染实践

如果你做过前端可视化&#xff0c;或者帮团队搭过内部工具平台&#xff0c;大概率会有这种感受&#xff1a;画一个静态图表不难&#xff0c;难的是做一个“能拖、能连、能缩放、能撤销、能导出”的 diagram-design 系统。这个标题听起来像是一个普通的设计稿命名&#xff0c;但…

作者头像 李华
网站建设 2026/10/21 10:25:29

diagram-design 图表设计体系:从信息梳理到视觉统一的完整指南

1. 从一张草图到一套系统&#xff1a;diagram-design 到底在解决什么问题第一次看到 diagram-design 这个词&#xff0c;很多人会以为它只是一个画图工具的别名&#xff0c;或者某个设计模板库的代号。但真正在项目里摸爬滚打过几年的人会明白&#xff0c;它指向的是一套完整的…

作者头像 李华
网站建设 2026/10/21 10:24:08

35岁后才明白:同事是战友,领导是过客,横向关系才是职场隐性资产

35岁那年&#xff0c;我经历了工作以来最大的一次组织变动——整个部门被裁撤。那段时间我反复复盘一个问题&#xff1a;这些年我在职场投入最多的是什么&#xff1f;答案让我有点尴尬——是向上管理。我花了很多精力揣摩领导的意图、满足领导的期待&#xff0c;为一个口头表扬…

作者头像 李华
网站建设 2026/10/21 10:24:04

从态叠加原理到量子比特:狄拉克量子力学入门

在读量子力学之前&#xff0c;我书架上的物理书大体上分两类&#xff1a;一类是教科书&#xff0c;事无巨细地把知识点排得整整齐齐&#xff1b;另一类是专著&#xff0c;默认你已经懂了&#xff0c;然后在你面前把整个物理学重新推一遍。狄拉克的《量子力学原理》属于后者&…

作者头像 李华
网站建设 2026/10/21 10:24:01

artcraft:从审美到手工的约束推导与分层执行方法

1. 从“artcraft”这个名字说起&#xff1a;它到底想解决什么问题第一次看到“artcraft”这个标题&#xff0c;我脑子里蹦出来的第一反应是&#xff1a;这大概率不是一个单纯的艺术创作工具&#xff0c;也不是一个纯粹的手工教程合集。它更像是一个把“艺术表达”和“手工实现”…

作者头像 李华