眼见不一定为实之MySQL中的不可见字符

目录

前言

一、问题的由来

1、需求背景

2、数据表结构

二、定位问题

1、初步的问题

2、编码是否有问题

3、依然回到字符本身

三、深入字符本身

1、回归本质

2、数据库解决之道

3、代码层解决

四、总结


前言

        在开始今天的博客内容之前,正在看博客的您先来看看以下这两条sql,如果您刚好还有MySQL的环境。不妨先猜测一下它输出的内容,然后看看是否有什么区别?第一条sql如下:

select length('G30L3B01') as l1;

        接着再来看另一条sql,sql脚本如下:

select length('G30L3B01') as l2;

        各位不妨猜测一下,上面两条sql语句的执行结果l1和l2分别是多少?是不是在你的预料之中。 这里不卖关子了,相信执行过sql的朋友一定看到了结果。没有数据库客户端的朋友来看我的执行结果。在给出结果之前,首先把基础环境介绍一下。这里的服务器用的的个人的Windows 7 专业版开发机,数据库服务用的是MySQL5.7,打开的查询服务窗口所在的数据库编码是UTF-8。

         上面这个结果有没有超出你的预想,这两个字符串“看”起来似乎是一模一样的。这里的“看”我打了引号,至于原因,后面会讲到。本文即以上述场景为例,讲解在MySQL中,为什么会有这种“看”起来一致,但实际上不一样的问题,通过现象找本质,通过一步一步的排查,找到问题的根源,最后在寻根溯源后,找解决的办法;从应用代码编程的角度和底层数据库的角度来解决上述问题。如果您现在也遇到了这种“看”起来不正常的值,不妨一起交流一下。

一、问题的由来

        首先依然要介绍一下上述问题的出现场景,以便于其它的朋友在此情此景下,有更大的印象。因此本节首先将对问题的场景进行详细的描述。这里打算从两个方面讲述,第一个方面是讲述需求背景,即在什么情况下作这个事。第二个是讲述数据背景,把相关的表设计也说明一下。

1、需求背景

        事情发生的背景是这样的,客户要求我们做一个功能,他们会提供一个数据的Excel模板,然后我们需要将这些数据批量导入到数据库中。这个需求咋一看起来,是一个非常简单的需求啊。读取Excel的依赖库,一大把。解析Excel的数据,然后批量插入到数据库中,对于MybatisPlus或者其它的ORM工具都是非常简单的事。当时我们选择的是阿里巴巴开源的读取Excel的组件。感兴趣的朋友可以自己上github搜索一下。然后也基于组件也已经将Excel没个表格都读取到了内存中,然后调用MP的批量插入方法,意外的是在进行批量插入的时候数据库报错了。下面还是将数据库的表先做一个介绍。

2、数据表结构

        为了实现将数据导入到数据库中,根据面向对象的原则,我们将Excel表格中的每个单元格都设计成了一个字段,然后对字段的数据类型进行了设计,同时包括数据长度。其中有一个项是输入数据的编码,然后用户是有编码规则的,每一个数据都有一个对应的编码规则。我们简单的看了他们的编码规则,得知其长度大致为8位,因此我们在数据库中设计成了varchar(8);这么设计其实是中规中矩的,非常合理。然而在上面的批量入库过程中就一直报对应的这个字段too long。这个异常看起来很奇怪,因为我们人工去“看”的时候,这个字符串的长度确实是8。于是陷入了沉思。

二、定位问题

        既然在开发过程当中出现了问题,那么如何解决问题呢?在解决问题之前,首先要定位问题,只有正确的定位问题,才能对症下药,问题才能迎刃而解。因此本节将重点讲解怎么定位问题。

1、初步的问题

        当看到上面问题的时候,第一感觉是懵的,刚开始都有点不敢相信自己的眼睛。因为肉眼根本“看”不出来这个字符串居然不是8位。最开始怀疑的是在字符串的前后可能存在空格,因此导致了其长度超出了8位。为此我们将数据进行去空,使用sql进行去空如下:


SELECT length('G30L3B01') AS VisibleLength,
       length(replace('G30L3B01', '​', '')) AS CleanLength;

        然后发现还是不对,经过替换后其长度还是11,说明还是超长了。 

2、编码是否有问题

        在进行空字符过滤之后还是没有解决问题。于是换了一个方向,想着有没有可能是字符集的问题。其实大家可以看看UTF-8的字符集规范,字符集虽然会有一定的影响,但是这里存储的都是英文和数字,其长度均是标准的1,因此不存在字符的问题。问题一下子没有了方向,不知道往哪个方面去排查。

3、依然回到字符本身

        在第一次尝试了空字符替换无果,又排除了字符编码的问题后,再一次将目光投入到字符本身。这一次的想法是,虽然空字符本文替换后,长度还是11,但是这并不说明其内容一定是空格。有没有什么其它的东西在捣乱呢。有时候找问题就是这样,反反复复。

