Java按字节截取字符串的实现
admin 发布于:2018-08-31 22:25:07
阅读:loading
本文所描述的按字节截取字符串是指在一个字符串中按指定的长度进行截取,与String的substring不一样的是,这里的字节表示是中文占2个字节,字母与数字等占1个字节,例:字符串“2018年8月31日”按3位截取则为201,按4位截取为2018,按5位截取为2018,按6位截取为2018年...按8位截取为2018年8,按9位截取为2018年8月,等等,主要就是当这个指定位置为中文时,不能出现被截断成半截的乱码情况。
理论上讲,按字节截取字符串可以应用在许多地方,关于此需求的实现从网上找了一些其它的示例,发现本人看不懂(在这个算法方面太渣太渣),于是就自己琢磨着去实现了一下,主要是将字符串转换为Unicode编码的byte数组,再去截取数组中的数据,我们知道中文有几种编码情况,GBK的一个中文字符占2个字节,英文占1个字节;UTF8的一个中文占3个字节,Unicode编码的中文与英文均占2个字节,但英文的2个字节中后一位为0,可以使用这种特性去实现按字节截取字符串,参考如下数组所示:
[-2,-1,0,49,0,57,0,57,0,54,94,116,-1,12,0,83,0,117,0,110,81,108,83,-8,83,-47,94,3,78,-122,0,74,0,97,0,118,0,97,118,-124,123,44,
78,0,78,42,95,0,83,-47,93,-27,81,119,83,5,-1,8,0,74,0,68,0,75,0,49,0,46,0,48,-1,9](源参数可见下列代码处的红色文本)
以上为jdk1.7.0_09输出的结果,这个byte[]开头处的-2,-1为编码的标记位,真是数据从下标2的位置开始的,如上0,49对应的是1;0,57对应的9;94,116对应的年,即我们可以得出下列结果的代码:
String text = new String(new byte[] {-2,-1,94,116} , "Unicode");
System.out.println(text);//输出结果为“年”
同时还可以看出所有的字母数字的两个字节中第一位为0,可以用于区分是否为中文字符的实现,故一个不太高大上的代码实现参考如下:
package com.chendd.examples;
import java.io.UnsupportedEncodingException;
public class Test {
public static void main(String[] args) throws Exception {
String text = "1996年,Sun公司发布了Java的第一个开发工具包(JDK1.0)";
System.err.println(text);
int length = text.getBytes("Unicode").length;
for (int i = 1; i <= length; i++) {
String value = substr(text , i);
System.out.println("按 " + i + " 个字节截取:" + value);
}
}
private static String substr(String text, int length) throws UnsupportedEncodingException {
byte bytes[] = text.getBytes("Unicode");
int total = bytes.length;
StringBuilder builder = new StringBuilder();
int num = 0;
for(int i = 2; i < total && num < length ; i++){
if(i + 1 > total){
break;
}
//英文(字母或数字)
if(bytes[i] == 0 && i % 2 == 0){
builder.append(new String(new byte[]{-2,-1,bytes[i],bytes[i+1]} , "Unicode"));
i++;
num = num + 1;
continue;
}
if(num + 2 > length){
break;
}
//中文
builder.append(new String(new byte[]{-2,-1,bytes[i],bytes[i+1]} , "Unicode"));
num = num + 2;
i++;
}
return builder.toString();
}
}
1996年,Sun公司发布了Java的第一个开发工具包(JDK1.0)
按 1 个字节截取:1
按 2 个字节截取:19
按 3 个字节截取:199
按 4 个字节截取:1996
按 5 个字节截取:1996
按 6 个字节截取:1996年
按 7 个字节截取:1996年
按 8 个字节截取:1996年,
按 9 个字节截取:1996年,S
按 10 个字节截取:1996年,Su
按 11 个字节截取:1996年,Sun
按 12 个字节截取:1996年,Sun
按 13 个字节截取:1996年,Sun公
按 14 个字节截取:1996年,Sun公
按 15 个字节截取:1996年,Sun公司
按 16 个字节截取:1996年,Sun公司
按 17 个字节截取:1996年,Sun公司发
按 18 个字节截取:1996年,Sun公司发
按 19 个字节截取:1996年,Sun公司发布
按 20 个字节截取:1996年,Sun公司发布
按 21 个字节截取:1996年,Sun公司发布了
按 22 个字节截取:1996年,Sun公司发布了J
按 23 个字节截取:1996年,Sun公司发布了Ja
按 24 个字节截取:1996年,Sun公司发布了Jav
按 25 个字节截取:1996年,Sun公司发布了Java
按 26 个字节截取:1996年,Sun公司发布了Java
按 27 个字节截取:1996年,Sun公司发布了Java的
按 28 个字节截取:1996年,Sun公司发布了Java的
按 29 个字节截取:1996年,Sun公司发布了Java的第
按 30 个字节截取:1996年,Sun公司发布了Java的第
按 31 个字节截取:1996年,Sun公司发布了Java的第一
按 32 个字节截取:1996年,Sun公司发布了Java的第一
按 33 个字节截取:1996年,Sun公司发布了Java的第一个
按 34 个字节截取:1996年,Sun公司发布了Java的第一个
按 35 个字节截取:1996年,Sun公司发布了Java的第一个开
按 36 个字节截取:1996年,Sun公司发布了Java的第一个开
按 37 个字节截取:1996年,Sun公司发布了Java的第一个开发
按 38 个字节截取:1996年,Sun公司发布了Java的第一个开发
按 39 个字节截取:1996年,Sun公司发布了Java的第一个开发工
按 40 个字节截取:1996年,Sun公司发布了Java的第一个开发工
按 41 个字节截取:1996年,Sun公司发布了Java的第一个开发工具
按 42 个字节截取:1996年,Sun公司发布了Java的第一个开发工具
按 43 个字节截取:1996年,Sun公司发布了Java的第一个开发工具包
按 44 个字节截取:1996年,Sun公司发布了Java的第一个开发工具包
按 45 个字节截取:1996年,Sun公司发布了Java的第一个开发工具包(
按 46 个字节截取:1996年,Sun公司发布了Java的第一个开发工具包(J
按 47 个字节截取:1996年,Sun公司发布了Java的第一个开发工具包(JD
按 48 个字节截取:1996年,Sun公司发布了Java的第一个开发工具包(JDK
按 49 个字节截取:1996年,Sun公司发布了Java的第一个开发工具包(JDK1
按 50 个字节截取:1996年,Sun公司发布了Java的第一个开发工具包(JDK1.
按 51 个字节截取:1996年,Sun公司发布了Java的第一个开发工具包(JDK1.0
按 52 个字节截取:1996年,Sun公司发布了Java的第一个开发工具包(JDK1.0
按 53 个字节截取:1996年,Sun公司发布了Java的第一个开发工具包(JDK1.0)
......
按 72 个字节截取:1996年,Sun公司发布了Java的第一个开发工具包(JDK1.0)
特别注意:白天在使用别的的编码环境编写时遇到的一个很奇怪的情况,获取到的上述字符串的Unicode编码为[-1,-2...]开头,另外英文与数字的两位字节中,后一位为0,与本示例中的代码正好相反,目前还不清楚为何,待后续摸索清除再进行更新,如果遇到上述情况时上述的代码需要小小的调整一下,如将if(bytes[i] == 0 && i % 2 == 0)这里的判定逻辑修改为if(bytes[i+1] == 0 && (i+1) % 2 != 0),同时将两个new String(new byte[]{-2,-1处的-2,-1调换位置即可。
package cn.chendd.examples;
import java.util.Arrays;
public class TestFile {
public static void main(String[] args) throws Exception {
//自行测试
String text = "1996年,Sun公式发布了Java的第一个开发工具包(JDK1.0)";
System.out.println(subByte(text.getBytes("unicode") , 3 , 11));
}
/**
*
* <pre>
*
* </pre>
* @param bytes Unicode字节数组
* @param start 要截取的起始位置
* @param end 要截取的结束位置
* @return 字符串
* @throws Exception 异常处理
*/
public static String subByte(byte[] bytes, int start, int end)
throws Exception {
//判断Unicode的编码是否以-2开头
if (bytes[0] == -2) {
return subByte(bytes, start, end , false);
}
return subByte(bytes, start, end , true);
}
private static String subByte(byte[] bytes, int start, int end , boolean flag)
throws Exception {
int lens = bytes.length;
byte newValue[] = Arrays.copyOfRange(bytes, 2, lens);
int count = 0;
StringBuilder builder = new StringBuilder();
for (int i = 0; i < newValue.length / 2; i++) {
byte b1 = newValue[i * 2];// 判断是否为中文
byte b2 = newValue[i * 2 + 1];// 判断是否为中文
if (count >= start) {
builder.append(new String(new byte[] { bytes[0], bytes[1], b1,
b2 }, "unicode"));
}
if(flag){
count = b2 == 0 ? count + 1 : count + 2;
} else {
count = b1 == 0 ? count + 1 : count + 2;
}
if (count >= end + start) {
break;
}
}
return builder.toString();
}
}
特别注意:
1)、这个示例实现了根据指定位置按字节长度截取字符的实现,根据指定起始位置和待截取字节的长度实现,值得拥有;
2)、上文所说的Unicode编码以[-1,-2]开头的情况也终于复现了,由于当时使用的IBM的一版本jre导致,等于说标准JDK的Unicode都是[-2,-1],所以上述代码也为此专门做了兼容,具体见代码;
点赞