☰
三菱ST字符串函数CONCAT与REPLACE在工单拼接中的选型与实战
2026/10/21 1:08:08 网站建设 项目流程

1. 工单拼接场景下,为什么字符串函数选型比想象中更关键

做过产线工单系统的人大概都有过这种体会:设备跑得好好的,逻辑也没问题,偏偏在生成工单号、拼接报警信息、组装追溯码这类"边角料"环节上翻车。三菱PLC的ST语言里,字符串处理函数不算多,CONCAT和REPLACE算是出场率最高的两个,但很多人对它们的理解停留在"能拼就行"的层面,结果到了实际项目里,工单号长度对不齐、替换位置算错、字符串超长截断这些问题一个接一个冒出来。

这篇内容就是围绕三菱ST字符串函数对比展开,重点聊CONCAT和REPLACE在工单拼接场景下的实际用法、选型逻辑和踩坑经验。适合正在用三菱GX Works3做ST编程的电气工程师、自动化从业者,也适合刚接触ST语言、想搞清楚字符串处理到底该怎么选函数的朋友。不管你是做单机设备还是整线MES对接,只要涉及工单号生成、条码拼接、报警文本组装,这里的内容都能直接拿去用。

先说一个反直觉的结论:在工单拼接场景里,CONCAT和REPLACE不是"二选一"的关系,而是"先拼后改"的配合关系。很多人一上来就想用REPLACE一步到位,结果发现位置计算复杂得要命;也有人全程只用CONCAT,遇到需要修改中间某段内容时就傻眼了。正确的思路是:用CONCAT搭骨架,用REPLACE做局部修正。这个思路贯穿全文,后面会结合具体案例一步步拆开讲。

另外要提前说明的是,三菱ST里的字符串函数和C语言、Python那套完全不是一回事。它的字符串类型是STRING,底层是定长数组,操作逻辑更接近"数组搬运"而不是"动态字符串处理"。理解这一点,后面很多"为什么这样设计"的问题就迎刃而解了。

2. CONCAT与REPLACE的底层行为差异:定长字符串带来的连锁反应

2.1 三菱ST的STRING类型到底是怎么存的

要搞懂这两个函数的差异,得先明白三菱ST里STRING类型的存储方式。它不像高级语言里的字符串那样是"长度+内容"的动态结构,而是一个定长字符数组。声明STRING(20)就意味着分配了20个字符的固定空间,实际内容不足20个字符时,剩余位置用空字符填充。

这个特性直接决定了CONCAT和REPLACE的行为边界。CONCAT拼接两个字符串时,如果结果长度超过目标变量的声明长度,超出部分会被直接截断,而且不会报错。我见过太多项目因为这个问题导致工单号后半段丢失,排查半天才发现是声明长度不够。

REPLACE的行为更微妙。它的作用是在源字符串的指定位置开始,用替换字符串覆盖指定长度的内容。注意这里说的是"覆盖",不是"插入"。如果替换字符串比被替换的部分长,多出来的字符会挤占后面的内容;如果短,则会在替换区域留下空字符。这个逻辑和很多人直觉中的"替换"不太一样,后面会专门用案例说明。

2.2 CONCAT的拼接逻辑与长度陷阱

CONCAT的函数签名大致是这样的:CONCAT(IN1, IN2, OUT),把IN1和IN2拼起来放到OUT里。看起来简单,但有几个细节必须注意。

第一,OUT的长度必须足够容纳拼接结果。假设IN1是STRING(10)存了"WO-2024",IN2是STRING(10)存了"-001",拼接结果是"WO-2024-001",共11个字符。如果OUT声明为STRING(10),最后一个"1"就没了。这种错误在编译时不会报,运行时也不报,只有你在HMI上看工单号时才会发现少了一位。

第二,CONCAT一次只能拼两个字符串。要拼三段内容,就得嵌套调用或者分两步走。比如工单号格式是"前缀+日期+序号",就得先拼前缀和日期,再把结果和序号拼。嵌套写法可读性差,分步写又占扫描周期,这个取舍后面会讲。

