当前位置: 首页 > 后端技术 > Java

将一个Java字符串拆分成一个不超过固定字节长度的子字符串数组列表

时间:2023-04-02 00:08:09 Java

最近需要将邮件内容保存到数据库中。由于数据库中对应的字段有长度限制,所以去百度找了个长度比较大的字符串是分段保存的,但是在搜索引擎上往往只有一些使用String.split的方法,而这个split是基于字符串中的字符。如果是UTF-8编码,一个中文是4个字节,String.split后的实际长度不是字节长度。想必有人会想:直接把字节数组存到数据库里不是很好吗?答案是可以的,但是考虑到不方便查看,而且邮件内容是html格式,有时候网上有问题想手动修改的时候比较麻烦。截的题,所以自己写的,希望在这里分享给大家,不对的地方还请大家指正。首先第一个问题,截取字符串,逐字节拆分子串不是很简单吗?直接在str.getBytes()之后,把字节数组下标成子字节数组,相信大家都会这么想。但是按字节截取字符串其实有一个问题,就是如果截取的位置恰好在字符编码的中间,字符就会变成乱码。下面的代码就是为了说明问题,字符串是17个中文'一':publicclassTest{publicstaticvoidmain(String[]args)throwsUnsupportedEncodingException{intlen=5;Stringstr="一一一一一一一一一一一一一一一一一一一一一一一一一的一一一一一一一一一一一一一一一一一一一一一一byte[]bt=str.getBytes(StandardCharsets.UTF_8);字节[]br=新字节[len];System.arraycopy(bt,0,br,0,len);Stringres=newString(br,StandardCharsets.UTF_8);System.out.println(res);}输出结果为:1.因为一个中文是4个字节,而我们要5个字节的字符串,当然第二个“一”的一部分会被截去乱码人物。这时候大家也会想说:没关系,截取之后,就算乱码放在一起也没关系。我也天真地这么想,于是我把字节数组转成字符串后,拼接起来,发现更乱了。代码如下:publicstaticvoidmain(String[]args)throwsUnsupportedEncodingException{intlen=5;Stringstr="一个一个一个一个一个一个一个一个一个一个一个一个一个一个一个一个一个一个一个一个一个一个一个0,br,0,len);Stringres=newString(br,StandardCharsets.UTF_8);System.arraycopy(bt,5,br,0,len);Stringresnext=newString(br,StandardCharsets.UTF_8);System.out.println(res+resnext);}输出结果:1.1.之前遇到这个字节截断问题的时候,查了很多文章,大部分方法都是用Charset来先设置Encoding设置,然后用CharBuffer封装原始字符串再截取,由于这两个类我用的不多,当时也不是很懂,所以就放弃使用这个了。后来看了一些大佬的实现,他们的解决方案很巧妙,如下:1.先通过字节数组截取得到截取固定长度的子字节数组,2.将字节数组转为字符串得到一个新的String子串,3.将新的String子串再次转换为字节数组,比较两个数组的长度(因为新的String子串转换为字节数组时,截取的一半字符会完成为一个相应的代码集字符长度),4.如果新的String子串的字节数组比步骤1中根据长度截取的子串的字节数组长,则说明存在半截取的字符,而这个字符在最后一个位置会被丢弃,所以根据字符串的长度将新的String子串截掉1位,得到的字符串是没有截掉一半字符的子串,且长度小于等于到所需的字节长度。上面只是从字符串中截取了一个不超过固定字节长度的子串,那么我们如何将一个长字符串分成一个不超过固定字节长度的子串字符数组列表呢?具体代码如下:/***方法:将字符串按固定长度的字节分成一个数组*startPos在原字符串的字节数组开头截取子串*startStrPos在处截取下标原字符串开头*strLen原字符串字节数组长度*背景:由于编码格式不同,直接截取可能会得到半截乱码,比如utf-84byte,一个中文,如果截取是5byte,会出现乱码*原理:1.先根据字节数组截取,得到长度不大于固定截取长度的字节数组*2.将字节数组转为字符串,得到新的子串,以及然后转换字节数组,比较两个数组的长度(newsubstring字符串转换为字节数组时,截取的一半字符会补全到对应编码集中一个字符的长度),*如果新子串的byte数组比按照长度截取的子串的byte数组长,说明还有Intercepthalfofcharacters,这个字符会在最后一个位置,丢弃*所以截取新的子串根据字符串的长度减1位,得到的字符串是还没有被截取一半的字符,长度小于等于要求的字节长度子串。**1.当子串字节数组开始截取小于原字符串字节数组长度的下标时,一直循环*2.子串字节数组的大小需要根据截取的下标与长度的差值当前父字符串字节数组的和通过比较预期截取的字节长度创建(否则System.arraycopy会报错)*3.根据原字符串字节数组开头的子字符串,截取下标复制内容父字节数组到子字节数组的长度*4.根据原字符串开头截取的下标和子字节数组转换成字符串后的长度,在父字符串中截取一个伪子字符串(maybehalfofthelastcharacteris乱码)*5.比较伪子串转换如果字节数组后置长度大于预期截取的字节数组长度,则伪子串截取字符串长度-1*6.子串字节数组开始截取下标+得到的子串字节长度;子串在原始字符串开头的下标+得到的子串的字符长度*@paramstr原始字符串*@paramlen分割字符串的字节长度*@paramcharSet编码后的字符集*@returnList拆分子串*@throwsUnsupportedEncodingException*/publicstaticfinalListdivideStrByBytes(Stringstr,intlen,StringcharSet)throwsUnsupportedEncodingException{ListstrSection=newArrayList<>();byte[]bt=str.getBytes(charSet);intstrLen=bt.length;int开始位置=0;intstartStrPos=0;while(startPoslen){res=res.子串(0,resLen-1);}startStrPos+=水库。长度();strSection.add(res);startPos+=res.getBytes(charSet).length;}返回海峡;}试一下效果:publicstaticvoidmain(String[]args)throwsUnsupportedEncodingException{intlen=5;Stringstr="oneoneoneoneoneoneoneoneoneoneoneoneoneoneoneoneoneoneoneoneoneoneoneoneoneoneoneoneoneoneoneoneoneoneonestringList=divideStrByBytes(str,len,"UTF-8");stringList.forEach(item->{System.out.println(item);});}}输出结果:17Chinese'一'希望这段代码可以帮助到大家,如有错误,请分享指正,谢谢!