在Java中大家都用过String对象的substring方法,用来求一个子字符串。但是在我开发的项目中遇到一个问题,需要将字符串按照字节来切割以及需要计算字符串的字节数。不知大家有没有优雅点的解决办法。
我用getbytes,效率太低。
我需要的是一个像substring的方法,但参数是字节数。
比如:(注意:“您”站两个字节)
String str="a您好b";
StringUtil.substring(str,0,1);//可以得到从字节0开始的1个字节“a”
StringUtil.substring(str,1,3);//可以得到从字节1开始的2个字节“您”而不是str.substring(0,3)的“您好”
StringUtil.substring(str,0,3);//可以得到从字节1开始的2个字节“a您”而不是str.substring(0,3)的“a您好”
因此:StringUtil.substring(a,b,c)的三个参数为:
a:要切割的字符串(如果效率考虑,此处也可以是char[],请您自己决定)
b:开始字节数
c:结束字节数(和java一样,不包含此下标-->请详见java docs)另,还需要StringUtil.size(str)来得到str的字节数。

解决方案 »

  1.   

    那么StringUtil.substring(str,1,2);//就会出现乱码吧?
      

  2.   


    public class StringUtil {
    public static String substring(String str, int index, int count) {
    if(str==null || str.length()==0 || str.length()<index){
    return "";
    }
    byte[] temp = str.substring(index).getBytes();
    if(count>=temp.length){
    return str;
    }
    byte[] byteArray = new byte[count];
    int ii = 0;
    for (int i = 0; i < count; i++) {
    byteArray[i] = temp[i]; if (temp[i] < 0) {
    ii++;
    }
    }
    if (ii % 2 == 1) {
    byteArray[count - 1] = ' ';
    }
    return new String(byteArray).trim();
    }

    public static void main(String []args){
    String s = "a你好b";
    System.out.println(substring(s,0,3));
    }
    }
      

  3.   

    size就更简单了。
    public static int size(String str){
    return str.getBytes().length;
    }
      

  4.   

    感觉还是用char[]方便,不要用byte[]
    长度计算可以用ascii码,一般超出128就是双字节计算size,就一个一个数过去
    substring也一样String有一点很不好,charAt每次都会去判断是否越界
    转成char[]又要耗时间,耗空间,所以是否转成char[]计算长度,楼主自己衡量,反正是开源的
      

  5.   


    public static String substring(String str, int tocount,String more) 

    int reint = 0; 
    String restr = ""; 
    if (str == null) 
    return ""; 
    char[] tempchar = str.toCharArray(); 
    for (int i= 0; (i< tempchar.length && tocount > reint);i++) { 
    String s1 = str.valueOf(tempchar[i]); 
    byte[] b = s1.getBytes(); 
    reint += b.length; 
    restr += tempchar[i]; 

    if (tocount == reint || (tocount == reint - 1)) 
    restr += more; 
    return restr; 

      

  6.   

    遇到过你说的问题,个人认为,这算一个小Bug.
    substring应该按字符而不是字节来处理字符串.
      

  7.   

    我是楼主,先感谢大家的回答。
    我在做银行内部和AS400前置服务器交互的一个框架,这个框架属于业务层。
    此框架主要是和一个C做的服务器交互,这个服务器的服务是用Socket进行报文应答,报文都是ascii,连数值型比如100都要转成"100"来收发。
    其他都好转,但字符在Java中是char类型——两个字节,只能用byte,我本想用byte[]自己封装一个字符串也就OK,但实际问题比较复杂:
    从Web页面发给我的都是XML,我需要将XML解析成POJO,其他开发人员处理业务时需要此POJO,所以我的框架要调用开发人员的业务方法并传POJO过去,之后需要将此POJO生成Socket报文传给服务器,服务器产生的响应Socket报文我还要再解析成响应POJO,再调用事后业务处理,最终产生XML响应给Web层……
    不知道说清楚没。
    也就是说,我拿到Web层的数据是String,但最终要byte流一样的socket报文。
    POJO中有大量的String,如果每一个String都每一个都转换成byte[],效率低并且占空间,我目前解决方案就是这样丑陋,是不是不需要转也可以呢?
    这才是POJO转socket,反过来时还要更麻烦,把byte[]转成POJO……
    嗨,开发就是这么有挑战性,折磨人……
      

  8.   

    fosjos正解,字符型没必要用bytes,toCharArray转化成char[]即可
      

  9.   

    楼主可能对字符编码比较陌生。
    楼主说的那个例子,是建立在GBK或者GB2312编码的情况下的。
    Java内部的字符编码统一使用的是Unicode,所以,一个字符用两个字节来表示。通过9楼的描述,感觉楼主在编一个类似综合业务代理网关的东西。
    (主要功能是鉴权、信息转发和协议转换)首先,楼主要明确一点,Socket方式收发的内容,有没有汉字?
      如果有,那楼主所说的“报文都是ascii”就不确切了,因为ASCII编码中没有汉字字符的存在。
    其次,如果Socket方式收发的内容不存在汉字,
      那么,String.subString(index,offset)方法,与转换成byte数组再处理的结果相同。
    第三,如果Socket方式收发的内容存在汉字,POJO对象的处理方式沿用Java对字符处理的方式就可以了。
      没必要非要精确到字节,只需这对字符编程即可。
      这中情况下,只在向Socket收发信息时,进行POJO与byte[]的转换即可。
      提示两个方法:
        1.String recieve = new String(byteArray,"GB2312");
        2.byte [] msgBeSent = msg.getBytes("GB2312");
    第四,楼主可以把整个信息的流向,过程整理一下。有个清晰地处理步骤。
      这样,设计的时候关注整体,开发的时候关注局部,就可以解决问题了。
      

  10.   

    实现serializable接口直接writeObject, readObject就可以了
      

  11.   

    感谢12楼朋友的帮忙,对其说法我回复如下:原文我会用[]括住。[12楼原文:楼主可能对字符编码比较陌生。 
    楼主说的那个例子,是建立在GBK或者GB2312编码的情况下的。 
    Java内部的字符编码统一使用的是Unicode,所以,一个字符用两个字节来表示。 ]答:也许比12楼侯磊兄陌生,但这里更多的不是编码类型转换问题,如果只是编码类型转换encode或decode就能解决了。我主要关注效率。[12楼原文:通过9楼的描述,感觉楼主在编一个类似综合业务代理网关的东西。 
    (主要功能是鉴权、信息转发和协议转换) ]答:类似,但主要是协议转换[12楼原文:首先,楼主要明确一点,Socket方式收发的内容,有没有汉字? 
      如果有,那楼主所说的“报文都是ascii”就不确切了,因为ASCII编码中没有汉字字符的存在。 ]答:有汉字,用的是GB2312,GB2312的汉字表示方法是2Byte扩展ASCII编码表示一个GB2312的汉字(说明一下:ASCII用7bit表示,即二进制00000000~01111111,十进制0~127共128个字符,从二进制10000000~11111111,十进制128~255为扩展ASCII编码,GB2312就是这样用ASCII表示汉字的,不知道是否能说清楚)[12楼原文:其次,如果Socket方式收发的内容不存在汉字, 
      那么,String.subString(index,offset)方法,与转换成byte数组再处理的结果相同。 ]答:绝对同意[12楼原文:第三,如果Socket方式收发的内容存在汉字,POJO对象的处理方式沿用Java对字符处理的方式就可以了。 
      没必要非要精确到字节,只需这对字符编程即可。]答:在9楼我曾说过,
    [web生成xml->我的模块
    我的模块生成POJO
    我的模块调用其他程序员编写的业务并传入POJO处理
    我的模块生成将POJO转为socket报文
    最后发送报文]
    这个过程中,xml和POJO一定是用String不用转换的,而POJO到socket报文就一定需要转换了。
    [12楼原文:这中情况下,只在向Socket收发信息时,进行POJO与byte[]的转换即可。 
      提示两个方法: 
        1.String recieve = new String(byteArray,"GB2312"); 
        2.byte [] msgBeSent = msg.getBytes("GB2312"); 
    ]答:不是编码转换问题。[12楼原文:第四,楼主可以把整个信息的流向,过程整理一下。有个清晰地处理步骤。 
      这样,设计的时候关注整体,开发的时候关注局部,就可以解决问题了。]答:流向很清楚,我再说一遍:
    数据上行:
    [web生成xml->我的模块
    我的模块生成POJO
    我的模块调用其他程序员编写的业务并传入POJO处理
    我的模块生成将POJO转为socket报文--此步骤最需要改造(String转为byte[])
    最后发送报文]数据下行:
    [得到AS400的socket报文
    解析报文生成POJO对象--此步骤最需要改造(byte[]转为String)
    我的模块调用其他程序员编写的业务并传入POJO处理
    根据POJO生成XML返回给Web
    ]
      

  12.   

    楼主说的那个substring方法,用在上述步骤中的那个环节?
    我认为,貌似,没有哪个环节适合用这样的方法。
      

  13.   


    比如需要向服务器发送一个消息,并且此时消息包括有中英文混合。
    此时:
    第一、需要我统计出有多少个字符
    第二、服务器会将此字符串当成ASCII来解读。
    但是如果这个字符串太长,我就需要手动来拆分字符串,将其分为两个报文。
    例如服务器一个报文体最长100个“字符”则只能有50个汉字。我要发送的字符串假设是80个“汉字,就需要拆分成两条,此时拆分并不复杂。
    但如果是中英文混合就需要上面的需求了。
      

  14.   

    饿。。手边没java环境。。
    楼主这种精神,很崇拜。不像很多人,扔了个问题就走
      

  15.   

      楼主解决了吗?要是解决了,给大家分享下嘛!我不太明白lz项目要中socket报文,这个是不是和计算机网络中的ip数据报分组差不多啊。要是这样的话,lz可以直接求得要传送给socket的报文有多长(用字节算),然后和服务器端的socket支持的最大报文的长度(同样去字节长度)比较,如果大于服务器端支持的socket的最大报文,那么就分组,算出每个组的长度,但是应该判断下下每个分组的最后一个字节是否取到半个汉字,这里做个判断,然后做相应的处理,应该就可以了吧。呵呵
      

  16.   

    这样就不能再网络中传输了,socket传的是字节型的数据
      

  17.   

    还是不太明白,网络传输学的不太好。到底java网络传输中是传字节型数据还是字符型数据,lz自己都搞糊涂了吧!
      

  18.   

    以下是“preferme”的回复,结贴喽!!!
      

  19.   

    引用[http://topic.csdn.net/u/20100109/18/b6a979c7-707d-4185-b269-bd87683be501.html#replyachor]
      

  20.   

    Java codepackage houlei.java.util;import java.io.UnsupportedEncodingException;/**
     * 这个类的功能类似于String对象的包装器类,由于String类不能被继承,
     * 所以,也只是根据现有状况进行了一部分关于编码后的字节功能方面的扩展。<p>
     * 在使用该类的对象时,尽量一个String对象,只创建一次对应的ByteString对象,因为构造器要进行转码操作。<br/>
     * 该对象的其他方法可以多次调用也没关系,因为转码确实比较耗费CPU时间。
     * @author HouLei
     */
    public class ByteString {
        public static final String DefaultCharsertName = "GBK";
        private String value;//该参数用于本类功能的扩展,目前而言,可以除去。
        private byte [] buff;
        
        public ByteString(String value,String charsetName) throws UnsupportedEncodingException {
            this.value = value;
            buff = value.getBytes(charsetName);
        }    public ByteString(String value) throws UnsupportedEncodingException {
            this(value,DefaultCharsertName);
        }
        
        /**
         * 获得字符串被转码以后所占的字节数。
         * @return 所占字节数。
         */
        public int size(){
            return buff.length;
        }
        
        /**
         * 将转码以后的字节数组进行切割,并返回切割后的字符数组。<br/>
         * 该方法并不保证切割后的字节数组,仍然是完整的字符串。
         * @param beginIndex 字节数组的开始位置。
         * @param endIndex 字节数组的结束位置。
         * @return 切割后的结果。
         */
        public byte [] subBytes(int beginIndex,int endIndex){
            if (beginIndex < 0) {
                throw new StringIndexOutOfBoundsException(beginIndex);
            }
            if (endIndex > buff.length) {
                throw new StringIndexOutOfBoundsException(endIndex);
            }
            if (beginIndex > endIndex) {
                throw new StringIndexOutOfBoundsException(endIndex - beginIndex);
            }
            byte [] newBuff = new byte [endIndex - beginIndex];
            System.arraycopy(buff, beginIndex, newBuff, 0, newBuff.length);
            return newBuff;
        }
        
        /**
         * 这个方法是示例代码,应该删除。
         */
        public static void main(String args []) throws UnsupportedEncodingException{
            String str="a您好b"; 
            ByteString bs = new ByteString(str);//只要保持bs对象,就可以多次调用它的一些方法,这样效率就高了。
            byte [] b1 = bs.subBytes(0, 1);
            byte [] b2 = bs.subBytes(1, 3);
            byte [] b3 = bs.subBytes(0, 3);
            System.out.println(new String(b1,"GBK"));//可以得到从字节0开始的1个字节“a” 
            System.out.println(new String(b2,"GBK"));//可以得到从字节1开始的2个字节“您”而不是str.substring(0,3)的“您好” 
            System.out.println(new String(b3,"GBK"));//可以得到从字节1开始的2个字节“a您”而不是str.substring(0,3)的“a您好”
        }
    }