第三,空字符的处理。如果IN1或IN2里有空字符(比如未初始化的字符串变量),CONCAT会把空字符也当普通字符拼进去,导致结果中间出现"空洞"。实际项目中,建议在拼接前用LEN函数确认源字符串的实际长度,或者确保所有参与拼接的变量都已正确初始化。

2.3 REPLACE的覆盖机制与位置计算

REPLACE的签名通常是:REPLACE(IN, L, S, OUT),其中IN是源字符串,L是替换起始位置(从1开始计数),S是替换字符串,OUT是结果。它的行为是:从IN的第L个字符开始,用S的内容覆盖,覆盖长度等于S的长度。

这里有个关键点:REPLACE不改变字符串的总长度。它是在原有空间上做覆盖操作。如果S比IN中从L开始的剩余部分长,超出部分会被截断;如果S比剩余部分短,IN中未被覆盖的部分会保留在S后面。

举个例子:IN = "WO-2024-001",L = 4,S = "2025"。执行后OUT = "WO-2025-001"。因为从第4位开始,"2024"被"2025"覆盖了,后面的"-001"保持不变。但如果S = "25",结果就是"WO-25-4-001",因为"25"只覆盖了"20",剩下的"24"还在。这个行为在工单号年份替换、序号修改时特别容易踩坑。

2.4 两者在工单拼接中的角色分工

基于上面的分析,可以得出一个清晰的结论:CONCAT负责"从无到有"的组装,REPLACE负责"从有到改"的修正。

工单拼接的典型流程是:先从各个数据源(日期、班次、设备号、序号)收集信息,用CONCAT拼成一个完整的工单号或追溯码,然后在特定场景下(比如返修、补打、跨天续号)用REPLACE修改其中的某一段。这个分工不是人为规定的,而是由两个函数的底层行为决定的。

下面这张表对比了它们在工单场景下的关键特性:

对比维度CONCATREPLACE
核心作用拼接多个字符串覆盖指定位置内容
长度变化结果长度=各输入长度之和结果长度=源字符串长度
位置参数无必须指定起始位置
典型场景工单号组装、条码生成年份替换、序号修正
主要风险目标变量长度不足导致截断位置算错导致内容错位
是否改变原串否,输出到新变量否,输出到新变量

3. 工单号拼接的完整实操:从数据采集到字符串组装

3.1 工单号的典型结构拆解

在动手写代码之前,得先把工单号的格式定清楚。不同产线的工单号规则不一样,但常见的结构无非这几段:前缀标识 + 日期 + 班次 + 设备编号 + 流水号。比如"WO20240612A01001"这种,拆开就是:WO(工单前缀)、20240612(日期)、A(班次)、01(设备号)、001(流水号)。

为什么要拆这么细?因为每一段的来源不同。日期来自PLC的实时时钟,班次来自HMI上的选择开关,设备号是常量或配置参数,流水号是计数器。这些数据在PLC里的存储类型也不一样:日期是DATE或INT数组,班次是STRING或INT,流水号是INT或DINT。所以第一步不是拼字符串,而是把各种类型的数据统一转成字符串。

三菱ST里做类型转换主要靠INT_TO_STRING、DINT_TO_STRING这类函数。注意转换后的字符串长度是固定的,比如INT_TO_STRING转出来的结果可能带前导空格或固定位数,需要根据实际格式做处理。这一步的细节后面会专门讲。

3.2 用CONCAT分步组装工单号

假设我们已经把各段数据都转成了字符串,存在以下变量里:

  • sPrefix: STRING(4) := 'WO'
  • sDate: STRING(10) := '20240612'
  • sShift: STRING(2) := 'A'
  • sDevNo: STRING(4) := '01'
  • sSeq: STRING(6) := '001'

目标工单号存在sWorkOrder: STRING(30)里。用CONCAT分步拼接的代码如下:

// 第一步:拼接前缀和日期 CONCAT(sPrefix, sDate, sTemp1); // 第二步:拼接班次 CONCAT(sTemp1, sShift, sTemp2); // 第三步:拼接设备号 CONCAT(sTemp2, sDevNo, sTemp3); // 第四步:拼接流水号,得到最终工单号 CONCAT(sTemp3, sSeq, sWorkOrder);

