你可能也会掉进这个简单的 String 的坑


作者 | 程序猿石头
责编 | 晋兆雨
头图 | 付费下载于视觉中国

关于作者:程序猿石头(ID: tangleithu),现任阿里巴巴技术专家,清华学渣,前大疆后端 Leader。


背景

作者的同学是某大公司高级开发工程师,某日收到不少错误告警信息,于是便去开始排查。

跟踪日志发现是某个服务抛出的异常信息,奇怪的是这个服务上线也有一段时间了。之前很少看到类似的错误信息,最近偶尔多了起来。

后来才定位到是因为服务调用了某外部接口,发现对方对参数长度做了限制,如果输入参数超过 1000 bytes,就直接抛异常,代码类似如下:

/*** @param status* @param result, the size should less than 1000 bytes* @throws Exception*/
public XXResult(boolean status, String result) {if (result != null && result.getBytes().length > 1000) {throw new RuntimeException("result size more than 1000 bytes!");}......
}

心想,这还不简单,咱们的 result 也不是什么关键性的东西,你有限制,我直接 trim 一下不就行了?


解决方案

于是三下五除二,给搞了个 trim 方法,支持传不同参数按需 trim,代码如下:

/*** 将给定的字符串 trim 到指定大小* @param input* @param trimTo 需要 trim 的字节长度* @return trim 后的 String*/
public static String trimAsByte(String input, int trimTo) {if (Objects.isNull(input)) {return null;}byte[] bytes = input.getBytes();if (bytes.length > trimTo) {byte [] subArray = Arrays.copyOfRange(bytes, 0, trimTo);return new String(subArray);}return input;
}

再在需要调用外部服务的地方,先调用这个 trimAsByte 方法,一顿操作连忙上线,一切完美~

灾难现场

一切完美,作者也是这样认为的。然后幸福总是短暂的。

经过一段时间后(前面也提到,业务场景确实是偶发的),相同的错误仍然发生了。

简直不敢相信,都 trim 了为啥还会超出?你也帮忙想想,是哪里的问题?

看看上面的例子(为了方便展示,简单修改文首代码了下),

trimAsByte("WeChat:tangleithu", 8)

输入字符串 WeChat:tangleithu 太长了,只 trim 到剩下 8 个字节,对应的字节数组是从 [87,101,67,104,97,116,58,116,97,110,103,108,101,105,116,104,117] 变为了 [87,101,67,104,97,116,58,116],字符串变成了 WeChat:t ,结果正确。

其实在写这个方法的时候还是太草率了,本应该很容易想到中文的情况的,我们来试试:

trimAsByte("程序猿石头", 8)

看上述截图,悲剧了,输入程序猿石头,3 个字节一个汉字,一共 15 个字节 [-25,-88,-117,-27,-70,-113,-25,-116,-65,-25,-97,-77,-27,-92,-76],trim 到 8 位,剩下前 8 位 [-25,-88,-117,-27,-70,-113,-25,-116] 也正确。再 new String,又变成3 个 “中文” 了,虽然第 3 个“中文”,咱也不认识,咱也不敢问到底读啥,总之再转换成字节数组,长度多了 1 个,变成 9 了。

问题算是定位到了。


不禁要问,为什么?

来看看这个 String 的构造函数,看看上面注释才发现,其实我们忽略了一个很重要的概念,就是编码方式。

/*** Constructs a new {@code String} by decoding the specified array of bytes* using the platform's default charset.  The length of the new {@code* String} is a function of the charset, and hence may not be equal to the* length of the byte array.** <p> The behavior of this constructor when the given bytes are not valid* in the default charset is unspecified.  The {@link* java.nio.charset.CharsetDecoder} class should be used when more control* over the decoding process is required.** @param  bytes*         The bytes to be decoded into characters** @since  JDK1.1*/
public String(byte bytes[]) {//this(bytes, 0, bytes.length);checkBounds(bytes, offset, length);this.value = StringCoding.decode(bytes, offset, length);
}

当我们用默认的构造函数 new String 的时候,只是用了系统默认的编码(本文是“UTF-8”)去尝试解码,构造出字符串。

所以,当我们在用字节数组(字节流)来表达具体的语义的时候,一定要约定好以什么方式进行编码,本文不具体阐述编码问题了。下面用一个例子来解释上文的现象:

[-25,-88,-117,-27,-70,-113,-25,-116,-65,-25,-97,-77,-27,-92,-76] 仍然用这串字节数组来实验,这串字节数组,如果用 “UTF-8” 编码去解释,那么其想表达的语义就是中文“程序猿石头”,从上文标注的 1,2,3 中可以看出来,没有写即用了系统中的默认编码“UTF-8”。

假设按照 “GBK” 来解释(标注 4),就是表达的 “绋嬪簭鐚跨煶澶�”,注意看下其中的 � 是不是似曾相识;

注意标注 5,通过 GBK 解释构造字符串后,再通过默认的 “UTF-8” 获取字节数组,长度就变成 24 了,然后还通过 “GBK” 编码得到的字节数组长度为 15(标注 6),再试图构造字符串(标注 7),其中“程序猿石头”的“头”字,已经没了。说明这个转换过程中,其实信息已经被丢了。

