openGauss/MogDB 零字节问题处理

问题描述:java 应用端程序调用 GZIP 压缩类对数据进行编码压缩后入库 ,然后从数据库取出进行解压,原来再 mysql 数据库中是正常的,但迁移到 openGauss/mogdb 之后,解压出来的数据是乱码,不正常。

mysql 端表结构如下:

CREATE TABLE test (

id bigint(20) NOT NULL,

info varchar(20) NOT NULL,

info2 mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC;

迁移到 openGauss/MogDB 后表结构如下:

create table test(

id int,

info text,

info2 text

);

java 压缩接口方法如下:

public static String compress(String str) throws IOException {
if (null == str || str.length() <= 0) {
return str;
}
GZIPOutputStream gzip = null;
// 创建一个新的输出流
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
// 使用默认缓冲区大小创建新的输出流
gzip = new GZIPOutputStream(out);
// 将字节写入此输出流
gzip.write(str.getBytes("utf-8"));
// 因为后台默认字符集有可能是GBK字符集,所以此处需指定一个字符集
gzip.close();
// 使用指定的 charsetName,通过解码字节将缓冲区内容转换为字符串
return out.toString("ISO-8859-1");
} finally {
closeQuietly(gzip);
closeQuietly(out);
}
}

java 解压接口方法如下:

public static String unCompress(String str) throws IOException {

GZIPInputStream gzip = null;

if (null == str || str.length() <= 0) {

return str;

}

// 创建一个新的输出流

ByteArrayOutputStream out = new ByteArrayOutputStream();

// 创建一个 ByteArrayInputStream,使用 buf 作为其缓冲 区数组

ByteArrayInputStream in = new ByteArrayInputStream(str.getBytes("ISO-8859-1"));

try {

// 使用默认缓冲区大小创建新的输入流

gzip = new GZIPInputStream(in);

byte[] buffer = new byte[256];

int n = 0;

// 将未压缩数据读入字节数组

while ((n = gzip.read(buffer)) >= 0) {

out.write(buffer, 0, n);

}

// 使用指定的 charsetName,通过解码字节将缓冲区内容转换为字符串

return out.toString("utf-8");

} finally {

closeQuietly(gzip);

closeQuietly(in);

closeQuietly(out);

}

}

测试用例部分关键代码参考如下:

1.对 UTF8 编码的字符串数据进行压缩,然后存到数据库中

String str = "{"name":"jerome","familyName":"peng","company":"enmotech"}";

System.out.println("input:"+str);

String compress_java = GZipUtils.compress(str);

    try{
ps = conn.prepareStatement(sql);
ps.setInt(1, 100);
ps.setString(2, str);
ps.setString(3, compress_java);
ps.execute();
} catch (Exception e) {
e.printStackTrace();
}

2.从数据库中取出字段进行解密

    sql = " select info,info2 from test where id=100";
ResultSet rs = null;
try{
ps = conn.prepareStatement(sql);
rs = ps.executeQuery();
while (rs.next()) {
String compress_db = rs.getString(2);
String unCompress= GZipUtils2.unCompress(compress_db );
System.out.println("output:"+unCompress);
}
} catch (Exception e) {
e.printStackTrace();
}

期望结果是从数据库中取出来的字符串能够解压出原始数据。也就是上面的 unCompress 变量输出的结果应该要与上面的 str 变量输出结果一致,应为:

{"name":"jerome","familyName":"peng","company":"enmotech"}

如果我们在 pg 数据库里进行测试,上面测试第一步会报错提示无法对 0 字节进行存储

org.postgresql.util.PSQLException: ERROR: invalid byte sequence for encoding "UTF8": 0x00

但在 openGauss/MogDB 里面,数据可以正常存储,不会报错,但是压缩接口进行解码时数据显示乱码。

下面我们对比入库前和入库后的字节序列(以 hex 字符形式显示,两个字符表示一个字节):

入库前的 hex 字符串