这段代码逻辑清晰,每一步的结果都能在线监控,排查问题很方便。缺点是占用了三个临时变量,而且扫描周期比嵌套写法略长。但在工单拼接这种非高频操作里,这点开销完全可以接受。可读性和可维护性比省那几个微秒重要得多。

如果非要用嵌套写法,可以这样:

CONCAT(CONCAT(CONCAT(CONCAT(sPrefix, sDate, sTemp1), sShift, sTemp2), sDevNo, sTemp3), sSeq, sWorkOrder);

说实话,这种写法除了显得"技术好"之外没有任何好处。一旦中间某段格式要调整,改起来非常痛苦。我在实际项目里一律用分步写法,临时变量命名清楚,后面接手的人也能看懂。

3.3 数字转字符串时的位数对齐问题

工单号里最容易被忽视的就是位数对齐。流水号"1"和"001"在视觉上完全不同,但INT_TO_STRING(1)转出来就是"1",不会自动补零。如果工单号要求固定位数,就必须手动补零。

补零的常用做法是用REPLACE在数字前面插入零。比如流水号转出来是"1",要变成"001",可以这样做:

// 先把流水号转成字符串 INT_TO_STRING(iSeq, sSeqRaw); // 在开头插入两个零 CONCAT('00', sSeqRaw, sSeqPadded);

但如果流水号是"12",这样拼出来就是"0012",多了一位。所以更稳妥的做法是根据实际长度动态补零:

// 计算需要补几个零 iLen := LEN(sSeqRaw); iPadCount := 3 - iLen; // 根据补零个数选择拼接方式 IF iPadCount = 2 THEN CONCAT('00', sSeqRaw, sSeqPadded); ELSIF iPadCount = 1 THEN CONCAT('0', sSeqRaw, sSeqPadded); ELSE sSeqPadded := sSeqRaw; END_IF;

这段逻辑看起来有点笨,但在ST环境里是最直接可靠的。三菱ST没有高级语言里那种PADLEFT函数,补零只能自己动手。我试过用循环拼接的方式,代码更简洁但可读性差,而且循环次数不确定时容易出问题。对于工单号这种位数固定的场景,直接判断反而更稳。

3.4 拼接结果的验证与边界检查

工单号拼完之后,必须做长度验证。我见过一个项目,工单号声明为STRING(20),正常情况下拼出来是18位,没问题。但有一次日期格式从"20240612"变成了"2024-06-12"(多了两个横杠),结果变成20位,刚好卡在边界上。后来又加了个班次后缀,直接超长截断,工单号最后一位没了,导致MES系统拒收。

验证的方法很简单:

// 检查工单号长度是否符合预期 IF LEN(sWorkOrder) <> 18 THEN bWorkOrderError := TRUE; // 触发报警或记录日志 END_IF;

更严格的做法是检查每一段的长度和内容格式。比如日期段必须是8位数字,班次段必须是单个字母。这些检查在调试阶段可能觉得多余,但到了现场运行阶段,就是这些检查帮你挡住了一次次潜在故障。

注意:LEN函数返回的是字符串的实际长度,不包括填充的空字符。但如果字符串中间有空字符,LEN会把它算进去。所以拼接前确保所有源字符串都是"干净"的,没有中间空字符。

4. REPLACE在工单修正中的实战:位置计算与常见误用

4.1 什么时候该用REPLACE而不是重新拼接

工单拼接完成后,并不是就一劳永逸了。实际产线里经常遇到需要局部修改的场景:

  • 跨天续号:工单号里的日期段需要从"20240612"改成"20240613"
  • 返修工单:在原有工单号后面追加"-R"标识,但要求总长度不变
  • 班次切换:把班次段从"A"改成"B"
  • 设备变更:工单从一台设备转到另一台,设备号需要替换

这些场景如果重新用CONCAT拼一遍,需要把所有源数据重新收集一遍,代码冗长且容易出错。用REPLACE直接修改目标位置,效率高得多。

