
039、类型转换与CLEAR/REFRESH昨晚值班被一个电话叫醒生产系统有个报表突然出了乱码查了半天发现是某个Z字段从数据库读出来是CHAR10程序里直接往一个INT4变量上赋值结果ABAP悄悄做了截断和补零最后拼到消息文本里成了“订单号0000001234 ”客户看到那串空格直接炸了。这种问题不报错运行得好好的就是数据不对。所以今天想聊聊类型转换以及那个被无数人随手一写却暗藏杀机的CLEAR/REFRESH。先看一个典型的坑。你在ABAP里写DATA: lv_num TYPE i. lv_num 123abc. WRITE lv_num.猜猜输出什么不是报错是123。ABAP在把字符串往整数上放的时候从左到右解析遇到非数字就停剩下的不管了。更诡异的是如果字符串是“abc123”那结果直接是0。这种隐式转换在ABAP里非常“宽容”宽容到你根本不知道它替你做了多少决定。老司机写代码要么用MOVE显式声明要么用CONV或者类型化方法绝不指望隐式转换能帮你兜底。再说说CLEAR和REFRESH。新手最爱写CLEAR: itab.然后在同一段逻辑里CLEAR: wa.——看起来没问题但如果你CLEAR的是一个带表头的内表老语法里那玩意儿现在不建议用了或者你CLEAR的是一个结构体里嵌着内表的结构那爽了内表内容也跟着没了。那次我调试一个ALV刷新的bug刷新前明明把内表数据存到了本地备份结果备份变量是一个结构体里面有个field是内表CLEAR Backup直接把那个内表也清空了等于备份了个寂寞。REFERSH和CLEAR的区别一句话REFRESH只清内表行不动其他属性CLEAR是万能清空针对于内表效果和REFRESH一样但作用于结构体或单独变量时会把所有组件都初始化为初始值。问题是ABAP里“初始值”这个概念本身就容易绕晕——CLEAR一个STRING它变成空字符串CLEAR一个INT4它变成0CLEAR一个REF TO对象它变成空引用。但如果是一个TYPE TABLE OF的内表CLEAR和REFRESH都只删行不释放内存。你想彻底释放内存得用FREE。FREE之后这内表就真的啥都没有了连表头都没了。有一次我优化一个后台作业内存飙到几百MB查了半天就是循环里CLEAR内表但内表一直往里加记录行数清了但存储空间没还给内存池每次循环追加记录后内存重新分配碎片越积越多。改成REFRESH一样不解决必须用FREE或者让内表出了作用域。再讲一个类型转换和CLEAR结合的诡异场景。假设有个选择屏幕参数s_carrid TYPE s_carr-carrid默认是CHAR3你直接在WHERE语句里把它和数据库字段比较没问题。但如果某天你把s_carrid定义成STRING然后WHERE里写了carrid s_carridABAP会先把数据库字段转成STRING再比较还是把STRING转成CHAR3答案是数据库层可能直接把STRING传给底层但不同数据库驱动处理起来不一样有时候明明LH能匹配上LLH却匹配不上就因为尾随空格。你在CLEAR后忘了去掉字符串尾巴然后拼接一个LEFT JOIN条件那个多余的空格让你整个查询结果少了一行。处理办法CONDENSE之前先SHIFT或者直接用CONV s_carr-carrid(s_carrid)强制转换别让ABAP自己发挥。我个人的经验是ABAP里的类型转换像一把没锁的枪不响的时候你好我好响了就是生产事故。好习惯是所有从外部接口、数据库表、选择屏幕拿来的值进业务计算之前先显式转一遍。比如写个辅助方法METHODS convert_to_char10 IMPORTING iv_input TYPE any RETURNING VALUE(rv_output) TYPE char10.里面用CONDENSE、SHIFT、REPLACE把脏数据洗干净再往外给。宁可在转换方法里多花几行也不要在后面查数据错乱。CLEAR和REFRESH那边我给个土办法内表操作只用REFRESH结构体清空用CLEAR释放内存用FREE。如果有人问为什么要区分你就说CLEAR是动词REFRESH是动词FREE是重动词。但真正心里要清楚CLEAR一个内表不会释放内存REFRESH也一样。如果你在循环里反复使用同一个内表并不断追加请在每个循环末尾用FREE或者把内表定义在循环体内部。别怕性能损失ABAP对象创建比你想象中便宜内存泄漏可比性能昂贵多了。还有一个坑是CLEAR带表头行的工作区。老语法里TABLES: sflight.然后CLEAR sflight.这个行为是在清工作区但如果你想清的是内表行得CLEAR sflight[]。少写一对中括号结果就是工作区清空了内表行还在然后你用READ TABLE sflight INDEX 1读出来的是旧数据debug半小时找不到原因。现在的面向对象写法不推荐带表头了但存量代码里到处是这种“幽灵”。你接手老程序时看到CLEAR xxx先确认xxx是结构体还是内表如果是内表但没带[]基本可以断定原开发人员是从VB转过来的。最后说一个真实调试案例。前端传过来一个订单号参数JSON解析后放到lv_input里类型是STRING。后端用这个值去查表条件是vbeln lv_input可是怎么也查不到。我打断点看值屏幕显示“0000001234”看起来没问题。然后我查STRLEN(lv_input)竟然是10不是8。字符串末尾有两个看不见的空格。前端传的是“0000001234 ”带空格JSON解析保留了空格。我那时候犯懒想直接CLEAR一下但CLEAR不会去掉空格啊它只会把整个变量清空。后来我用CONDENSE lv_input NO-GAPS去掉所有空格再转成CHAR10查询就正常了。打那以后我定了一条规矩凡是来自外部的字符串要参与字段筛选或比较的必须经过CONDENSESHIFTCONV三步曲少一步就是给自己埋雷。你说ABAP为什么不自动去掉尾随空格因为它固定长度字符类型CHAR就是带空格的这是ABAP的底层DNA。别人家语言里“abc”就是三个字符ABAP里CHAR10的“abc”是“abc ”后面七个空格。你习惯了就无所谓就怕从Python转来的新人一上来就被这个特性玩死。我见过有人为了比较两个字符串写了个循环把每个字符挨个儿比效率惨不忍睹。顺便提一嘴CLEAR和REFRESH在性能上没有本质区别都差不多一微秒的事别纠结。真正有区别的是你对数据生命周期的理解。如果你在FORM里接收一个内表你想把它“清空”后重新填充用REFRESH是标准的。但你如果只是想把内存里一个大内表干掉用FREE。还有CLEAR一个LOOP AT itab里的wa并不会影响itab本身itab的当前行在LOOP内部是不能被CLEAR的那是语法错误编译直接报错。有人想通过CLEAR wa来跳过处理那没用还不如CHECK。写到这里我看了看时间凌晨两点半服务器日志还在那儿刷。干ABAP这行很多时候不是技术多高端而是细节够不够细。类型转换和CLEAR/REFRESH一个管数据值长什么样一个管数据活多久都是最基础的东西但事故率居高不下。你在CSDN上搜“ABAP类型转换EXCEPTION”能搜出好几页帖子什么“CX_SY_CONVERSION_ERROR”“CX_SY_MOVE_CAST_ERROR”全是没做防御性转换的。我建议你在程序开头写个TRY...CATCH把转换错误全接住至少能让错误信息看起来像人话而不是一堆十六进制地址。个人最终建议别信隐式转换显式写CONV。别乱用CLEAR先问一句这个变量要不要彻底归零。内表行清空用REFRESH内存释放用FREE。如果有老代码带表头加[]总没错。还有任何从RFC、HTTP、数据库出来的字符进业务逻辑前先SHIFT和CONDENSE。这些习惯养成了你半夜被叫醒的概率能低一大半。