上面的 � 其实是 UNICODE 编码方式中的一个特殊的字符,也就是 0xFFFD(65535),其实是一个占位符(REPLACEMENT CHARACTER),用来表达未知的、没办法表达的东东。上文中在进行编码转换过程中,出现了这个玩意,其实也就是没办法准确表达含义,会被替换成这个东西,因此信息也就丢失了。你可以试试前面的例子,比如把前 8 个字节中的最后一两个字节随便改改,都是一样的。

程序猿石头:65533 示例


总结

总结一下,其实本来是一个很简单的问题,却经过几次修改才最终解决,说明对 “基础” 掌握得还是不够,一个重要的点是,在处理二进制数据的时候,一定要联想到 “编码” 方式。

另外,提醒我们,看似简单的问题,我们往往容易忽略。比如如果单纯看到文中提到的这个trim 方法,其实很容易写个单元测试就能尽早发现有问题;

更多阅读推荐

  • 大神们都是如何在时间序列中进行特征提取的?看完就懂了!

  • 亿级大表分库分表实战总结(万字干货,实战复盘)

  • 赠书 | 华为数据底座的整体架构与建设策略

  • 区块链和大数据一起能否开启数据完整性的新纪元?

  •  情感 AI,再不涉猎就要晚了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/516012.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

FileZilla客户端连接腾讯云FTP服务器时出现“227 Entering Passive Mode”

FTP的主动模式(PORT Mode)及被动模式(Passive Mode) FTP的特殊性&#xff1a; 大多数的TCP服务是使用单个的连接&#xff0c;一般是客户向服务器的一个周知端口发起连接&#xff0c;然后使用这个连接进行通讯。但是&#xff0c;FTP协议却有所不同&#xff0c;它使用双向的多个连…

Excel VBA 巧用自定义函数进行数组去重

目录 一. Dictionary方法删除重复项二. Collection 方法删除重复项三. 使用这两个函数1. RemoveDupesDict 函数调用示例:2. RemoveDupesColl函数调用示例:本贴将展示两种从数组中删除重复项的方法。 第一种方法使用字典,第二种方法使用集合。每种方法都有优点和缺点,但都能…

分享实录 | 企业CICD规模化落地浅析

【以下为分享实录&#xff0c;有删节】 今天分享的题目是《企业CICD规模化落地》&#xff0c;因此我们不会侧重讲解CICD是什么以及怎样做CICD&#xff0c;而是你已经知道怎样“玩转”CICD了&#xff0c;要如何在一个比较大的企业中规模化地落地。 研发流程与持续交付简析 持…

重磅发布:阿里云云安全中心一键防勒索功能上线!

5月6日&#xff0c;阿里云云安全中心重磅发布一键防勒索功能&#xff0c;致力于帮助客户实现对勒索病毒的一键式纵深防御。 勒索病毒对企业来说是危害极大的安全风险之一&#xff0c;一旦核心数据或文件被加密&#xff0c;除了缴纳赎金&#xff0c;基本上无法解密。两年前爆发…

用 Excel VBA 提取小数部分-自定义函数

利用VBA 提取小数部分 Function GetDec(cell As Range, Optional ConvertToWhole As Boolean) As Variant 示例:2.154a)如果忽略或设置为FALSE,则函数返回小数,类似于“0.154”的形式b)如果设置为TRUE,则函数返回一个整数,如“154”Dim i%If

【数据湖存储】数据湖的终极奥秘,无招胜有招

作为海量数据存储与分析的重要承载方式的数据湖&#xff0c;从2011年概念诞生至今&#xff0c;已经发展了9个年头。而数据湖是什么&#xff1f;又能为数字化经济带来什么&#xff1f;《阿里云数据湖存储解决方案蓝皮书》将为您揭开数据湖的终极奥秘——无招胜有招 关注“阿里云…

科天云会议产品升级,打造企业数字化转型办公协同新基建

2020年11月24日&#xff0c;科天云会议宣布升级科天章鱼云并发布科天aPaas办公协作中台。依托科天aPaas办公协作中台核心技术架构&#xff0c;科天章鱼云10大音视频通讯核心能力&#xff0c;科天云会议将以便捷轻量的SaaSPaaS模式&#xff0c;为大中型企业提供云到端、全场景的…

社区首款 OAM 可视化平台发布!

作者 | 徐运元&#xff0c;杭州谐云科技合伙人及资深架构师&#xff0c;云计算行业和 Kubernetes 生态资深从业者 导读&#xff1a;什么是 OAM&#xff1f;2019 年 10 月 17 日&#xff0c;阿里巴巴合伙人、阿里云智能基础产品事业部总经理蒋江伟&#xff08;花名&#xff1a;小…

linux:根据关键字或日期查找日志

文章目录脚本11. 脚本描述2. 要点3. 格式4. 脚本原型5. shell脚本6. 效果图脚本22.1. 脚本描述2.2. 要点2.3. 格式2.4. 脚本原型2.5. shell脚本2.6. 效果图脚本1 1. 脚本描述 查询指定日志文件中是否包含指定的关键词的日志信息 2. 要点 包含则输出包含的关建行所在日志信息…