但REPLACE的坑也在这里:位置计算必须精确到每一个字符。工单号"WO20240612A01001"里,日期段从第3位开始,到第10位结束。如果要从第3位开始替换8个字符,L参数就是3。这个计算看起来简单,但工单号格式一变,所有位置都要重新算。

4.2 日期段替换的完整案例

假设当前工单号是sWorkOrder := 'WO20240612A01001',需要把日期改成"20240613"。日期段从第3位开始,长度8位。替换代码如下:

// 新日期字符串 sNewDate := '20240613'; // 从第3位开始替换,替换长度为8 REPLACE(sWorkOrder, 3, sNewDate, sWorkOrderNew);

执行后sWorkOrderNew就是"WO20240613A01001"。看起来很简单,但有几个细节要注意。

第一,REPLACE的第三个参数是替换字符串,不是替换长度。替换的长度由替换字符串的长度决定。所以如果sNewDate是"20240613",就替换8个字符;如果是"2024",就只替换4个字符,后面的"0612"会保留,结果变成"WO20240612A01001"里的"2024"被覆盖成"2024",等于没变。这个行为很容易让人误以为REPLACE有"替换长度"参数。

第二,替换位置从1开始计数。三菱ST的字符串函数位置参数都是从1开始的,不是从0。这一点和C语言习惯不同,写惯了C的人容易在这里栽跟头。

第三,替换后的字符串长度不变。如果新日期比原日期短,比如把"20240612"替换成"2024",结果是"WO20240612A01001"变成"WO20240612A01001"——等等,这里需要仔细算:从第3位开始,用"2024"覆盖4个字符,原来的"2024"被覆盖成"2024",后面的"0612"保持不变,所以结果还是"WO20240612A01001"。如果新日期是"2025",结果就是"WO20250612A01001"。这个逻辑必须理解清楚,否则替换结果会和预期完全不一样。

4.3 位置参数算错的典型表现与排查

位置算错是REPLACE最常见的误用。表现通常有两种:替换到了错误的位置,或者替换后内容错位。

比如想把班次从"A"改成"B",班次在工单号里的位置是第11位。如果误写成从第10位开始替换,就会把日期最后一位"2"覆盖成"B",结果变成"WO2024061BA01001",班次没改对,日期还坏了。

排查这类问题的方法很直接:在替换前后分别打印或监控字符串内容。三菱的编程软件支持在线监控字符串变量,但字符串显示可能不完整,建议用LEN确认长度,再逐段截取检查。截取可以用MID函数,比如MID(sWorkOrder, 3, 8, sDateCheck)就能把日期段取出来单独看。

更稳妥的做法是把位置参数定义成常量或变量,而不是直接写数字。比如:

// 定义各段在工单号中的起始位置 cDatePos := 3; cShiftPos := 11; cDevPos := 12; cSeqPos := 14; // 替换时引用这些常量 REPLACE(sWorkOrder, cDatePos, sNewDate, sWorkOrderNew);

这样工单号格式调整时,只需要改常量的值,不用满代码找数字。这个习惯在大型项目里能省下大量调试时间。

4.4 REPLACE与CONCAT配合的进阶用法

前面说过,CONCAT和REPLACE是配合关系。一个典型的配合场景是工单号追加后缀但保持总长度不变。

假设工单号固定18位,现在要追加"-R"表示返修,但不能超过18位。思路是:先用CONCAT拼出带后缀的完整工单号,再用REPLACE把超出部分截掉。但REPLACE本身不能截断字符串,它只能覆盖。所以更实际的做法是:用CONCAT拼出完整工单号,然后检查长度,如果超长就用MID截取前18位。

这里就引出了另一个函数MID,虽然标题只提了CONCAT和REPLACE,但实际工单拼接场景里,MID、LEN、LEFT、RIGHT这些函数都是配套使用的。单独用CONCAT和REPLACE能完成大部分工作,但加上这些辅助函数,代码会更健壮。

// 拼接返修后缀 CONCAT(sWorkOrder, '-R', sWorkOrderTemp); // 检查长度,超长则截取 IF LEN(sWorkOrderTemp) > 18 THEN MID(sWorkOrderTemp, 1, 18, sWorkOrderFinal); ELSE sWorkOrderFinal := sWorkOrderTemp; END_IF;

