zoukankan      html  css  js  c++  java
  • StringUtils # split 的坑

    背景

    public static void main(String[] args) {
        String netmask = "103.140.147.0/24";
        System.out.println(org.springframework.util.StringUtils.split(netmask, ","));
        System.out.println(org.apache.commons.lang3.StringUtils.split(netmask, ","));
    }
    
    

    有这么一段常见的代码,可以想象结果吗? 今天在项目测试中就出现了 误用StringUtils.split. 方法导致的 NullPointException。

    根据 异常 NullPointException 提示,我定位了到 StringUtils.split 方法返回null,但不符合 逻辑啊, 不满足 分片,应该直接返回 本身 才对。况且以前也在用的,点进去查看了 jar,并看了方法。

    发现引用了 org.springframework.util.StringUtils

    /**
    	 * Split a {@code String} at the first occurrence of the delimiter.
    	 * Does not include the delimiter in the result.
    	 * @param toSplit the string to split
    	 * @param delimiter to split the string up with
    	 * @return a two element array with index 0 being before the delimiter, and
    	 * index 1 being after the delimiter (neither element includes the delimiter);
    	 * or {@code null} if the delimiter wasn't found in the given input {@code String}
    	 */
    	@Nullable
    	public static String[] split(@Nullable String toSplit, @Nullable String delimiter) {
    		if (!hasLength(toSplit) || !hasLength(delimiter)) {
    			return null;
    		}
    		int offset = toSplit.indexOf(delimiter);
    		if (offset < 0) {
    			return null;
    		}
    
    		String beforeDelimiter = toSplit.substring(0, offset);
    		String afterDelimiter = toSplit.substring(offset + delimiter.length());
    		return new String[] {beforeDelimiter, afterDelimiter};
    	}
    

    其实也解释的很清楚了,分隔符 不存在,就直接返回了 null

    分析

    在改成 org.apache.commons.lang3.StringUtils, 才是符合我们要求的

    public static String[] split(final String str, final String separatorChars) {
            return splitWorker(str, separatorChars, -1, false);
        }
        
    
    /**
         * Performs the logic for the {@code split} and
         * {@code splitPreserveAllTokens} methods that return a maximum array
         * length.
         *
         * @param str  the String to parse, may be {@code null}
         * @param separatorChars the separate character
         * @param max  the maximum number of elements to include in the
         *  array. A zero or negative value implies no limit.
         * @param preserveAllTokens if {@code true}, adjacent separators are
         * treated as empty token separators; if {@code false}, adjacent
         * separators are treated as one separator.
         * @return an array of parsed Strings, {@code null} if null String input
         */
        private static String[] splitWorker(final String str, final String separatorChars, final int max, final boolean preserveAllTokens) {
            // Performance tuned for 2.0 (JDK1.4)
            // Direct code is quicker than StringTokenizer.
            // Also, StringTokenizer uses isSpace() not isWhitespace()
    
            if (str == null) {
                return null;
            }
            final int len = str.length();
            if (len == 0) {
                return ArrayUtils.EMPTY_STRING_ARRAY;
            }
            final List<String> list = new ArrayList<>();
            int sizePlus1 = 1;
            int i = 0, start = 0;
            boolean match = false;
            boolean lastMatch = false;
            if (separatorChars == null) {
                // Null separator means use whitespace
                while (i < len) {
                    if (Character.isWhitespace(str.charAt(i))) {
                        if (match || preserveAllTokens) {
                            lastMatch = true;
                            if (sizePlus1++ == max) {
                                i = len;
                                lastMatch = false;
                            }
                            list.add(str.substring(start, i));
                            match = false;
                        }
                        start = ++i;
                        continue;
                    }
                    lastMatch = false;
                    match = true;
                    i++;
                }
            } else if (separatorChars.length() == 1) {//我的场景
                // Optimise 1 character case
                final char sep = separatorChars.charAt(0);
                while (i < len) {
                    if (str.charAt(i) == sep) {
                        if (match || preserveAllTokens) {
                            lastMatch = true;
                            if (sizePlus1++ == max) {
                                i = len;
                                lastMatch = false;
                            }
                            list.add(str.substring(start, i)); //这里 分割
                            match = false;
                        }
                        start = ++i;
                        continue;
                    }
                    lastMatch = false;
                    match = true;
                    i++;
                }
            } else {
                // standard case    这里像是有满足 分隔符中的字符 就进行分割
                while (i < len) {
                    if (separatorChars.indexOf(str.charAt(i)) >= 0) {
                        if (match || preserveAllTokens) {
                            lastMatch = true;
                            if (sizePlus1++ == max) {
                                i = len;
                                lastMatch = false;
                            }
                            list.add(str.substring(start, i));
                            match = false;
                        }
                        start = ++i;
                        continue;
                    }
                    lastMatch = false;
                    match = true;
                    i++;
                }
            }
            if (match || preserveAllTokens && lastMatch) {
                list.add(str.substring(start, i));  //遍历完自符,如果分隔符不存在,则返回 str 本身
            }
            return list.toArray(new String[list.size()]);
        }
    

    扩展

    org.apache.commons.lang3.StringUtils@split 方法能满足一般的情况,但是 他不会 对 空格做处理,如果有特殊要求,可以用 guava的 工具类

    List<String> hostNameList = Splitter.on(",")
                        .trimResults()
                        .omitEmptyStrings()//可以 选择是否对 空字符串 做处理
                        .splitToList(hostName);
    

    guava 是一种很强大的工具,能想到的功能基本都有,大家有空可以多看看它里面的实现和 设计思路。

  • 相关阅读:
    进程池,线程池,协程,gevent模块,协程实现单线程服务端与多线程客户端通信,IO模型
    线程相关 GIL queue event 死锁与递归锁 信号量l
    生产者消费者模型 线程相关
    进程的开启方式 进程的join方法 进程间的内存隔离 其他相关方法 守护进程 互斥锁
    udp协议 及相关 利用tcp上传文件 socketserver服务
    socket套接字 tcp协议下的粘包处理
    常用模块的完善 random shutil shevle 三流 logging
    day 29 元类
    Django入门
    MySQL多表查询
  • 原文地址:https://www.cnblogs.com/idea-persistence/p/13639054.html
Copyright © 2011-2022 走看看