三、深入字符本身

        上面从几个方面分析了可能存在的问题,尤其是第三点,我们从最开的字符又回到了起点。那么这次采用什么分析思路呢?本小节深入道来。

1、回归本质

        为了来看看这个数据到底是什么?我们将字符串转为十六进制的字符串,通过比较原始字符串来看看区别。在MySQL中可以使用HEX(str)实现转换。相关的转换SQL如下所示:

SELECT HEX('G30L3B01') AS hex_representation,HEX('G30L3B01') as n2;

hex_representation	n2
4733304C33423031	4733304C33EFBBBF423031

        不知道眼尖的你发现了问题没有,上面这条SQL执行完之后发现,其目标字符串似乎不一样啊。后面的字符串,也就是有问题的字符串其长度真的超长了。下面仔细对比这两个字符串。

4733304C33        423031	
4733304C33 EFBBBF 423031

        为了方便展示,我把相同的部分进行对比,我们发现,前面的字符经过十六进制的转换后,一共16个字节,8位。是符合我们的预期的,而下面的十六进制字符则多了6字节,一共22字节,记11位。这也就是为什么这两个字符串不一样的原因。那么这个多出来的EFBBBF又是什么呢?由于这是转成了十六进制的表示,我们需要将其转为十进制。将十六进制转十进制,如果不想计算的朋友可以在网页中直接搜索:

         当然你也可以在MySQL中进行进制转换,转换方法如下:

-- CONVERT(CONV('EFBBBF', 16, 10), UNSIGNED) 是在mysql中实现将16进制转10进制数据
select length(char(15711167)),hex(char(15711167)),CONVERT(CONV('EFBBBF', 16, 10), UNSIGNED);

        执行之后,可以看到:

length(char(15711167))	hex(char(15711167))	CONVERT(CONV('EFBBBF', 16, 10), UNSIGNED)
3	EFBBBF	15711167

        到这里,为什么两个“看”起来一模一样的字符串,实际上不一样呢?根源就在这里,在字符串的中间位置插入了不可见字符。就是这不可见字符EFBBBF导致我们做数据插入时报too long。

2、数据库解决之道

        在明确了以上的问题所在之后,我们就可以根据实际情况来进行调整。只要将这种不可见字符替换掉即可。实现的方式也很简单,sql如下:

select length(REPLACE('G30L3B01', CHAR(15711167),''))  t;

t
8

        通过replace函数就实现了不可见字符EFBBBF的去除,这样再调用Insert语句,就不会报too long的问题。

3、代码层解决

        除了在数据库层来解决问题,还可以在什么地方解决呢?在编码层解决是否可行。答案是肯定的。下面我们来详细介绍一下代码层的处理办法,以java语言为例。其实原理是一样的,都是要将不可见字符进行替换掉。

/*
* 字节数组转16进制字符串
*/
public static String bytesToHexString(byte[] bArr) {
	if (bArr == null) {
		return null;
	}
	StringBuffer sb = new StringBuffer(bArr.length);
	String sTmp;
	for (int i = 0; i < bArr.length; i++) {
		sTmp = Integer.toHexString(0xFF & bArr[i]);
		if (sTmp.length() < 2)
			sb.append(0);
		sb.append(sTmp);
	}
	return sb.toString();
}

// 转化十六进制编码为字符串
public static String toStringHex(String s) {
	byte[] baKeyword = new byte[s.length() / 2];
	try {
		for (int i = 0; i < baKeyword.length; i++) {
	baKeyword[i] = (byte) (0xff & Integer.parseInt(s.substring(i * 2, i * 2 + 2), 16));
		}
		s = new String(baKeyword, "utf-8");// UTF-16le:Not
	} catch (Exception e1) {
			e1.printStackTrace();
	}
	return s;
}

        下面给出测试代码,您可以实际测试以下结果,看是否与我的预期一样:

public static void main(String[] args) {
		System.out.println("G8014Z03".substring(0, 2));
		System.out.println(bytesToHexString("G30L3B01".getBytes()).toUpperCase()); // 长
		System.out.println(bytesToHexString("G30L3B01".getBytes()).toUpperCase()); // 短
		System.out.println("G30L3B01".length()); // 长
		System.out.println("G30L3B01".length()); // 短
		System.out.println("进制转换======================================================");
		System.out.println(toStringHex("4733304C33423031"));
		System.out.println(toStringHex("4733304C33423031").length());
		System.out.println(toStringHex("4733304C33EFBBBF423031"));
		System.out.println(toStringHex("4733304C33EFBBBF423031").length());
		System.out.println("================================================");
		System.out.println(toStringHex("4733304C33EFBBBF423031".replaceAll("EFBBBF", "")));
		System.out.println(toStringHex("4733304C33EFBBBF423031".replaceAll("EFBBBF", "")).length());
		
		System.out.println("G30L3B01".equals("G30L3B01"));
		
		System.out.println(toStringHex("4733304C33EFBBBF423031".replaceAll("EFBBBF", "")).equals("G30L3B01"));
	}

        在代码中使用上述代码也可以实现目标字符的替换,将不可见字符进行替换掉。欢迎在实践中进行测试使用。