这段代码的逻辑是:先拼,再检查,最后截断。虽然多了几步,但保证了工单号长度始终符合MES系统的要求。

5. 字符串长度管理与内存占用:定长数组的隐藏成本

5.1 STRING声明长度对程序的影响

三菱ST里声明一个STRING(30)变量,占用的内存是30个字符加上一个长度字节(具体实现可能因PLC型号而异)。这个空间是静态分配的,不管实际存了多少字符,都占这么多。所以声明长度不是随便写的,写大了浪费内存,写小了截断数据。

在工单拼接场景里,工单号、临时变量、各段数据都需要声明STRING。如果临时变量声明得太大,比如每个都STRING(50),在小型PLC上可能就会遇到内存紧张的问题。我一般的原则是:按实际最大可能长度加2到3个字符的余量来声明。

比如工单号最大18位,就声明STRING(20)。日期段固定8位,声明STRING(10)。流水号最大6位,声明STRING(8)。这样既不会截断,也不会浪费太多。

5.2 临时变量复用的技巧与风险

为了省内存,有人会把临时变量复用,比如sTemp1在第一步用完后,第二步接着用。这样做确实省空间,但风险是如果前一步的结果还需要后续使用,复用就会覆盖掉。

我的建议是:工单拼接这种非高频、非性能敏感的场景,不要复用临时变量。每个临时变量只做一件事,命名清楚,比如sTempPrefixDate、sTempPrefixDateShift。这样代码可读性好,调试时也能清楚看到每一步的结果。省那几个字节的内存,换来的是调试时间的成倍增加,不划算。

如果确实内存紧张,可以考虑用数组+循环的方式处理,但ST语言里字符串数组的操作比较麻烦,不如直接多声明几个变量来得直接。

5.3 字符串操作对扫描周期的影响

字符串操作在PLC里属于"重"操作,因为涉及内存搬运。CONCAT和REPLACE每次执行都要复制字符数组,长度越长,耗时越多。在高速产线上,如果工单拼接放在主循环里每个扫描周期都执行,可能会影响扫描周期。

正确的做法是用触发条件控制字符串操作的执行。比如只在工单号需要更新时才执行拼接,其他时间跳过。可以用上升沿触发或者状态机控制:

// 只在工单更新请求的上升沿执行拼接 IF bWorkOrderUpdate AND NOT bWorkOrderUpdatePrev THEN // 执行拼接逻辑 CONCAT(sPrefix, sDate, sTemp1); // ... 后续拼接 END_IF; bWorkOrderUpdatePrev := bWorkOrderUpdate;

这样字符串操作只在需要时执行一次,不会持续占用扫描周期。实测下来,18位工单号的完整拼接(4次CONCAT)在主流三菱PLC上耗时大约几十微秒,单次执行完全无感,但如果每个扫描周期都跑,累积起来就不可忽视了。

6. 现场调试中那些文档不会写的经验

6.1 字符串显示不全导致的误判

三菱的编程软件在线监控字符串时,显示长度可能有限制。有时候工单号明明拼对了,但监控画面上只显示前几位,让人误以为后面丢了。遇到这种情况,不要急着改代码,先用LEN确认实际长度。如果LEN返回的值是对的,那就是显示问题,不是逻辑问题。

我一般会在调试阶段加一段临时逻辑,把关键字符串的长度和内容写到HMI上,或者用MID分段显示。这样能避免很多"看起来错了其实没错"的误判。

6.2 空字符引发的拼接异常

未初始化的STRING变量里可能包含随机数据或空字符。如果直接拿来做CONCAT,结果可能莫名其妙。所有参与拼接的字符串变量,在使用前必须初始化。可以在程序开头统一赋空字符串:

sPrefix := ''; sDate := ''; sShift := ''; // ... 其他变量

三菱ST里给STRING赋空字符串就是赋两个单引号,这样变量里就是干净的空字符填充,不会带入随机数据。

6.3 工单号跨天续号的边界处理