1f8b0800000000000000ab56ca4bcc4d55b252ca4a2dca07327494d2127333732afd20a205a979e940b1e4fcdc82c4bc4aa0406a5e6e7e496a7286522d003efb28273a000000

入库后的 hex 字符串

1f8b0820202020202020ab56ca4bcc4d55b252ca4a2dca07327494d2127333732afd20a205a979e940b1e4fcdc82c4bc4aa0406a5e6e7e496a7286522d203efb28273a202020

我们发现其实是 00 与 20 的差异,所有的 hex 00 被转义为了 hex 20,也就是 0 字节被转义为了空格。

既然知道了这个差异,那我们对取出的数据做一次反向替换,应该可以解决这个问题。

我们可以按字节进行读取,如果数值是 32(hex 20 对应十进制 32)的字节,那我们就替换为 0 字节。

if(bytes_src[i]==32) {

bytes_dest[i]=(byte)0;

}else {

bytes_dest[i]=bytes_src[i];

}

这样修改之后测试发现还是有问题,因为压缩后的字节数据里可能也包含 hex 20,这样我们会把不该替换的字节也做了误处理。

进一步修正为只对首尾固定的部分进行处理,思路来源与 GZIP 公共类。

//头部10个字节或者尾部8个字节还原0字节

if((i<=10 || i>=len-1-8) && bytes_src[i]==32) {

bytes_dest[i]=(byte)0;

}else {

bytes_dest[i]=bytes_src[i];

}

这样处理后,测试数据可以正常解压,测试结果如下:

input:{"name":"jerome","familyName":"peng","company":"enmotech"}

HEX_ja:1f8b0800000000000000ab56ca4bcc4d55b252ca4a2dca07327494d2127333732afd20a205a979e940b1e4fcdc82c4bc4aa0406a5e6e7e496a7286522d003efb28273a000000

HEX_db:1f8b0820202020202020ab56ca4bcc4d55b252ca4a2dca07327494d2127333732afd20a205a979e940b1e4fcdc82c4bc4aa0406a5e6e7e496a7286522d203efb28273a202020

HEX_cv:1f8b0800000000000000ab56ca4bcc4d55b252ca4a2dca07327494d2127333732afd20a205a979e940b1e4fcdc82c4bc4aa0406a5e6e7e496a7286522d003efb28273a000000

output:{"name":"jerome","familyName":"peng","company":"enmotech"}