四、总结

        以上就是本文的主要内容,本文以数据库中不可见字符处理为例,讲解在MySQL中,为什么会有这种“看”起来一致,但实际上不一样的问题,通过现象找本质,通过一步一步的排查,找到问题的根源,最后在寻根溯源后,找到解决的办法;从应用代码编程的角度和底层数据库的角度来解决上述问题。

        使用建议,如果您也有这方面的问题,尤其是从Excel或者其它的模板中导入数据的,很有可能会遇到这个问题。如果碰到看起来一致,但实际内容不一样的情况,可以试试看是不是碰到了不可见字符,本文即分享了一种在MySQL中的不可见字符的解决方案,同时分享了使用JAVA语言进行上述问题的解决。本文行文仓促,难免有不足之处,欢迎各位专家朋友在评论区批评指正,不慎感激。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ENVI实战—一文搞定非监督分类

实验1&#xff1a;使用isodata法分类 目的&#xff1a;学会使用isodata法开展非监督分类 过程&#xff1a; ①导入影像&#xff1a;打开ENVI&#xff0c;按照“文件→打开为→光学传感器→ESA→Sentinel-2”的顺序&#xff0c;打开实验1下载的哨兵2号数据。 图1 ②区域裁剪…

【Oracle篇】Oracle数据库坏块处理:rman修复坏块实践与案例分析(第七篇,总共八篇)

&#x1f4ab;《博主介绍》&#xff1a;✨又是一天没白过&#xff0c;我是奈斯&#xff0c;DBA一名✨ &#x1f4ab;《擅长领域》&#xff1a;✌️擅长Oracle、MySQL、SQLserver、阿里云AnalyticDB for MySQL(分布式数据仓库)、Linux&#xff0c;也在扩展大数据方向的知识面✌️…

分层Agent

分层Teams 分层Agent创建tool研究团队工具文档编写团队工具 通用能力定义Agent团队研究团队文档编写团队 添加图层 分层Agent 在前面的示例&#xff08;Agent管理&#xff09;中&#xff0c;我们引入了单个管理节点的概念&#xff0c;用于在不同工作节点之间路由工作。 但是&a…

ppt转换word文档怎么操作?6个软件让你自己轻松转换文件

ppt转换word文档怎么操作&#xff1f;6个软件让你自己轻松转换文件 将PPT文件转换为Word文档是一项常见的任务&#xff0c;可以通过多种软件和在线工具来实现。以下是六款常用的软件和工具&#xff0c;它们可以帮助您轻松地将PPT文件转换为Word文档&#xff1a; 1.迅捷PDF转换…

②-Ⅱ单细胞学习-组间及样本细胞比例分析(补充)

数据加载 ①单细胞学习-数据读取、降维和分群_subset函数单细胞群-CSDN博客‘ #2024年6月20日 单细胞组间差异分析升级# rm(list = ls()) library(Seurat)#数据加载(在第一步已经处理好的数据) load("scedata1.RData")#这里是经过质控和降维后的单细胞数据 tabl…

STM32单片机-FLASH闪存

STM32单片机-FLASH闪存 一、FLASH简介二、FLASH工作原理三、读写内部FLASH四、读取芯片ID 一、FLASH简介 STM32F1系列的FLASH包含程序存储器、系统存储器和选项字节三个部分&#xff0c;通过闪存存储器接口(外设)可以对程序存储器和选项字节进行擦除和编程读写FLASH的用途&…

2024年6月20日 (周四) 叶子游戏新闻

超市播音系统: 定时播放不同音乐 强制卸载软件: 一款强制卸载软件 免费多人沙盒游戏《宝藏世界》推出更新“潮起潮落”&#xff0c;带来全新克苏鲁风冒险准备好迎接一场超凡的冒险吧&#xff0c;MMORPG发行商gamigo宣布《宝藏世界》的最新更新&#xff1a;“潮起潮落”。这次更…

计算机编码以及URL转码

目录 一、计算机编码 1.ASCII编码 2. GB2312编码 3.GBK编码 4.UTF-8编码 二、URL转码 1.encodeURI和decodeURI 2.encodeURIComponent 和 decodeURIComponent 三、Base64 一、计算机编码 在计算机中&#xff0c;所有的数据在存储和运算时都要使用二进制数表示&#xf…

Redis 网络模型