跨天续号是工单系统里比较绕的场景。比如夜班从晚上8点到早上8点,工单号里的日期段应该用哪个日期?如果简单用PLC当前日期,到了零点日期就变了,但工单还没结束。

常见的处理方式是:用班次开始时间来判断日期。如果当前时间在夜班范围内,且小时数小于班次开始小时,日期就用前一天。这个逻辑用ST写起来不复杂,但需要和字符串替换配合:

// 判断是否需要使用前一天日期 IF iCurrentHour < cNightShiftStartHour THEN // 日期减一天 iWorkDate := iCurrentDate - 1; ELSE iWorkDate := iCurrentDate; END_IF; // 把日期转成字符串并替换到工单号里 INT_TO_STRING(iWorkDate, sNewDate); REPLACE(sWorkOrder, cDatePos, sNewDate, sWorkOrderNew);

这里的坑在于日期减一天不是简单的数字减1,因为月份和年份的边界需要处理。三菱ST里有日期运算函数,但用起来不如高级语言方便。实际项目中,我倾向于用PLC的时钟指令直接读取"班次开始时刻"的日期,而不是自己做日期运算。

6.4 与MES系统对接时的字符串格式校验

工单号最终要传给MES或上位机,格式必须严格匹配。常见的校验点包括:总长度、各段分隔符、字符集(只能包含数字和大写字母)、校验位等。

在PLC侧做校验比在上位机侧做更主动。可以在工单号生成后加一段校验逻辑:

// 检查工单号是否只包含允许的字符 bValid := TRUE; FOR i := 1 TO LEN(sWorkOrder) DO MID(sWorkOrder, i, 1, sChar); // 检查sChar是否在允许范围内 IF NOT IsValidChar(sChar) THEN bValid := FALSE; EXIT; END_IF; END_FOR;

这段代码里的IsValidChar需要自己实现,判断字符是否是数字或大写字母。虽然增加了代码量,但能在工单号发出去之前就发现问题,避免MES拒收后再回头排查。

6.5 版本兼容性:不同PLC型号的字符串函数差异

三菱的不同PLC系列(比如FX和Q系列)在字符串函数的支持上可能有细微差异。有的型号CONCAT支持嵌套调用,有的不支持;有的REPLACE的位置参数从0开始,有的从1开始。这些差异在跨平台移植时特别容易出问题。

我的经验是:在项目初期就确认目标PLC型号的函数手册,把关键函数的参数定义、返回值、边界行为都搞清楚。如果项目可能移植到不同型号,尽量用最基础的函数组合,避免依赖特定型号的扩展功能。比如补零操作,用CONCAT和判断逻辑实现,比用某个型号特有的PAD函数更保险。

7. 从工单拼接延伸出去的字符串处理思路

工单拼接只是字符串处理的一个缩影。同样的思路可以套用到报警文本组装、追溯码生成、配方名称拼接等场景。核心逻辑都是一样的:先明确目标格式,再拆解数据来源,然后用CONCAT搭骨架,用REPLACE做修正,最后加校验。

我在实际项目里养成了一个习惯:把字符串格式定义成常量或配置数据,而不是硬编码在逻辑里。比如工单号的各段位置、长度、允许字符集,都放在一个结构体或数据块里。这样格式调整时只改配置,不动逻辑,维护成本大幅降低。

另外,字符串操作虽然看起来简单,但在PLC环境里有很多"反直觉"的地方。定长数组、位置从1开始、替换不改变长度、超长静默截断,这些特性都需要在实际项目中反复体会。我踩过的坑包括:工单号超长导致MES拒收、替换位置算错导致日期错乱、临时变量复用导致数据覆盖、空字符导致拼接结果异常。每一个坑都花了不少时间排查,但搞清楚之后,再遇到类似场景就能快速定位。

如果你正在做三菱ST的工单系统,建议先把CONCAT和REPLACE的行为用一个小Demo验证一遍,把各种边界情况都试出来。比如故意让目标变量长度不足,看看截断发生在哪里;故意把替换位置写错,看看结果怎么变。这些实验花不了多少时间,但能帮你建立起对这两个函数的"肌肉记忆",后面写代码时自然就会避开那些坑。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询