去空格失效

5/17/2022 Java

心灵鸡汤

当你还是一只猫的时候,记着你的目标要成为一只虎。当你成为一只虎的时候,别忘了你曾经是一只猫。心态要高,姿态要低。不要看轻别人,更不要高估自己

# 一:场景

首先看下面这段代码,以及先猜想一下运行结果。

public class Test {

    public static void main(String[] args) {
        String string = "广州xxxx有限公司  ";
        String print = "【%s】 \t length:%d \n";
        System.out.printf(print, string.trim(), string.length());
        System.out.printf(print, string, string.length());
    }

}
1
2
3
4
5
6
7
8
9
10

运行结果:

# 二:char

一个 char 保存一个Unicode字符,无论中英文。可以使用 \u + 4位十六进制数 来表示一个Unicode字符。例如,A 的ASCII,十进制为 65,十六进制为 41

public static void main(String[] args) {
    char c1 = 'A';
    int c1Int = c1; // 把char类型的值转换为十进制整型
    System.out.println("A 的十进制:" + c1Int);
    char c2 = '\u0041'; //  \\u 加 4位十六进制
    System.out.println("\\u0041表示:" + c2);
}
1
2
3
4
5
6
7

运行结果:

# 三:问题定位

首先把之前那个String,按照char打印

public static void main(String[] args) {
    String string = "广州xxxx有限公司  ";
    for (char c : string.toCharArray()) {
    	System.out.printf("【%s】十进制为:%d\n", c, (int)c);
    }
}
1
2
3
4
5
6

运行结果:

这时候可以去查 ASCII 表,就会惊讶的发现,空格 在ASCII表的十进制为32,十六进制为20,而上面的运行结果,最后两行的空格,十进制为12288,那难道不是空格?

首先十进制12288,转成十六进制为3000。之后可以通过在线工具,例如 52unicode (opens new window) 找到代表的字符,可以发现确实为空格。只不过是全角空格。

科普

  • \u00A0:不间断空格,主要用在office中,让一个单词在结尾处不会换行显示,快捷键ctrl+shift+space ;
  • \u0020:半角空格(英文符号);
  • \u3000:全角空格(中文符号)。

那就剩下最后一个问题,String#trim 方法为什么失效?

package java.lang;

public final class String implements java.io.Serializable, Comparable<String>, CharSequence {

    /**
     * 返回一个字符串,其值为该字符串,删除了前缀和后缀的空格。
     *
     * 如果此 String 对象表示一个空字符,或者此 String 的第一个和最后一个字符的 unicode 都大于 '\u0020'(空格字符),则返回对该 String 对象的引用。
     * 
     * 否则,如果字符串中没有代码大于 '\u0020' 的字符,则返回空字符串。
     *
     * 设 k 为字符串中编码大于 '\u0020' 的第一个字符的索引,设 m 为字符串中编码大于 '\u0020' 的最后一个字符的索引。
     * 返回一个 String 对象,表示这个字符串的子字符串,它以索引 k 处的字符开始,以索引 m 处的字符结束——也就是这个的结果。
     * substring(k, m + 1)
     * 
     * @return  一个字符串,去掉前后缀空格
     */
    public String trim() {
        int len = value.length;
        int st = 0;
        char[] val = value;    /* avoid getfield opcode */

        while ((st < len) && (val[st] <= ' ')) {
            st++;
        }
        while ((st < len) && (val[len - 1] <= ' ')) {
            len--;
        }
        return ((st > 0) || (len < value.length)) ? substring(st, len) : this;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

不难发现,trim() 只能去除英文的空格。

# 四:解决方案

# 4.1 重写 trim

新 trim 工具类如下:

import org.apache.commons.lang3.StringUtils;

/**
 * 去除字符串前后各种编码类型的空格
 */
public class StringUtil {

    /**
     * 普通的英文半角空格Unicode编码
     */
    private static final int SPACE_32 = 32;

    /**
     * 中文全角空格Unicode编码(一个中文宽度)
     */
    private static final int SPACE_12288 = 12288;

    /**
     * 普通的英文半角空格但不换行Unicode编码(== &nbsp; == &#xA0; == no-break space)
     */
    private static final int SPACE_160 = 160;

    /**
     * 半个中文宽度(== &ensp; == en空格)
     */
    private static final int SPACE_8194 = 8194;

    /**
     * 一个中文宽度(== &emsp; == em空格)
     */
    private static final int SPACE_8195 = 8195;

    /**
     * 四分之一中文宽度(四分之一em空格)
     */
    private static final int SPACE_8197 = 8197;

    /**
     * 窄空格
     */
    private static final int SPACE_8201 = 8201;

    /**
     * 去除字符串前后的空格, 包括半角空格和全角空格(中文)等各种空格, java的string.trim()只能去英文半角空格
     *
     * @param str 待去掉前缀尾缀空格的字符串
     * @return 去掉前缀尾缀空格的字符串
     */
    public static String trim(String str) {
        if (StringUtils.isEmpty(str)) {
            return str;
        }

        char[] val = str.toCharArray();
        int st = 0;
        int len = val.length;
        while ((st < len) && isSpace(val[st])) {
            st++;
        }
        while ((st < len) && isSpace(val[len - 1])) {
            len--;
        }
        return ((st > 0) || (len < val.length)) ? str.substring(st, len) : str;
    }

    /**
     * 判断是否为空格
     *
     * @param aChar 待判断的字符
     * @return true:是,false:否
     */
    private static boolean isSpace(char aChar) {
        return aChar == SPACE_32 || aChar == SPACE_12288 || aChar == SPACE_160 || aChar == SPACE_8194
                || aChar == SPACE_8195 || aChar == SPACE_8197 || aChar == SPACE_8201;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76

测试:

public static void main(String[] args) {
    String string = "广州xxxx有限公司  ";
    String print = "【%s】 \t length:%d \n";
    String temp = StringUtil.trim(string);
    System.out.printf(print, temp, temp.length());
    System.out.printf(print, string, string.length());
}
1
2
3
4
5
6
7

运行结果:

# 4.2 替换全角空格

str.replace((char) 12288, ' ').trim();
1

测试:




 




public static void main(String[] args) {
    String string = "广州xxxx有限公司  ";
    String print = "【%s】 \t length:%d \n";
    String temp = string.replace((char) 12288, ' ').trim();
    System.out.printf(print, temp, temp.length());
    System.out.printf(print, string, string.length());
}
1
2
3
4
5
6
7

运行结果:

# 五:参考文献

最后更新: 5/18/2022, 5:59:01 PM