
1. 先搞清楚三菱ST字符串的底层脾气干过三菱PLC项目的朋友应该都有同感在梯形图里处理字符串就像拿筷子夹汤圆怎么都不顺手。后来ST语言普及了字符串函数多了起来情况好转不少但依然有一堆看着能用、一跑就翻车的细节。这篇文章我围绕三菱ST语言里的CONCAT和REPLACE展开拿工单拼接这个最常见的场景做实操例子把从指令机制到项目接线再到调试排错的完整链路都过一次最后给出我自己常用的替代方案如果你手上正好有FX5U或者Q系列的项目应该能直接抄作业。先得接受一个现实三菱ST的字符串本质上是一个固定长度的字符数组不是C语言里那种按需扩容的char数组。你在声明变量时就得先定好尺寸比如VAR sOrderNo : STRING(32); sBatchNo : STRING(16); sFullInfo : STRING(64); END_VAR字符串变量一旦定义了长度后面所有操作都必须在这个框框里玩。长度不够要么被截断要么报错。而且三菱ST字符串自带终止符类似C语言的\0你从触摸屏、扫码枪、MES系统读进来的数据后面往往跟着看不见的空格或结束符直接拼接就是坑。1.1 字符串长度、空格和ASCII编码的关系三菱ST字符串从本质上看是ASCII字节串。字母、数字、标点符号一个字符占一个字节中文一个字符占两个字节LEN函数返回的到底是字符数还是字节数不同系列、不同GX Works版本的处理不太一样。我的经验是按字节数来理解因为底层是UINT数组和ASCII码的映射字符编码再复杂最后PLC内部走的都是字节处理。也因为这样工单拼接里有个高频问题你从MES拿到的数据可能带前后空格。比如工单号实际是WO20250801但上位机给你传过来的是WO20250801 尾部多了个空格。直接用CONCAT拼到路径里生成的文件名看起来没问题但上位机反读时解析就会出怪事——文件名末尾有个不可见字符FTP上传、数据库查询都会对不上。处理办法有两种。一是用三菱自带的函数做规整类似TRIM功能但很多低端系列没提供现成的得自己写二是拼接前显式检查LEN再用RIGHT、LEFT、MID裁剪掉异常字符。没有通用函数不可怕可怕的是你根本不知道字符串里有空格这回事。1.2 为什么有人绕道用数组存字符我在一些老工程师的项目里见过这样的做法不用STRING类型改用ARRAY[0..31] OF BYTE或ARRAY[0..31] OF CHAR存工单信息然后自己拼ASCII码。表面看是自讨苦吃实际理解了就明白早期三菱FX3U的ST支持不完善字符串函数不是缺这个就是缺那个而且函数处理起来有长度限制不如自己用数组灵活。数组方式的好处是拼接时可以控制精确字节位置上位机通信也直观每个字节都看得见摸得着。代价是代码量翻倍可读性差后来FX5U和Q系列ST函数补全了新项目我基本不再推荐这种土办法。但是有一个场景我至今还在用数组需要拼接工单号 日期时间 随机校验码这种固定格式报文时数组方式可以直接一个字节一个字节填进去调试时用监视窗口看到的就是纯ASCII值不会出现STRING变量那种你看到的是空格还是终止符的暧昧问题。除此之外还是乖乖用CONCAT / REPLACE省心。2. CONCAT和REPLACE到底谁适合做工单拼接工单拼接看起来就是把几段字符串串起来但真正在项目里拼接不是只有串起来这一种动作。有时候你要在固定模板里替换一段参数有时候你要把多个字段并成一个报文这两个需求恰好对应CONCAT和REPLACE两个函数。2.1 指令层面的机制对比先看官方指令的典型用法。CONCAT的作用是把多个字符串按顺序连接成一个新字符串三菱ST里一般支持多个参数我实际用下来建议最多只传三个参数太多可读性太差出问题也不好排查。示例sFullInfo : CONCAT(sOrderNo, sBatchNo); // 拼接后WO20250801BATCH07注意一个关键机制CONCAT是把左操作数和右操作数的有效字符部分连接起来但左右操作数自己的长度不会主动压缩。比如sOrderNo实际占32字节只有前12字节有数据后面的全是空格那CONCAT(sOrderNo, sBatchNo)的结果是WO20250801后面跟着一堆空格再接BATCH07。为了避免这种情况我给每条规则是所有参加CONCAT的字符串变量在使用前必须做一次有效长度截断或者保证源字符串写入时没有多余空格。REPLACE的作用是在指定字符串的指定位置替换掉指定长度的字符。这个函数在处理模板填充场景时非常好用。比如你收到上位机的指令格式是ORDER:00000000|QTY:0000要把中间的具体订单号和数量填进去用REPLACE就是精确打击sMsg : ORDER:00000000|QTY:0000; // 从第7个字符开始替换8个字符为实际工单号 sMsg : REPLACE(sMsg, 7, 8, sOrderNo); // 从第18个字符开始替换4个字符为实际数量 sMsg : REPLACE(sMsg, 18, 4, sQty);这里REPLACE四个参数分别是源字符串、开始位置、替换长度、替换内容。有些工程师会把源字符串和替换内容搞反或者在替换长度上填错结果整个报文结构直接被干翻。2.2 用C语言视角理解三菱ST函数如果你写过C语言理解这两个函数特别简单。CONCAT约等于C里的strcat但三菱ST是函数式写法返回一个新字符串REPLACE则有点像sprintf加指针偏移的组合体只是它更机械——不在内容里找匹配而是直接按位置和长度干。C语言里有strstr这样的函数可以在字符串里搜索某个子串的位置但三菱ST里没有现成的全局替换函数。这是很多从C转过来的人最不适应的一点。在工单拼接场景里你可能希望只要字符串里出现日期前缀就自动替换成新日期但ST的REPLACE需要你先用INSTR找到日期前缀的位置再拿那个位置做替换参数。看起来多了一步实际也不难关键是脑子里要清楚REPLACE不负责找位置只负责按坐标操作找位置的活儿归INSTR管。所以我的判断是CONCAT适合字段拼接REPLACE适合模板填充。工单拼接如果只是把几个数据并到一起用CONCAT如果是从MES下发的模板报文里填参数用REPLACE。这两个不是二选一的关系更多时候是组合拳。2.3 耗时和工程习惯上的差异还有一个很现实的问题字符串拼接太频繁会影响PLC扫描周期吗以我的实测经验单个CONCAT或REPLACE指令执行时间都在微秒到几十微秒级别对于通常的扫描周期5ms~10ms来说偶尔拼几次完全无感。但你要是设了一个每秒执行N次的功能块里面反复做长字符串拼接CPU占用会明显上去。曾经有一次我在FX5U里做每秒50次的报文生成每个报文拼60个字符地址连续传输结果扫描周期从4ms飙到了7ms查了半天才发现是频繁的字符串拷贝占用了时间。从此我养成一个习惯高频执行段里静态字符串用常量数组预置动态内容只在数据变化时才重新拼接。这个思路不只是省CPU还能让程序逻辑更清晰——什么时候拼、拼给谁用一眼就能看出来。3. 工单拼接实战从报文到文件名的三个真实场景光说指令机制不够我拿三个项目里踩过的真实场景展开每个都附可复现的代码骨架。3.1 场景一MES下发工单号批次信息拼接设备上有一个扫码枪读取托盘条码MES会下发当前工单号、批次号、数量设备需要把这些信息拼成一条报文上传给上位机回车换行结尾。// 注意1、2是回车换行的ASCII码 sTmp : CONCAT(sOrderNo, ,); sTmp : CONCAT(sTmp, sBatchNo); sTmp : CONCAT(sTmp, ,); sTmp : CONCAT(sTmp, INT_TO_STRING(iQty)); sSend : CONCAT(sTmp, $R$L);这段代码在功能上没错但它犯了一个我在前面说的经典错误sOrderNo和sBatchNo如果带尾部空格拼出来的报文字段间就会出现莫名空格。正确做法是先规整数据// 规整函数去除字符串尾部空格 // 方法从右往左找第一个非空格字符用LEFT截取这个规整函数后面第5章我给出完整实现。实际抓包调试时你会发现上位机根本不关心你逻辑多复杂它就要求报文的每一个字节都和协议完全一致多一个空格都是校验失败。工单拼接不是给人看的是给机器看的严格是按字节对齐来考核。3.2 场景二模板字符串用REPLACE填参数第二个项目里上位机通过以太网给FX5U下发一套模板报文内容是ORDER,{ORD},DATE,{DAT},QTY,{QTY}PLC要做的不是拼接整条报文而是把大括号里的占位符替换成实际值。这种场景用REPLACE做模板填充就非常合适比CONCAT拼接更灵活因为模板本身是上位机可配置的。比如PLC收到模板后先检查长度确认在STRING变量范围内然后逐个用INSTR找占位符的位置iPos : INSTR(sTpl, {ORD}); IF iPos 0 THEN sTpl : REPLACE(sTpl, iPos, 5, sOrderNo); END_IF;注意{ORD}在字符串里占5个字符替换成实际的工单号后整个字符串长度可能变长也可能变短。REPLACE是按位置替换指定长度替换后如果新字符串比旧内容短占位符多出来的部分会被塞成空格如果长可能会顶掉后面的内容。所以在模板填充前确认每个字段长度上限宁可配一个64位的模板字符串也不要把大小卡得刚刚好。这个项目我后来改用第5章的手写模板替换函数来解决变长问题总体稳定很多。3.3 场景三触摸屏路径/文件名拼接触摸屏上要显示一个工单报告页面文件路径是/usr/order/20250801/WO20250801_A.csv这种格式。这部分逻辑放在PLC里做用CONCAT拼接。拼接逻辑不难难点在于触摸屏和PLC之间的字符串变量映射。很多国产触摸屏与三菱通信时STRING变量传输会带上长度前缀或终止符导致PLC里拼得好好的字符串到触摸屏上末尾多了个空格或有乱码。我的应对方式是PLC侧拼完文件名后顺手把字符串用BYTE数组方式填充一遍确保最后一个有效字节后面是终止符再映射给触摸屏。如果你用的是三菱GOT情况好一些GOT和三菱自家PLC的字符串类型对接相对规范。如果是第三方屏就得做好屏幕显示和PLC监视不一致的思想准备。4. 拼接时的典型翻车现场与排查链路如果说前面是理论课这一节就是实战课。字符串相关bug隐蔽性高报错信息又不直观排查起来最花时间。我总结两个高频翻车现场和一套完整排查链路。4.1 翻车现场一拼接结果带空格导致上位机解析失败现象就是上位机报报文格式错误看日志字符串显示正常但用Wireshark抓包发现某几个字节是0x20空格ASCII码。排查链路先确认是不是CONCAT引入的空格。打开GX Works软件监视窗口把拼接前的源字符串拉出来逐个字节看。注意监视窗口显示空格和字符不直观可以把字符串强制转换为字节数组或者用十六进制显示模式。确认源字符串本身是否干净。如果你是从触摸屏组态或MES通信存储区读出来的数据极大概率带了填充空格。很多通信协议里字段是固定长度8字节内容只占6字节后两位自动补空格。在拼接前后分别做LEN测试。比较LEN的结果和预期有效字符数是否一致。如果LEN结果比预期大说明有空格或不可见字符混入。修复方式对参与拼接的每个字符串进入前统一执行一遍尾部空格清理。这个坑我踩过不只一次后来在团队里立了一条硬规矩所有来自外部通信的字符串在存入全局变量前必须过一遍规整函数。宁可损失一点扫描周期也不能让脏数据流到业务逻辑里。4.2 翻车现场二字符串长度不够直接截断第二种翻车是字符串变量声明太短拼接时数据被截断。比如工单号实际是18位你声明STRING(16)CONCAT拼接时数据会超出16字节的一部分被悄悄丢掉而且通常不报错。等你发现工单号对不上的时候可能已经过去半天。这个坑的隐蔽性在于PLC正常情况下不会崩溃程序照跑只有数据校验时才暴露。更坑的是REPLACE往指定位置填内容时如果替换内容比声明的字符串空间长也可能出现字符串无变化的假象因为被截断后看起来像没替换成功。排查方式很明确凡是出现字符串变化不符合预期的现场第一件事先看变量定义的大小再去看函数参数。我一般会在程序里加一段上电自检逻辑把每条字符串变量的最大长度、当前LEN、关键拼接结果上传到上位机日志这样可以做到事后复盘。4.3 排查链路DUT、Watch窗口、串口调试的三层递进提到排查我建议按三层递进去做而不是一上来就猜函数用法。第一层是DUTDevice Unit Test层说白了就是写个独立测试功能块只把输入和输出打印出来验证字符串函数本身的拼接逻辑。很多问题在这一层就能暴露。比如你怀疑REPLACE位置参数不对就单独写一段sT : REPLACE(HELLO WORLD, 7, 5, PLC);在Watch窗口看结果不用把整个设备拉起来就能确认。第二层是Watch窗口实时监视。GX Works的监视窗口可以看STRING变量的当前值但注意它显示的字符对你判断空格不友好。可以把监视格式切到ASCII或十六进制字节视图能看到每个字节的码值。第三层是用串口或以太网调试工具把PLC实际发出的原始报文截下来和协议文档逐字节比对。很多通信问题最后都是在这一层破案的。比如上位机说你发了两个逗号你根本不用在逻辑里翻直接把报文拉出来数一下就知道是哪个字段多拼了东西。5. 没有常用函数时的手写替代方案你可能会遇到一个尴尬的情况用的PLC系列比较老或者项目组要求在标准库基础上减少函数调用这时候CONCAT和REPLACE不一定可随便用。这些年我攒了两个比较稳定的手写方案放在这里供参考。5.1 手写字符串拼接用FOR循环和BYTE数组实现思路就是定义一个字节数组用FOR循环把源字符串里的有效字符依次搬进目标数组。三菱ST里BYTE数组配合ASCII相关函数可以完成这个操作。简单骨架// 功能把src追加到dst尾部dst是STRING变量 // 注意src里的空格会被跳过按有效字符处理实际写成ST代码时我会先用LEN拿到两个字符串的字节数再用MID按单字节取出字符拼到目标字符串后面。性能方面10个字节以内的拼接基本无感超过50个字节循环取字符会稍微慢一些但工单信息这种体量完全够用。优点是你对每个字节都有绝对控制权能剔除空格、终止符、回车的干扰。5.2 手写模板替换解决REPLACE遇到变长内容的尴尬REPLACE的问题是它按固定长度替换内容变长变短都会导致模板错位。工单模板填充的时候工单号可能8位也可能18位直接REPLACE很容易把后面的字符顶丢。所以我写了一个按起始标记、结束标记截断替换的函数。原理是用INSTR找到起始标记位置用INSTR找到结束标记位置计算出要替换的旧内容长度用LEFT取标记前内容、RIGHT取标记后内容再CONCAT成新字符串。这个方案相当于手写了一个支持变长的模板替换。同样是填充{ORD}它可以保持模板其它部分原封不动比原生REPLACE更稳。代价是代码量上去了一旦遇到多个占位符就得写循环或者重复多段处理。我的建议是占位符少于三个时手写没问题多了还是建议用原生REPLACE配合长度约束让上位机下发固定长度的工单号字符串。6. 选型建议CONCAT、REPLACE、手写方案各自的上场时机很多朋友喜欢把问题简单化总想问哪个函数好用。但字符串处理这件事没有绝对的好用只有匹配不匹配。我列一个自己在项目里用来做选型的对照表也方便后面复制到内部文档里。场景首选方案备选方案原因几段字段拼成一条报文工单批次数量CONCAT手写数组拼接CONCAT写法简单可读性好手写方案能严格控字节模板报文填充固定占位符REPLACE INSTR手写模板替换REPLACE高效配合INSTR定位即可模板报文填充变长字段手写模板替换REPLACE 长度规整变长内容容易破坏模板结构通信数据清洗去空格自己写Trim无原生可直接调用三菱没标配Trim至少我接触的系列里没有高频扫描段内拼接尽量少用字符串函数静态常量预置防止扫描周期恶化选型判断标准有三条数据来源是否可控、字符串长度是否固定、寄存到通信收发是否与协议严格对齐。数据来源不可控优先做清洗再拼接长度不固定优先模板替换和手写方案对接上位机通信优先字节级方案。另外提一个容易被忽略的点ST语言里字符串变量可以赋字符串常量比如sVersion : VER1.0;但常量赋值同样受限于变量长度。工单号这类业务数据我建议在触摸屏或者MES侧就统一成固定位数不足位补零或者补空格到PLC里只是当一个定长字段处理所有拼接逻辑都按定长来做工程会简单很多。千万不要在PLC里既要处理定长又要处理变长那是最容易埋雷的。7. 一个不起眼但能救命的习惯上电自检和字符串诊断字符串函数的坑很多时候是运行时才暴露不是编译时能拦住的。所以我在项目里给字符串处理单独建了一个诊断页把关键拼接结果、源字符串长度、目标字符串长度全部传给触摸屏。设备调试阶段打开诊断页跑一遍完整流程所有字符串相关交接点一目了然。上电自检我建议做三件事。第一把每条外部通信输入的重要字符串变量打印LEN和协议预期值做对比第二把工单拼接后的首字节、末字节打印出来看有没有异常第三把拼接结果做一次往返校验——用PLC自己解析一遍拼出来的报文看能不能还原回各字段类似编码解码自检。这三件事成本极低但能避免大量设备跑三天后突发通讯故障的诡异问题。我在好几个项目里就是因为这个习惯把原本可能要在现场熬两天的排查工作缩短到了半小时内定位到是MES端多传了一个换行符导致的。最后再分享一个小技巧调试字符串相关逻辑时别只用监视器看试着把字符串输出到触摸屏报警信息里或者直接在PLC里用串口调试指令发到PC端串口工具。眼睛看到实际字节流之后很多我认为是这样的错误预判都会被纠正。经过几次洗礼你自然就会总结出自己的一套字符串处理规矩。