做设备管理的项目做久了,你会发现一个规律:越是看起来不起眼的功能,越容易在关键时候卡你一下。三菱PLC的ST语言里,字符串处理就是典型例子。工单编号拼接、批次号生成、MES数据上报、刀具寿命追溯——这些现场最常见的需求,全都压在CONCAT、REPLACE这几个字符串函数上。很多人写梯形图写了几年,一碰到ST语言里的字符串操作就发怵,总觉得不如C语言顺手。其实三菱的ST字符串函数没有你想象的那么“反人类”,只是它的参数风格和习惯用法跟通用编程语言有区别。这篇文章就围绕工单拼接这个真实场景,把CONCAT和REPLACE这两个函数的使用逻辑、参数细节、常见坑全部过一遍,顺便聊聊我在FX5U平台上实测下来的一些心得。内容主要面向设备工程师和调试人员,不管你是刚接触ST语言,还是已经在项目里被字符串折磨过几次,看完应该都能直接照着做。
1. 为什么工单拼接要单独拿出来说
1.1 工单拼接到底解决什么问题
工单拼接不是一个演示用的“Hello World”,它直接对应现场的追溯体系。比如一条自动化产线,每加工一个工件,系统要把订单号、物料编码、炉批号、生产日期、班次、序号全部拼成一个唯一的序列号,然后通过扫码枪或MES接口上传。这个序列号一旦拼错一位,追溯链就断了,后面客户投诉、质量问题回溯全部没法做。更麻烦的是,工单格式往往不是一成不变的——客户改了编码规则、现场换班需要追加班次标识、日期格式要从“20240901”改成“24-09-01”,这些改动都要在PLC程序里快速完成。CONCAT负责把零散字段拼起来,REPLACE负责在已有字符串里做局部替换修正,两者组合起来,基本可以覆盖工单拼接的绝大多数场景。我见过不少工程师为这个需求去写一大堆MOV指令配合ASCII码换算,代码又长又难维护,其实用ST字符串函数几行就搞定了。
1.2 三菱ST语言字符串处理能力概览
三菱的ST语言(结构化文本)在GX Works3里被定位成IEC 61131-3标准语言,但它在字符串处理上做了一些自己的封装。最核心的一点是:字符串在三菱ST里不是“字符数组”,而是一个带长度的STRING类型,长度上限在变量声明时确定。这一点跟C语言差别很大,写惯了C的工程师往往会在长度上限上吃亏。三菱ST内置的字符串函数主要包括CONCAT(拼接)、REPLACE(替换)、LEFT/RIGHT/MID(截取)、INSTR(查找)、LEN(取长度)等,基本覆盖了日常需求。但它的函数命名和参数顺序跟西门子SCL、跟C语言标准库都不一样,比如REPLACE的入参是“原字符串+起始位置+替换长度+替换内容”,而不是C语言里那种“全量字符串替换”。这种差异如果不注意,写出来的程序就是逻辑正确但结果完全不对。
1.3 CONCAT与REPLACE的分工定位
在工单拼接这个场景里,CONCAT和REPLACE承担的是不同阶段的工作。CONCAT负责“从零到一”的构建:把多个独立字段按顺序装填到一个目标字符串里。REPLACE负责“从一到优”的修正:工单拼完之后发现格式不对、中间某段需要换成新值,或者要在固定位置插入工厂代号,这时候CONCAT未必方便,REPLACE反而更合适。举个例子,一个工单已经拼成“P12345-240901-A-001”,现在客户要求把中间的日期段从“240901”改成“24-0901”,你不可能把整个字符串拆开重新拼一遍,REPLACE直接从指定位置替换就行了。实际项目里我经常把两者串起来用:先用CONCAT拼出基础工单,再用REPLACE做格式修正。这个思路能让程序结构清晰很多,现场改规则的时候也不用翻一大片梯形图。
2. 三菱ST字符串函数核心解析
2.1 CONCAT函数语法与特性
CONCAT在三菱ST里的作用是把两个字符串首尾相接合成一个。它的基本调用方式是直接在ST程序里写result := CONCAT(str1, str2);,其中str1和str2既可以是字符串变量,也可以是字符串字面量。有一点必须强调:三菱ST的CONCAT只支持两个入参,这一点跟其他编程语言里的多参数拼接不同。你想拼三个字段,就得嵌套调用:CONCAT(CONCAT(a, b), c)。我见过有人在GX Works3里试着写CONCAT(a, b, c),编译直接报错,还以为是软件出问题了。另外,三菱ST的赋值符号是:=,不是等号,很多初学者在这上面栽跟头。CONCAT的拼接结果要写到一个STRING变量里,这个变量的长度声明必须足够大,否则运行时会把超出部分截断,而且不会有任何报警提示。我在FX5U上实测过,声明的字符串长度是16个字符,结果拼了20个字符进去,最后只保留前16个,后面静默丢掉。这种“安静的截断”是最坑人的,因为它不会让程序停跳,也不会触发错误标志,只有你核对数据内容时才会发现不对。所以用CONCAT之前,先算清楚最长会拼出多长的结果,尽量给目标字符串留出冗余余量。还有一个实用技巧:CONCAT可以处理字符串变量和字面量的混合拼接,比如result := CONCAT('P', CONCAT(partNo, '-A'));这种写法在构建固定格式头时非常方便,不需要为每个固定段单独建变量。
2.2 REPLACE函数语法与特性
REPLACE在三菱ST里的作用是,从源字符串的指定位置开始,把指定长度的字符替换成新内容。它的调用格式是result := REPLACE(source, startPos, replaceLen, newStr);。注意这里的startPos从1开始计数,不是0,这是跟C语言差异最大的一点。比如源字符串是“P12345-240901-A-001”,想从第8个字符开始替换4个字符,把“2409”换成“24-0”,写出来就是REPLACE(src, 8, 4, '24-0')。实际执行效果是把原位置4个字符先去掉,再插入4个新字符,长度保持不变。但如果新的内容跟被替换长度不一致,结果字符串的长度就会变化。比如替换长度为4,新内容是“24-09-01”共8个字符,那最终字符串就会变长。这意味着REPLACE不只是“替换”,它本质上是“先删除再插入”。理解了这一点,很多边界问题都能想通。REPLACE的startPos加上replaceLen如果超出源字符串总长度,三菱的处理是替换到字符串末尾为止,不会报错。但如果你startPos本身大于源字符串长度,那REPLACE会直接返回源字符串,等于什么都没发生。这个行为我专门测试过,不同版本固件可能略有差异,稳妥的做法是在调用前用LEN函数自己判断一下,避免在这种边界上出幺蛾子。REPLACE还有一个经常被忽略的用途:它可以在指定位置“插入”内容。只要把replaceLen设为0,比如REPLACE(src, 5, 0, '-X'),意思是“在第5个字符位置前插入'-X'”。这招在做工单扩展时特别好用,不用重新拼接整个字符串。
2.3 两者对比与选型原则
CONCAT和REPLACE虽然都跟字符串变长变短有关,但它们的适用场景有清晰边界。我整理了一个对比表,项目里用的时候可以直接对着选:
| 对比维度 | CONCAT | REPLACE |
|---|---|---|
| 核心动作 | 末尾追加拼接 | 指定位删除并插入 |
| 入参个数 | 2个字符串 | 4个参数(源串+位置+长度+新串) |
| 位置计数 | 无位置概念 | 从1开始 |
| 是否改变长度 | 结果长度=两串长度之和 | 取决于替换长度与新内容长度的差值 |
| 典型用途 | 多字段组装、拼接头尾固定段 | 格式修正、局部替换、指定位置插入 |
| 边界风险 | 目标变量长度不足会静默截断 | 起始位置越界会原样返回 |
| 多段拼接 | 需要嵌套调用 | 不需要,但一次只处理一个位置 |
选型原则其实很简单:如果你手上是“一堆要组装的数据字段”,那就用CONCAT一层层套;如果你手上是“一个已经存在的字符串,需要在特定位置动刀”,那就用REPLACE。两者不是竞争关系,更多是配合关系。我实际项目里最常见的手法是用CONCAT拼好基础工单,然后用REPLACE做日期格式修正、在固定位置插入工厂代码。这样分工明确,程序可读性也高。还有一点要注意,三菱ST里的字符串比较不是用双等号,而是用=,但字符串比较时是否会区分大小写取决于CPU固件设置,FX5U默认不区分大小写,Q系列某些版本区分。这个差异在做工单判重、扫码对比时非常关键,建议在项目早期就把这个行为确定下来,并写在设计文档里。
3. 实操:基于FX5U的工单拼接完整案例
3.1 场景需求与变量规划
我把这个案例设定成一条真实的自动化装配线场景:每完成一个工件,PLC需要生成一个唯一工单号,格式为“工厂代码-订单号-日期-班次-序号”。具体要求是工厂代码固定为“F01”,订单号由上位机下发的D100~D103四个16位字组成,日期从PLC时钟读取,格式是“YYMMDD”,班次是一个字符“A”或“B”,序号是计数器C0的当前值,要求补足3位,不足前面补0。最终工单示例:F01-2235-240915-A-007。拿到这个需求,第一步不是写代码,而是规划变量。在GX Works3的ST程序里新建局部变量,我做了如下声明:
SrcOrder : STRING(8)接收订单号字符转换结果DateStr : STRING(8)日期字符串ShiftStr : STRING(2)班次标识SeqStr : STRING(4)序号字符串WorkNo : STRING(32)工单最终结果TempStr : STRING(32)中间拼接缓冲区
这里有个细节:WorkNo声明为STRING(32)而不是STRING(24),表面上看24个字符就够用了,但我故意留了8个字符的余量。因为现场规则随时可能变,比如客户后来要在订单号后面加产品版本号,如果长度刚好卡死,改起来就要动数据结构,留余量能省掉一次程序升级。这种习惯我建议从刚开始写ST就养成——字符串长度是程序里最不该“精打细算”的资源。另外,三菱的STRING类型在声明时括号里的数字指的是“字符个数”,不是字节数,中文字符要特别注意。FX5U的字符串内部按UTF-8或者UCS-2编码取决于设置,但STRING长度计数是按字符算的,一个中文算一个字符。细节后面问题排查部分会专门说。
3.2 使用CONCAT实现基础拼装
变量规划好后,就可以写ST逻辑了。第一步是把D100~D103里的订单号拼成一个字符串。上位机下发的订单号是数值型,不能直接进CONCAT,需要先用整数转字符串函数INT_TO_STRING。三菱ST里这个函数的名称是INT_TO_STRING或DINT_TO_STRING,具体看数据类型。这里有一个关键点:INT_TO_STRING转换出来的字符串是“原样数字”,不会自动带千分位,也不会补前导0,所以订单号是2235就直接转换出“2235”。如果订单号本身有前导0需求,比如要求显示“02235”,那就得自己补0。接下来写基础拼接程序:
WorkNo := 'F01-'; WorkNo := CONCAT(WorkNo, INT_TO_STRING(D100)); TempStr := CONCAT(WorkNo, '-'); WorkNo := CONCAT(TempStr, DateStr); TempStr := CONCAT(WorkNo, '-'); WorkNo := CONCAT(TempStr, ShiftStr); TempStr := CONCAT(WorkNo, '-'); WorkNo := CONCAT(TempStr, SeqStr);这段代码的写法比较“笨”,但逻辑非常透明。每拼一段就放到TempStr过渡,再继续拼下一段。实际程序里你也可以用更紧凑的嵌套写法,比如WorkNo := CONCAT(CONCAT(CONCAT('F01-', INT_TO_STRING(D100)), '-'), CONCAT(DateStr, CONCAT('-', ShiftStr)));一次完成,但嵌套太多层之后,现场维护的人读起来会非常吃力。我建议的原则是:如果拼接段数超过3段,就老老实实用中间变量分步拼,牺牲一点代码美观度,换来的是调试时可读性。拼装完之后,WorkNo的内容就是“F01-2235-240915-A-007”这样的字符串了。但这个程序有个潜在问题:DateStr、SeqStr从哪里来还没处理,下一节专门讲格式修正。
3.3 使用REPLACE修正工单格式
日期字符串和序号补零,这两个环节是REPLACE真正发挥价值的地方。先说日期。三菱PLC读取时钟可以用内置函数CLOCK或直接读特殊寄存器D8018(年)、D8019(月)、D8020(日),FX5U还能通过RD_TIMER读取。为了拿到“YYMMDD”的紧凑格式,我先把年月日分别转成字符串,然后拼接,但这里会遇到一个典型问题:某个月是9月时,转换结果只是“9”,而要的是“09”。两个办法:一个是拼接前判断,月数小于10就在前面补0;另一个就是用REPLACE。我推荐第二种思路,更通用。做法是先拼一个占位串“YYMMDD”,比如先用字符串“240900”,然后通过REPLACE把月段位置的内容替换掉。具体流程是预先准备年月日各自补好0的字符串,然后拼到一起。补零本身可以用REPLACE实现:MonthStr := REPLACE('00', 2, 1, INT_TO_STRING(monthVal));,意思是在字符串“00”的第2个位置,替换1个字符,把月份数字放进去。这个方法比IF判断嵌套简洁得多。序号补零同理,计数器值是7,要显示“007”,可以先准备“000”,然后用REPLACE('000', 3, 1, INT_TO_STRING(countVal))把最后一位替换成7,得到“007”。但如果计数器值超过9,变成两位数,替换1个位置就不够了。更稳妥的方法是用CONCAT:先判断序号是否大于99,小于10就拼两个0,小于100就拼一个0。REPLACE更适合固定位置的格式修正,补零这种“头部填充”需求用CONCAT拼前导零更直观。所以在这个例子里,我的最终实现是:日期用REPLACE修月日格式,序号用CONCAT补前导0。两者结合的思路是:能用REPLACE在固定位置动刀的地方就用REPLACE,能用CONCAT在头部做填充的地方就用CONCAT。这也是我前面说的“分工定位”在实际代码里的落地。
3.4 关键参数与执行细节
整个拼装程序写完后,有两个执行细节必须落实。第一是字符串变量的初始化。三菱ST里的STRING变量如果声明后没有赋值,初始内容是空字符串,但如果你是复用中间变量,上一次执行残留的内容会留在里面。比如TempStr在扫描周期1里保存了“F01-2235-”,如果扫描周期2执行时,拼接流程只走了前半段就跳过了,TempStr还是旧内容,后续把这个变量拿来当基座继续拼,结果就会叠加上历史值,工单号越变越长。我的习惯是在每个扫描周期开始,把所有用到的字符串中间变量统一清空,写成TempStr := '';。这个习惯救了我不止一次。第二是字符串拼接的扫描周期问题。CONCAT函数在ST里是当扫描周期内立即执行的,不存在异步延迟,所以不用担心跨周期数据不一致。但如果工单号生成后马上要发给上位机,建议在工单号生成完成位置加一个“生成完成”标志位,上位机看到标志位再读字符串。不要在生成过程的中间节点去读工单号,那样读到的可能是半成品。另外,把工单号写入HMI或触摸屏时,注意触摸屏侧的字符串变量长度也要留够,不然显示会被截断。我在一个项目里见过PLC侧WorkNo是STRING(32),但HMI组态时那个变量只设了16个字符,结果界面显示就少了后半段,排查了半天还以为是通讯问题——其实就是两边长度定义不一致。
4. 常见问题与排查技巧实录
4.1 字符串长度到底炸没炸
字符串长度溢出是工单拼接里最隐蔽的问题。很多工程师有个错觉:GX Works3编译都通过了,程序运行也正常,就觉得字符串没毛病。但实际上,编译检查只能确认语法和数据类型匹配,不会检查运行时长度。我在2.1节提到过,字符串写入超过STRING声明的字符数时,多出来的部分会被直接截断,CPU不报任何错误。这个问题在调试界面特别有迷惑性——你在监视窗口看WorkNo的值,看到的是“F01-2235-240915-A”,以为程序只生成了这么多,其实是因为后面“-007”被截掉了。排查方法很简单:在最终赋值的位置再加一个LEN(WorkNo)的监视,比对实际长度跟期望长度是否一致。更稳妥的做法是在程序里做显式检查:IF LEN(WorkNo) <> ExpectedLen THEN,一旦长度不对就置一个报警位。还有一个小技巧:把字符串变量声明得比预期长一些之后,如果现场规则变化导致拼接内容变长,程序大概率不会炸,但如果声明的时候刚好卡死,任何一个小改动都可能带来连锁问题。经验值是留出20%到30%的余量。
4.2 中文乱码与编码问题
工单拼接遇到中文,是另一个重灾区。三菱FX5U的字符串内部编码方式在不同固件版本和不同设置下有差异,GX Works3里字符串默认按UTF-8处理,但如果你把字符串通过MODBUS TCP传给上位机,上位机那边如果按GBK解码,中文就会乱码。再加上STRING长度的“字符数”和“字节数”不一致的问题——UTF-8编码下,一个中文占3个字节,但在STRING里计1个字符——就很容易出现长度判断和实际传输字节数不一致的情况。我在项目里遇到过:工单里含客户中文名称,PLC侧LEN函数返回13,但发到上位机的报文里字节数却是35,上位机按协议解析就错位了。解决思路是:工单号这类要跨系统传输的数据,尽量用纯ASCII字符;如果实在要带中文,一定要把编码格式在通讯协议里写死,并要求上位机按UTF-8解析。另外要注意,三菱ST的字符串比较函数在处理中文时也受编码影响,两个看起来一样的字符串用=比较返回FALSE,往往就是因为编码细节不一致。建议中文只用于界面显示,不参与逻辑判断和工单生成。
4.3 REPLACE的边界行为
REPLACE函数的边界行为,我在2.2节提了一部分,但这里要专门展开。当startPos等于源字符串长度+1且replaceLen为0时,REPLACE的效果等同于在字符串末尾追加内容。这个特性可以用来做追加,但我实测时发现在FX5U固件版本1.210以下,这个用法偶尔会触发异常,建议不要在关键逻辑里用REPLACE做末尾追加,老老实实用CONCAT。当replaceLen大于源字符串从startPos开始到末尾的剩余长度时,三菱的处理是把剩余部分全部替换掉。比如源字符串“ABCDEFGH”,从第3个位置开始替换6个字符,新串是“XY”,结果会变成“ABXY”,F到H会被直接删除——因为原位置只剩6个字符(CDEFGH),全部被替换成2个新字符。这个行为如果没意识到,很容易在计算最终长度时出错。我踩过的坑是:用一个固定长度的替换内容去修正工单,结果工单长度因边界截断变短了,后续提取子串的MID函数位置全部错位。从那以后,我写REPLACE之前都会先用LEN确认源字符串长度和替换范围,避免想当然。
4.4 工单拼接问题速查表
把我在多个项目里实际遇到过的字符串问题整理成一个速查表,下次可以直接对照排查:
| 现象 | 可能原因 | 排查要点 |
|---|---|---|
| 工单号后半段莫名消失 | 目标STRING声明长度不足 | 检查WORKNO的声明和LEN返回值 |
| 两个工单号看起来一样但比较不等 | 编码或尾随空格差异 | 用INSTR/LEN核对尾部字符 |
| REPLACE后长度不符合预期 | replaceLen设置过大或startPos越界 | 计算替换区段实际剩余长度 |
| 字符串拼接后偶尔出现前次残值 | 中间变量未清零 | 周期开始时统一清空字符串变量 |
| 中文传输到上位机乱码 | UTF-8与GBK编码不匹配 | 协议固定编码并在两端统一 |
| 日期月份“5”显示成“5”而非“05” | 整数转字符串未补前导0 | 用CONCAT或REPLACE补零 |
| 程序运行后工单号不刷新 | 拼接逻辑被跳周期执行 | 检查生成使能条件的扫描周期行为 |
| 触摸屏显示工单号被截断 | HMI变量长度小于PLC长度 | 两边长度定义保持一致 |
这张表不能覆盖所有问题,但能覆盖我遇到过的90%场景。真正重要的是建立“字符串问题往往不会报错”的意识,对所有工单类字符串数据,都要主动核对长度和内容,而不是依赖监控窗口“看着差不多就行”。另外,在程序里针对工单号单独做“长度校验+内容校验”其实成本很低,就是几条比较指令的事,但能极大减少现场数据追溯的隐患。
4.4.1 一个另类的CPU扫描周期问题
严格来说这不算是字符串函数本身的问题,但它会表现得像字符串问题。我的一个老项目中,工单拼接逻辑放在了一个由M100触发的ST程序块里,M100是上升沿脉冲。结果生产节拍快的时候,M100的脉冲宽度太窄,ST块还没执行完整个拼接流程,脉冲就消失了,导致工单号只拼了一半就把结果发出去。表面看是“字符串拼接不完整”,实际是触发方式有问题。后来我改成“置位标志位开始拼接,拼接完成复位标志位”,彻底解决了。这个经验说明工单拼接这类多步逻辑,一定要用一个内部状态位来保证“从开始到结束的完整性”,而不是依赖外部短脉冲。字符串本身不会跑飞,但执行时机不对,它就会给你一个“看起来坏掉了”的半成品。
5. 扩展:让工单拼接更健壮的高级用法
5.1 其他字符串函数配合使用
CONCAT和REPLACE不是万能的,工单拼接里还经常用到另外几个函数。MID函数可以从字符串中截取子串,比如要从完整工单“F01-2235-240915-A-007”里单独取出订单号“2235”,用MID(WorkNo, 5, 4)就能拿到。INSTR函数可以查找某个子串在源字符串中的起始位置,比如想知道“-A-”在工单里的位置,用INSTR(WorkNo, '-A-')返回7。这两个函数配合REPLACE,可以实现“动态定位后替换”——先INSTR找到位置,再根据这个位置做REPLACE。这在工单格式不固定、但关键分隔符固定的场景里特别有用。比如客户的订单号长度会变化,你不能写死从第5个字符取订单号,但你可以先INSTR找到第一个“-”的位置,然后用MID从第一个“-”后面开始取。这种组合用法熟练之后,工单解析和格式转换就变得越来越通用。还有LEN函数,它不仅用来检查长度,还可以用来做“字符串末尾去空格”的判断——三菱ST没有内置TRIM函数,但可以通过LEN配合MID从尾部循环判断字符是否为空格来实现。
5.2 用结构化数组管理多工单
当产线同时跑好几个订单时,单个字符串变量就不够用了。我的做法是声明一个字符串数组:WorkNoArr : ARRAY[0..9] OF STRING(32);。每完成一个工件,就用上面的拼接逻辑生成工单号,按序号放入数组对应位置。这里有个重要的细节:三菱ST的数组元素可以是STRING类型,但数组下标必须用INT类型变量,不能用WORD。而且数组在初始化时会一次性把所有元素清空,这个初始化动作本身会占用扫描周期时间,在数据量大的时候要注意。用数组管理工单后,上位机可以通过批量读取方式一次拿走10个工单号,通讯效率比逐个读取高很多。但是要特别注意数组元素的数量不要超过CPU的字符串内存上限,FX5U的STRING总容量在参数里可以配置,默认值对于一般工单场景够用,但如果你定义了几百个STRING(128)的数组,内存占用会非常可观,PLC可能直接启动不了。我在项目里就因为数组开得太大,遇到过程序下载后CPU的ERROR灯常亮的情况,最后查出来是STRING区域配置不够,增大设置后才恢复正常。
5.3 工单拼接程序的模板化封装
当你在几个项目里都写了类似的工单拼接逻辑后,最好的做法是把它封装成一个标准功能块。三菱ST里可以用FUNCTION或FUNCTION_BLOCK来封装。我建议用FUNCTION_BLOCK,因为工单拼接经常需要保存中间状态,而且需要多个输出。封装思路是把“字段输入”作为FB的输入变量,“工单输出”和“完成标志”作为输出变量,“中间步骤标志位”作为内部变量。这样之后写新项目的工单逻辑,直接拖一个FB实例,填上订单号、日期、班次、序号,输出就是完整的工单号,不用每个项目都从头写一遍。封装的时候建议把“格式规则”也作为FB的参数,比如工厂代码、分隔符、日期格式,都提成输入参数,这样不同产线的差异就能靠参数配置来兼容,而不是靠改代码。这种模块化的做法,长期看能省下很多调试时间。我自己在这个FB里还会加一个“测试模式”输入,置ON时固定输出一个预置的测试工单号,方便上位机联调,这个设计在现场调试阶段帮了大忙。
6. 个人实操体会与小技巧
工单拼接看起来是小事,但它直接影响生产数据的完整性和可追溯性,出问题的时候往往是最着急的。我在几个项目里练出来的体会是:字符串操作要当作“有状态的数据流”来管理,而不是“一次性的计算”。什么意思呢?就是每个周期你都要清楚地知道,你的字符串变量当前处于什么状态,是已经拼好了还是没拼、是干净的还是带残留。这比学会某个函数的语法重要得多。每次写字符串拼接程序,我都会在心里过一遍这几个问题:这个变量的长度余量够不够?中间变量清空了吗?生成完成标志位跟数据更新是同步的吗?如果编码格式涉及中文,通讯协议里说清楚了吗?
最后再分享一个很实用的小技巧:在GX Works3的监视模式下,字符串变量的显示往往只显示前几个字符,调试的时候很容易造成“程序只有这么长”的错觉。想看完整内容,把监视窗口拉宽,或者直接把LEN(变量)也加到监视列表里,两个放在相邻位置一起看。如果LEN等于你期望的值,内容大概率是对的;如果LEN不对,再去找拼接逻辑里的问题。这招虽然简单,但真的能省掉很多无头苍蝇一样的排查时间。工单拼接这件事,说难不难,说简单不简单。把CONCAT和REPLACE用熟,把长度和边界条件管好,你会发现三菱ST的字符串处理完全可以做到既灵活又可靠。