char
arrays and in the
String
and
StringBuffer
classes
System.out.println(Integer.toBinaryString(c));
执行结果–
100111101100000
4f60
从结果里我们确定两件事
1.char里面存的是比特流数据(废话,计算机里存的都是01)
2.这个值对应的是一个unicode码值(UTF-16大端编码方式,因为UTF-16和unicode在BMP1里是一一对应的,所以可以理解成UTF-16就是UNICODE. 假设char完整的表达unicode中定义的所有字符,那就需要4位--其实就是存储码值,比如 char c=’\uFFFFFF’)
中文unicode码表: http://www.chi2ko.com/tool/CJK.htm
3.new String(byte[] ,String charsetName)在干啥?
根据方法注解:
Constructs a new {@code String} by decoding the specified array of bytes using the specified{@linkplain java.nio.charset.Charset charset}.The length of the new {@code String} is a function of the charset,and hence may not be equal to the length of the byte array.
字面意思是:对指定的字节数组,通过指定的字符集,构建一个新的String. String的长度取决与字符集,所以最终String的长度和字节数组长度很有可能不一致。
我们看String的构成,其实就是一个char[] value,所以通过字符集的解码,最终是将byte[]转换成char[],且这个char是UTF-16编码的。因此我们猜测,java中StringCoding.decode方法干的事就是:
1.根据charsetName对byte[]进行解码,得到一个unicode码值
2.对这个码值进行UTF-16编码,并将值填写到对应char类型中
我们通过StringCoding.decode源代码进行分析,看是否和我们猜测的一样
拿个简单点的,UTF-8解码器为例
看第8行:
int i3 = m << 18 ^ n << 12 ^ i1 << 6 ^ i2 ^ 0x381F80;
我们输入一个[女孩]的图标
String s = “[女孩]”;//—是一个emjoy图标,搜狐博客显示不出来,是一个小女孩的头像
byte[] bts = s.getBytes(“utf-8”);
然后计算 int code = bts[0] << 18 ^ bts[1] << 12 ^ bts[2] << 6 ^ bts[3] ^ 0x381F80;
得到的结果是:1f467
查询unicode表(http://www.unicode.org/Public/UNIDATA/NamesList.txt),描述如下:
1F467 GIRL
因此证实了初步猜想,(1.根据charsetName对byte[]进行解码,得到一个unicode码值)
再看后面2行代码:
paramArrayOfChar[(j++)] = Character.highSurrogate(i3);
paramArrayOfChar[(j++)] = Character.lowSurrogate(i3);
这2行代码是UTF-16对于扩展字符的处理方式,参考
http://blog.chinaunix.net/uid-20246210-id-1970535.html
“字符的值介于0x10000和0x10FFFF之间的,用一个值介于0xD800和0xDBFF(在
所谓的高8位区)的16位整数和值介于0xDC00和0xDFFF(在所谓的低8位区)的16位整
数来表示。“
说白了,就是当2个字符难以描述某个unicode的值的时候(超出16bit了),就用2个字符(4字节)来描述这个unicode值(这个是UTF-16的编码规则,java使用大端UTF-16,就是高位是左边的值)。然而这个用来拼接的值又必须和正常使用的值区分开来,因为utf-16是直接描述unicode,如果范围不区分,就难以辨别到底描述的是双字节的unicode,还是需要拼接的。所以utf-16编码格式,将0xD800-0xDBFF,0xDC00-0xDFFF留白,专门用于特殊编码。
这里证实了我们第2个猜测,拿到unicode值之后,进行了UTF-16的编码
到这里,我们终于可以明确的知道,String中的char[],用的是utf-16的大端编码后的值。
因此对于这样的情况 String s = new String(byte[],charsetName)
他表达的含义就是:将byte[]通过charsetName进行解码,得到对应的unicode值,然后重新编码成UTF-16的值。
那另外一个问题来了,重新编码成UTF-16,不是应该是byte[]么,怎么变成char[]了 ?
解答是,char就是两字节的UTF-16编码的byte[]数组。
然后我们讨论一个问题:
web编程经常遇到的,客户端浏览器GBK编码的,java-server是UTF-8的编码。我们调试的时候会发现,服务端是乱码,因为GBK格式的byte[]使用UTF-8编码方式进行解码,的确找不到对应的unicode.那么假设我们进行如下操作:
String s = new String(param.getBytes(“utf-8″),”GBK”);
能获取到正确的string么?
结论是:中文基本上不能正确解释。因为GBK的编码规则是:中文用2个字节表示,第一个字节>127. 这个编码规则和UTF-8的风马牛不相及。而当UTF-8拿到GBK的byte[]时,按照UTF-8规范进行解析,发现不符合规则,那么就替换成一个固定的字符(方块),到这里,原来的byte信息就丢失了。因此虽然param.getBytes(“UTF-8”)这样重新编码,但是已经不能恢复成原来的byte[]了。
扯了一大堆,回到最初的问题,怎么截包含emjoi表情的字符串呢?
先上java的方法
1.最简单的
private static String subByUtf32(String s, int reqNum) throws UnsupportedEncodingException {
if (StringUtil.isBlank(s)) {
return s;
}
byte[] bts = s.getBytes(“utf-32”);
int index = reqNum * 4;
index = index > bts.length ? bts.length : index;
return new String(bts, 0, index, “utf-32”);
}
2.使用codepoint
private static String subByCodePoint(String s,int reqNum){
if (StringUtil.isBlank(s) || s.length()<=reqNum) {
return s;
}
for (int index = reqNum; index <= s.length(); index++) {
if (s.codePointCount(0, index) > reqNum) {
return s.substring(0, index – 1);
}
}
return s;
}
3.其他解析编码规则的.
IOS版本
1.使用UTF-32L
-(NSString*)subByUtf32:(NSString*)str reqNum:(NSInteger)reqNum{
//长度不够的情况
if ([str length]<=reqNum) {
return str;
}
//先转成byte
NSData* data =[str dataUsingEncoding:NSUTF32LittleEndianStringEncoding];//这里的编码需要指明用大端还是小端,如果只用UTF-32,前面4字节会用来标记编码格式
Byte* bytes = (Byte*)[data bytes];
int index = reqNum*4;
index = index > data.length ? data.length : index;
return [[NSString alloc]initWithBytes:bytes length:index encoding:NSUTF32LittleEndianStringEncoding];
}
2.使用UTF-8
-(NSString*)subByUtf8:(NSString*) str reqNum:(NSInteger)reqNum{
//长度不够的情况
if ([str length]<=reqNum) {
return str;
}
//先转成byte
NSData* data =[str dataUsingEncoding:NSUTF8StringEncoding];
Byte* bytes = (Byte*)[data bytes];
int index = 0;
for (int i =0; i<reqNum; i++) {
if (index>=[data length]) {
//异常了
break;
}
int byte =bytes[index];
if (byte<128) {
//0xxxxxxx
//单字节
index+=1;
}else if(byte<224){
//110xxxxx
//双字节
index+=2;
}else if(byte<240){
//1110xxxx
//3字节
index+=3;
}else if(byte<248){
//11110xxx
//4字节
index+=4;
}else if(byte<252){
//111110xx
//5字节
index+=5;
}else{
//1111110x
//6字节
index+=6;
}
}
NSString* newStr = [[NSString alloc]initWithBytes:bytes length:index encoding:NSUTF8StringEncoding];
return newStr;
}
今天的文章表情包java_java截取最后一个字符「建议收藏」分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/85643.html