表情包java_java截取最后一个字符「建议收藏」

表情包java_java截取最后一个字符「建议收藏」在项目用有用到带emjoi表情符的用户昵称,在某些场景下,需要对昵称做截取

在项目用有用到带emjoi表情符的用户昵称,在某些场景下,需要对昵称做截取。如果按照String.length来截的话,会遇到乱码的情况。
看string源码,length取的是char[] value的长度,根据这个截,获取到的是N个完整的char.
但是实际情况,却遇到了乱码,到底是咋回事呢 ?
由此,我先需要弄明白,

1.char到底是啥玩意?
java官方文档定义char
http://tool.oschina.net/apidocs/apidoc?api=jdk_7u4
其中有句
Character information is based on the Unicode Standard, version 6.0.0.
说的是char里面存的信息,是用USC编码的(USC是早期的UTF-16编码,在UTF-16出现扩展字符前,是一样的。也就是为啥不能定义char c=’表情符号’ 这样的情况了)

那么问题来了,UTF-16是变长的编码方式,从2字节到4字节不等。而char的长度是2,意味着char不能完整的表达unicode字符集中所有的字符。
也就意味着,要么java不支持某些字符,比如emjoy表情,要么char并不能如字面含义那样,一个char表示一个字符(对应unicode码表上的字符),可能多个char合伙表达一个字符(emjoy表情就占2个char–4字节)

依旧看javaDoc,然后下方关于string的定义中有这么一句:
The Java platform uses the UTF-16 representation in 
char
 arrays and in the 
String
 and
StringBuffer
 classes
说的意思是 java使用UTF-16来表示在string或则stringbuffer中的char数组。
为啥在String中的char[]则需要用UTF-16编码呢?个人觉得因为String需要支持各种非2字节的符号的显示,如果使用USC编码,这部分数据是无法保存的。这里刚好解答了上面的疑问。所以说,在string中的char,并非一个完整的字符。

2.char里面存的啥?
从问题1的解答里,我们知道char其实是2个字节的内存,而且是utf-16的编码格式。 那么我们要确定下,这2个字节内存里存的是什么东西 ?
char c=’你’;

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解码器为例

表情包java_java截取最后一个字符「建议收藏」看第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

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注