Go 语言成为最受欢迎的语言

<关注阿里巴巴云原生公众号&#xff0c;回复 Go 即可下载清晰知识图谱> 对 Go 语言感兴趣但又不知从何学起的同学&#xff0c;可以参考一下 Go 语言系列文章&#xff1a; 为什么你要选择 Go&#xff1f;Go 面向失败编程带着服务器编程金刚经走进 2020 年敢问路在何方&a…

如何在DevSecOps道路上快速、安全地抵达终点

作者 | 吴翔责编 | 晋兆雨出品 | CSDN云计算头图 | 付费下载于视觉中国近年来&#xff0c;移动互联网的迅猛发展给人们带去不少便利&#xff0c;在软件安全领域内&#xff0c;一种名为敏捷开发的模式正悄然流行&#xff0c;而可打破业务隔离、提高效率的DevOps&#xff08;开发…

构建更动态更灵活的分布式计算生态

0. 前言 作为阿里巴巴核心大数据底座&#xff0c;伏羲调度和分布式执行系统&#xff0c;支撑着阿里集团内部以及阿里云上大数据平台绝大部分的大数据计算需求&#xff0c;在其上运行的MaxCompute(ODPS) 以及PAI等多种计算引擎&#xff0c;每天为用户进行海量的数据运算。 在&q…

企业微信H5_身份验证,H5应用网页授权登录获取身份

文章目录一、调用流程1. 企业微信OAuth2接入流程2. 使用OAuth2前须知3. 构造网页授权链接4. 获取访问用户身份二、调试前准备2.1. 配置域名映射2.2. 跨域域名请求2.3. 设置可信任域名2.4. 登录企微2.5. 选择自建应用三、实战演练3.1. 前端编码触发后端api3.2. 后端构造授权链接…

Istio 网关之南北向流量管理

作者 | 王夕宁 阿里巴巴高级技术专家 参与阿里巴巴云原生公众号文末留言互动&#xff0c;有机会获得赠书福利&#xff01; 本文摘自于由阿里云高级技术专家王夕宁撰写的《Istio 服务网格技术解析与实践》一书&#xff0c;文章介绍将集群外部的客户端连接到集群内运行的服务&…

想在边缘运行计算机视觉程序?先来迎接挑战!

作者 | alwaysAI翻译 | 火火酱~&#xff0c;责编 | 晋兆雨出品 | CSDN云计算头图 | 付费下载于视觉中国人工智能可以让计算机聪明地行动&#xff0c;并且在真实环境中快速做出决策&#xff0c;同时收获相对理想的效果。当然&#xff0c;这个概括性的定义较为宽泛和模糊&#xf…

企业微信_通讯录管理,获取部门列表部门成员及详情

企业微信H5_通讯录管理,获取部门列表部门成员及详情 文章目录一、POSTMAN调试1. 获取access_token2. 获取部门列表3. 获取部门成员4. 获取部门成员详情5. 获取成员详情二、实战演练2.1. 获取部门列表2.2. 获取部门成员2.3. 获取部门成员详情2.4. 获取人员详情三、代码讲解3.1.…

【深度学习】基于 Alluxio 数据缓存的性能优化

作者 | 车漾&#xff08;阿里云高级技术专家&#xff09;、顾荣&#xff08;南京大学 副研究员&#xff09; 导读&#xff1a;Alluxio 项目诞生于 UC Berkeley AMP 实验室&#xff0c;自开源以来经过 7 年的不断开发迭代&#xff0c;支撑大数据处理场景的数据统一管理和高效缓…

企业微信H5_身份验证,PC网站企业微信扫码授权登录

企业微信H5_身份验证,PC网站企业微信扫码授权登录 文章目录一、扫码登录配置1. 企业微信扫码登录接入流程2. 企业微信扫码登录接入流程拆解3. 开启网页授权登录二、实战演练2.1. 用户发起流程2.2. 构造授权链接2.3. 显示扫码二维码2.4. 用户扫码2.5. 用户确认登录2.6. 回调前端…

深根固柢 云起磐石——移动云大云磐石服务器重磅推出

中国移动云能力中心在2020年中国移动全球合作伙伴大会移动云分论坛上&#xff0c;发布了新型自研软硬融合弹性计算架构——大云磐石服务器。该架构以自主设计的可编程芯片HyperCard和轻量级Hypervisor为核心&#xff0c;将存储、网络虚拟化能力卸载到硬件芯片&#xff0c;彻底突…

面向大数据与云计算调度挑战的阿里经济体核心调度系统

编者按 伏羲&#xff08;Fuxi&#xff09;是十年前最初创立飞天平台时的三大服务之一&#xff08;分布式存储 Pangu&#xff0c;分布式计算 MaxCompute&#xff0c;分布式调度 Fuxi&#xff09;&#xff0c;当时的设计初衷是为了解决大规模分布式资源的调度问题&#xff08;本…