• 一个截取字符串函数引发的思考



    背景


    前些天,遇到这样一个问题,问题的内容例如以下:

    要求编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串。可是要保证汉字不被截半个,如“我ABC”, 4,截取后的效果应该为“我AB”,输入“我ABC汉DEF”, 6,应该输出为“我ABC”,而不是“我ABC+汉的半个”。


    问题


    刚看到这个问题的时候,以为还是非常easy的,但写出来之后,发现并非想要的效果。回忆一下当时的思路,就发现刚開始思考的时候,就没有考虑好,并且把汉字会导致的问题给忽略了,就这样没有一个完整的思路,就直接開始写代码了。大家可想而知,写出来的效果当然不会是你想要的了。

    既然题目中已经要求了汉字存在时应该出现什么样的结果,那么我们就应该考虑,怎样把英文和中文都统一成一种编码,而不是对它们进行单独的处理。设想一下,假设程序中对它们进行单独的处理,那么会出现什么样的问题呢?这个稍后再说。


    实现


    遇到这些问题之后,总得想办法解决吧,于是在网上找到了一种解决方式,主旨是,统一中英文的编码方式,即在GBK的编码方式下,把要截取的字符串转换为字节数组,此时,一个汉字就会转换为两个负数,比如,“我”就会转换为“-50, -46”。然后再针对截取到的负数的个数进行处理,就能够把汉字的问题攻克了。

    截取函数代码


    <span style="font-family:Microsoft YaHei;font-size:12px;">/**
     * 截取要点:
     * 1. 首先将字符串转换成字节数组,再将字节数组的每一个元素拿出来,推断有无负数(一个汉字为两个负数),就可以知道有没有汉字
     * 2. len:是将字符串转换成字节数组后,要截取的长度,如“我ABC你”的字节数组长度为7(-50,-46,65,66,67,-60,-29)
     * 		,而截取的是6(-50,-46,65,66,67,-60)
     * 3. 对截取的字节数组(-50,-46,65,66,67,-60),统计负数的个数
     * 4. String里的 substring 方法将双字节的汉字当成一个字节的字符(UCS2字符)
     * 
     * @param str
     * @param len
     * @return
     */
    public static String subString(String str, int len) {
    	// 推断字符串是否为空
    	if (str == null && "".equals(str)) {
    		return null;
    	}
    	
    	// 将字符串中的char数组转换成指定编码方式的byte数组的函数
    	byte[] strBytes = null;
    	
    	try {
    		strBytes = str.getBytes("GBK");
    	} catch (UnsupportedEncodingException e) {
    		e.printStackTrace();
    	}
    	
    	// 得到字符串的长度
    	int strLen = strBytes.length;
    	// 推断截取字符串的长度是否在推断的范围内,否则返回原串
    	if (len >= strLen || len < 1) {
    		return str;
    	}
    	
    	// 打印出字符串长度和截取的长度
    	System.out.println("strBytes.length = " + strBytes.length);
    	System.out.println("len = " + len);
    	
    	int count = 0;
    	for (int i = 0; i < len; i++) {
    		// 将每一个字节数组转换为整型数,以为后面依据值的正负来推断是否为汉字
    		int value = strBytes[i];
    		// System.out.println("strBytes[" + i + "] = " + strBytes[i] + ",");
    		System.out.println(value + ",");	// 我ABC你 -50,-46,65,66,67,-60,-29
    		
    		// 对于第一种情况:
    		// 注,一个函数转换成整型书就为两个负整数,上面的“我ABC你”,转换成整型数就为 -50,-46,65,66,67,-60,-29
    		// 可是 len=6,所以截取下来就是 -50,-46,65,66,67,-60,count就为3
    		// 假设是汉字(负),则统计截取字符串中的汉字所占字节数
    		if (value < 0) {
    			count++;
    		}
    		
    		System.out.println("汉字字节码的数量为 " + count);
    	}
    	
    	// 依据推断给定的字符串是否含有汉字,利用String类的substring()方法来截取不同的长度
    	// 依据所统计的字节数,推断截取到字符是否为半个汉字,奇数为半个汉字
    	if (count % 2 != 0) {
    		// 假设在截取长度为1时,则将该汉字取出,其它情况则不截取(截取字节长度数 - 截取汉字字节数/2 - 截取到的半个汉字的字节数)
    		len = (len == 1) ? len : len - count / 2 - 1;	// len=6-3/2-1=4 我ABC
    		// System.out.println("count / 2 = " + count / 2);
    		System.out.println("处理后的len = " + len);
    	} else {
    		// 截取字符长度为字节长度 - 汉字所占字节长度 / 2 (汉字占两个字节)
    		len = len - (count / 2);
    	}
    	
    	return str.substring(0, len);
    }</span>

    測试代码


    <span style="font-family:Microsoft YaHei;font-size:12px;">/**
     * @param args
     */
    public static void main(String[] args) {
    	// 情况一
    	System.out.println("------------ 情况一  ------------");
    	String inStr = "我ABC你";
    	String str = subString(inStr, 6);
    	System.out.println(str);	// 我ABC
    	
    	// 情况二
    	System.out.println("------------ 情况二  ------------");
    	inStr = "我ABC汉DEF";
    	str = subString(inStr, 1);
    	System.out.println(str);	// 我
    	
    	// 情况三
    	System.out.println("------------ 情况三  ------------");
    	inStr = "我AB爱孩子CDEF";
    	str = subString(inStr, 9);
    	System.out.println(str);	// 我AB爱孩
    	
    	// 情况四
    	System.out.println("------------ 情况四  ------------");
    	inStr = "ABCDEF";
    	str = subString(inStr, 4);
    	System.out.println(str); 	// ABCD
    }</span>


    測试结果








    思考


    通过这个题目,给了我非常多的启发,首先,在“下笔”之时,就应该考虑到灵活性的问题,尽管这里仅仅是中英文的字符串截取,可是假设再添加一种其它语言呢?扩展性就没有那么好了,这也是上边所提到的问题。好的设计是一方面,代码的实现上,也要做到解耦合。同一时候,这也提醒了我,在考虑问题的时候,首先不能一头扎到细节中,而是先要有一个全局观,对总体有个全面的了解,将详细的问题抽象化、简单化,层层剖析;做完这一点之后,才是对详细的问题详细分析。

  • 相关阅读:
    boot文件上传的坑
    页面前端获取时间和数据库获取时间差八个小时 CST
    springcloud 学习
    springcloud 的eureka服务
    ROWNUM()应用案例-实现一个拉链表
    python dict 常用操作
    【转】团队项目的Git分支管理规范
    Python中使用cx_Oracle调用Oracle存储过程
    【转载】ORACLE 物化视图
    C# 加密解密
  • 原文地址:https://www.cnblogs.com/lcchuguo/p/4484966.html
Copyright © 2020-2023  润新知