【踩坑记录】字节流数据按照string的方式读取然后按照string的方案存储,编码导致二进制数据发生变化,原理记录
目录
问题缘由
由于公司需求,需要读取游戏Redis数据做内外网数据迁移,没有与游戏组过多的沟通。 使用的数据类型是Hash, key是string,value是byte[]。以前对于编码的理解是:计算机底层存储的永远是01的二进制数据,编码是一种对于计算机二进制数据的字符映射,也就是约定好哪个值对应哪个字符。是为了便于在显示器上展示。
那么基于这个理解,我就以为 不需要关心存储的数据类型,因为我不需要用到数据,我只是负责做数据的搬运。于是我用的是HGetAsync方法去读的字符串。然后HSetAsync把数据存到另一个Redis。结果发现数据发生了编码。基于我上边对于编码的理解,也就是按照不同的编码读取字符串,只是显示器上会乱码,但是底层的01二进制没有发生变化,这次问题打破了我的认知。
背后原理
当一个byte[]在计算机中存储时,它就是以二进制形式保存的。如果这个byte[]中的每一个字节代表的是ASCII码(一个字节表示一个字符),那么它在不同的编码下读取应该没有问题。但是,如果它代表的是Unicode字符集(UTF-8和UTF-16等),那么在不同的编码下读取就会发生问题。因为不同的编码方式对存储方式和字节长度都有不同的要求。
以UTF-8为例,它对不同字符分配的位数不同。对于ASCII字符,UTF-8使用一个字节表示,而对于其他字符,它需要两个字节、三个字节或四个字节来表示。因此,在按照UTF-8格式读取一个byte[]时,如果它的编码确实是UTF-8,那么就可以读取正确的字符。但是,如果重新以UTF-8的格式存储它时,就会按照UTF-8的编码方式重新把这个字符转换成二进制。如果这个字符之前的编码不是UTF-8,那么它在转换为UTF-8的二进制时,就会变成不同的值,因此数据也就变了。
C#代码示例
var data = Encoding.UTF32.GetBytes("爱");
var word = Encoding.UTF8.GetString(data);
var word1 = Encoding.UTF32.GetString(data); File.WriteAllText($@"{AppDomain.CurrentDomain.BaseDirectory}/code.txt", word);
File.WriteAllText($@"{AppDomain.CurrentDomain.BaseDirectory}/code1.txt", word1); foreach (var d in File.ReadAllBytes($@"{AppDomain.CurrentDomain.BaseDirectory}/code.txt"))
{
Console.WriteLine(d);
}
Console.WriteLine("------------"); foreach (var d in File.ReadAllBytes($@"{AppDomain.CurrentDomain.BaseDirectory}/code1.txt"))
{
Console.WriteLine(d);
}
例如,我们有一个Unicode字符“爱”,其二进制表示为:0000 0100 1110 0111。按照UTF-8编码的规则,在存储这个字符时,我们需要使用3个字节的二进制数据:1110XXXX 10XXXXXX 10XXXXXX(X表示对应字符的二进制数据的高位)
我们将其存储到一个byte[]中,再将其存储到文件中。然后按照UTF-8的格式读取,解析出Unicode字符“爱”,再将其按照UTF-8的格式存储回文件。这时,由于使用了UTF-8编码,我们需要将Unicode字符“爱”转换为UTF-8编码的二进制数据,即,使用3个字节的二进制数据:11100100 10101110 10011111。
通过运行代码,可以看到,由于存储使用了UTF-8编码,而读取和重新存储又使用了UTF-8编码,因此二进制数据发生了变化。
总结
当一个byte[]在计算机中存储时,它就是以二进制形式保存的。如果这个byte[]中的每一个字节代表的是ASCII码(一个字节表示一个字符),那么它在不同的编码下读取应该没有问题。但是,如果它代表的是Unicode字符集(UTF-8和UTF-16等),那么在不同的编码下读取就会发生问题。
【踩坑记录】字节流数据按照string的方式读取然后按照string的方案存储,编码导致二进制数据发生变化,原理记录的更多相关文章
- [19/05/07-星期二] JDBC(Java DataBase Connectivity)_CLOB(存储大量的文本数据)与BLOB(存储大量的二进制数据)
一. CLOB(Character Large Object ) – 用于存储大量的文本数据 – 大字段有些特殊,不同数据库处理的方式不一样,大字段的操作常常是以流的方式来处理的.而非一般的字段,一次 ...
- BLOB存储图片文件二进制数据是非对错
子在一天一天虚度,生活也在一天一天中茫然 做人做事哪能尽如人意,付出多少收获多少虽然存在偏颇,但是不劳而获的心态是万万不对的,更不能去怨天尤人,低调为人.做好自己就可以了 改进你的系统的最好的方法是先 ...
- vue+ vue-router + webpack 踩坑之旅
说是踩坑之旅 其实是最近在思考一些问题 然后想实现方案的时候,就慢慢的查到这些方案 老司机可以忽略下面的内容了 1)起因 考虑到数据分离的问题 因为server是express搭的 自然少 ...
- 背水一战 Windows 10 (89) - 文件系统: 读写文本数据, 读写二进制数据, 读写流数据
[源码下载] 背水一战 Windows 10 (89) - 文件系统: 读写文本数据, 读写二进制数据, 读写流数据 作者:webabcd 介绍背水一战 Windows 10 之 文件系统 读写文本数 ...
- XML中二进制数据的处理方法
原文链接:http://www.west263.com/www/info/22308-1.htm 在xml中,所有的数据都是以文本的形式来显示,但是二进制数据不能直接以文本格式来表示,那xml又是怎么 ...
- Android学习笔记(十二)——使用意图传递数据的几种方式
使用意图传递数据的几种方式 点此获取完整代码 我们除了要从活动返回数据,也经常要传递数据给活动.对此我们能够使用Intent对象将这些数据传递给目标活动. 1.创建一个名为PassingData的项目 ...
- Erlang 位串和二进制数据
http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=25876834&id=3300393 因为在本人工作中,服务端Erla ...
- 讨论贴:Sqlserver varbinary 是二进制数据,却是十六进制的表现形式
首先创建一个数据表 CREATE TABLE [dbo].[log_info]( [id] [,) NOT NULL, [info] [varchar]() NULL, [info1] [varbin ...
- 认识Js中的二进制数据
Blob 在项目中涉及到要对html原生的audio组件进行样式复写,因此需要重新实现audio的一些功能,比如下载.实现一个下载大致的思路是服务端返回一段音频的二进制数据,客户端将其存放在Blob中 ...
- 转载:【原译】Erlang构建和匹配二进制数据(Efficiency Guide)
转自:http://www.cnblogs.com/futuredo/archive/2012/10/19/2727204.html Constructing and matching binarie ...
随机推荐
- Go语言:利用 TDD 测试驱动开发帮助理解数组与动态数组(切片)的区别
Array VS Slice 数组允许你以特定的顺序在变量中存储相同类型的多个元素. 对于数组来说,最常见的就是迭代数组中的元素. 我们创建一个 Sum 函数,它使用 for 来循环获取数组中的元素并 ...
- 深入理解 Python 虚拟机:集合(set)的实现原理及源码剖析
深入理解 Python 虚拟机:集合(set)的实现原理及源码剖析 在本篇文章当中主要给大家介绍在 cpython 虚拟机当中的集合 set 的实现原理(哈希表)以及对应的源代码分析. 数据结构介绍 ...
- EasyRelation发布,简单强大的数据关联框架
当开发人员需要进行关联查询时,往往需要编写大量的冗余代码来处理数据之间的关系.这不仅浪费时间和精力,还会影响项目的可维护性和可扩展性. EasyRelation 是一个简单.高效的自动关联数据框架,可 ...
- es6数组相关操作
1. 获取两个数组中某个属性值相等的项 let a=[{name:1},{name:4},{name:3}] let b=[{name:5},{name:4},{name:2}] let index ...
- JAVA基础——常用类(一)
首先认识到--String是不可以变性(final) String:字符串,使用一对""引起来表示. * 1.String声明为final的,不可被继承 * ...
- LabVIEW Actor Framwork (2)________ 边学边做server&client
回顾下初始需求: 现在要做一个类似聊天的demo,一个server端,若干个client端:首先是server启动,通过server可以打开若干个client端,然后每个client可以独立给serv ...
- 部署:keepalived-1.3.5+MHA部署mysql集群
MHA: MHA工作原理总结为以下几条: 从宕机崩溃的master保存二进制日志事件(binlog events): 识别含有最新更新的slave: 应用差异的中继日志(relay log)到其他sl ...
- vue条件判断循环
条件判断 v-if <!DOCTYPE html> <html lang="en"> <head> <meta charset=" ...
- Go语言实现基于TCP的内存缓存服务
接上文: https://www.cnblogs.com/N3ptune/p/16623738.html HTTP/REST的解析导致基于HTTP的内存缓存服务性能不佳,本次实现一个基于TCP的缓存服 ...
- 理解Linux系统: 进程
Linux内核版本: 2.6.11.12 编写代码: 创建进程 创建进程使用fork系统调用,官方文档对于fork的描述: fork() creates a new process by duplic ...