一、用户空间和内核空间 1.1 linux 简介 服务器大多采用 Linux 系统&#xff0c;这里我们以 Linux 为例来讲解&#xff0c;下面有两个不同的 linux 发行版&#xff0c;分别位 ubuntu 和 centos&#xff0c;其实发行版就是在 Linux 系统上包了一层壳。 任何 Linux 发行版&#…

【ARM】PK51如何将BL51链接器切换成LX51链接器

【更多软件使用问题请点击亿道电子官方网站】 1、 文档目标 解决客户在使用PK51进行项目研发的时候&#xff0c;想要使用LX51链接器进行使用。 2、 问题场景 客户在使用51芯片进行开发的时候&#xff0c;发现工程中使用的是BL51链接器&#xff0c;而不是LX51链接器&#xff…

多模块存储器

随着计算机技术的发展&#xff0c;处理的信息量越来越多&#xff0c;对存储器的速度和容量要求也越来越高&#xff1b;而且随着CPU性能的不断提高、IO设备数量不断增加&#xff0c;导致主存的存取速度已经称为了整个计算机系统的性能瓶颈。这就要求我们必须提高主存的访问速度。…

Hadoop3:MapReduce中实现自定义排序

一、场景描述 以统计号码的流量案例为基础&#xff0c;进行开发。 流量统计结果 我们现在要对这个数据的总流量进行自定义排序。 二、代码实现 我们要对总流量进行排序&#xff0c;就是对FlowBean中的sumFlow字段进行排序。 所以&#xff0c;我们需要让FlowBean实现Writab…

第二十五篇——信息加密:韦小宝说谎的秘诀

目录 一、背景介绍二、思路&方案三、过程1.思维导图2.文章中经典的句子理解3.学习之后对于投资市场的理解4.通过这篇文章结合我知道的东西我能想到什么&#xff1f; 四、总结五、升华 一、背景介绍 加密这件事&#xff0c;对于这个时代的我们来说非常重要&#xff0c;那么…

互联网应用主流框架整合之Spring Boot基本概念

Spring Boot是用来简化Spring应用程序的搭建、开发、测试和部署过程的&#xff0c;该框架使用了特定的方式进行配置&#xff0c;从而使开发人员不再需要定义样板化的配置&#xff0c;SpringBoot致力于快速应用开发(Rapid Application Development)领域的发展&#xff0c;它通过…

这些帮助你成长的IOS应用,建议收藏

TrackIt TrackIt是一款功能丰富的任务清单、日程管理和习惯打卡应用&#xff0c;旨在帮助用户提高效率和专注力。通过这些功能&#xff0c;用户可以更好地规划时间和任务&#xff0c;从而实现个人目标和养成良好习惯。 在目标设定方面&#xff0c;SMART原则是一个常用的方法&a…

【CPP】选择排序:冒泡排序、快速排序

目录 1.冒泡排序简介代码分析 2.快速排序2.1霍尔版本简介代码分析 2.2挖坑版本2.3前后指针版本2.4非递归的快排思路代码 什么是交换排序&#xff1f; 基本思想&#xff1a;所谓 交换&#xff0c;就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置&#xff0…

Meta FAIR研究新成果:图像到文本、文本到音乐的生成模型,多标记预测模型以及AI生成语音检测技术

Meta AI研究实验室(FAIR)公开发布了多项新研究成果&#xff0c;包括图像到文本和文本到音乐的生成模型&#xff0c;多词预测模型&#xff0c;以及检测AI生成语音的技术。发布的成果体现了开放性、协作、卓越和规模化等核心原则。公开早期研究工作旨在激发迭代&#xff0c;推动A…

uniapp 实人认证

首先Dcloud创建云服务空间&#xff0c;开启一键登录并充值 下一步 1. 右键项目 》 创建uniCloud云开发环境 》右键uniCloud》关联云服务空间 2. cloudfunctions右键 新建云函数&#xff0c;任意命名&#xff08;例&#xff1a;veify&#xff09;&#xff0c;然后右键项目》管…

加密好的WPSword文档,忘记密码怎么办?

在日常办公和学习中&#xff0c;我们经常使用WPS Word等文档处理软件来创建和编辑重要文件。为了保护这些文件不被未经授权的人访问&#xff0c;我们通常会选择给文档设置密码。然而&#xff0c;有时我们可能会因为时间久远或其他原因而忘记自己设置的密码&#xff0c;这时该如…

IT运维全面数字化|芯片设计行业领跑打造运维流程闭环

在当今数字化转型的浪潮中&#xff0c;科技行业正经历着前所未有的变革。随着5G、人工智能、物联网等新兴技术的快速发展&#xff0c;企业对于高效、智能的运营模式的需求日益迫切。 芯片设计公司作为科技产业链中的关键一环&#xff0c;不仅要在技术创新上保持领先&#xff0…