openGauss/MogDB零字节问题处理的更多相关文章

  1. C语言中的位操作(12)--判断一个数字是否包含一个全零字节

    本文主要介绍一系列算法,算法主要功能是判断一个数字(二进制)中是否包含全零字节 e.g.1010 1111 0000 0000 1001 1111 0001 1111 即 32位整数:A4A3A2A1 ...

  2. 各种编码中汉字所占字节数;中文字符集编码Unicode ,gb2312 , cp936 ,GBK,GB18030

    vim settings set fileencodings=utf-8,ucs-bom,gb18030,gbk,gb2312,cp936,latin1set termencoding=utf-8se ...

  3. BOM的来源是不可能出现的字符,GB2312双字节高位都是1,Unicode理论的根本缺陷导致UTF8的诞生

    Unicode字符编码规范   http://www.aoxiang.org 2006-4-2 10:48:02Unicode是一种字符编码规范 . 先从ASCII说起.ASCII是用来表示英文字符的 ...

  4. 【实验向】问题:假设计算机A和计算机B通信,计算机A给计算机B发送一串16个字节的二进制字节串,以数组形式表示:

    问题: 假设计算机A和计算机B通信,计算机A给计算机B发送一串16个字节的二进制字节串,以数组形式表示: unsigned char[16] = {0x3f, 0xa0, 0x00, 0x00, 0x ...

  5. oracle to mogdb 迁移---mtk工具

    ## 一.MTK工具介绍--------- MTK–异构数据迁移工具 MTK全称为 Database Migration Toolkit,是一个可以将Oracle/DB2/MySQL/openGaus ...

  6. 前端学HTTP之实体和编码

    前面的话 每天都有各种媒体对象经由HTTP传送,如图像.文本.影片以及软件程序等.HTTP要确保它的报文被正确传送,识别.提取以及适当处理.为了实现这些目标,HTTP使用了完善的标签来描述承载内容的实 ...

  7. Linux设备文件简介(转载)

    Linux 中的设备有2种类型:字符设备(无缓冲且只能顺序存取).块设备(有缓冲且可以随机存取).每个字符设备和块设备都必须有主.次设备号,主设备号相同的设 备是同类设备(使用同一个驱动程序).这些设 ...

  8. 我的MYSQL学习心得(四) 数据类型

    我的MYSQL学习心得(四) 数据类型 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(五) 运 ...

  9. PHP curl报错“Problem (2) in the Chunked-Encoded data”解决方案

    $s = curl_init(); curl_setopt($s, CURLOPT_POST, true); curl_setopt($s, CURLOPT_POSTFIELDS, $queryStr ...

  10. [转]C语言SOCKET编程指南

    1.介绍 Socket编程让你沮丧吗?从man pages中很难得到有用的信息吗?你想跟上时代去编Internet相关的程序,但是为你在调用 connect() 前的bind() 的结构而不知所措?等 ...

随机推荐

  1. HashMap,TreeMap,LinkedHashMap的默认排序

    简单描述 Map是键值对的集合接口,它的实现类主要包括:HashMap,TreeMap,HashTable以及LinkedHashMap等. TreeMap:能够把它保存的记录根据键(key)排序,默 ...

  2. 【Azure 事件中心】Event Hubs如何获取其中存放的历史消息

    问题描述 使用Azure Event Hub服务,除了正常的生产,消费消息以外,如果想拿到Event Hub中存储的历史消息?有什么方法呢? 问题解答 获取 Event Hubs 存储的历史消息,首先 ...

  3. 【Azure 环境】使用 az ad group create 时候遇见 Insufficient privileges to complete the operation

    问题描述 使用China Azure,通过Azure CLI 创建AAD组报错,提示权限不足 Insufficient privileges to complete the operation # 使 ...

  4. Java 常用类 String类与其他结构之间的转换-----String 与 byte[]之间的转换

    1 /** 2 * 3 * String 与 byte[]之间的转换 4 * 编码:String ---> byte[]:调用String的getBytes() 5 * 解码:byte[]--- ...

  5. Java 封装+构造器+this 小测试

    1 package com.bytezero.account; 2 3 4 public class Account 5 { 6 private int id; //账号 7 private doub ...

  6. Java 封装性的四种权限测试 + 总结

    *    总结封装性:Java提供了4中权限修饰符来修饰类及类的内部结构,体现类及类的内部结构再被调用时的可见性的大小 1 package com.bytezero.circle; 2 3 publi ...

  7. Java 从键盘输入不确定的整数 并判断读入的整数和负数的个数,输入0时候结束

    1 /** 2 * 从键盘输入不确定的整数 并判断读入的整数和负数的个数,输入0时候结束 3 * 4 */ 5 6 Scanner scan = new Scanner(System.in); 7 8 ...

  8. 【主流技术】日常工作中关于 JSON 转换的经验大全(Java)

    目录 前言 一.JSON 回顾 1.1结构形式 二.其它类型 -> JSON相关 2.1 JavaBean 转 JsonObject 2.2 JavaBean 转 Json 字符串 2.3 Li ...

  9. CSS Navigation - vscode 插件 - vue css 跳转

    CSS Navigation - vscode 插件 - vue css 跳转

  10. SqlServer复制和订阅(实现主从同步)

    SqlServer复制和订阅 注意: 1.登录必须是服务器名称不能是ip 2.订阅服务器不需要提前创建数据库 复制 1.展开要发布的数据库节点,找到复制下的本地发布 2.右击本地发布,选择本地发布 3 ...