package org.theonefx.wcframework.utils; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Set; import java.util.StringTokenizer; import java.util.TreeSet; /** * @File : StringUtils.java * @ClassName : StringUtils * @Author : 陈曦 * @Date : 2012-2-6 上午10:37:37 * @Version : v2.0 * @Description : String工具类 */ public class StringUtils { private static final String FOLDER_SEPARATOR = "/"; private static final String WINDOWS_FOLDER_SEPARATOR = "\\"; private static final String TOP_PATH = ".."; private static final String CURRENT_PATH = "."; private static final char EXTENSION_SEPARATOR = '.'; /** * @Method : equal * @author : 陈曦 * @date : 2011-12-8 下午03:03:09 * @Description : 判断两个字符串是否相等,且不可同时为null哦! */ public static boolean equal(String a, String b) { return equal(a, b, true); } /** * @Method : equal * @author : 陈曦 * @date : 2011-12-8 下午03:03:09 * @param nullAllow 是否允许同时为空:当nullAllow为false时,如果a==null and b==null,那么方法将返回true * @Description : 判断两个字符串是否相等 */ public static boolean equal(String a, String b, boolean nullAllow) { if(nullAllow){ if(a==null || b==null){ return false; } } if (a == b) { return true; } if (a != null) { return a.equals(b); } else { return b == null; } } /** * @Method : isBlank * @author : 陈曦 * @date : 2011-12-3 下午11:05:56 * @Description : 是不是为空白字符串 * <p> * <pre> * StringUtils.isBlank("") = true * StringUtils.isBlank(null) = true * StringUtils.isBlank(" ") = true * </pre> */ public static boolean isBlank(CharSequence cs) { if (null == cs) return true; int length = cs.length(); for (int i = 0; i < length; i++) { if (!(Character.isWhitespace(cs.charAt(i)))) return false; } return true; } public static boolean isNotBlank(CharSequence s) { return !isBlank(s); } /** * @Method : getFirstNotEmpty * @author : 陈曦 * @date : 2011-12-3 下午10:36:46 * @Description : 返回第一个不是空白字符串的String,如果都是空白字符串则返回字符串"null"。 */ public static CharSequence getFirstNotBlank(CharSequence... strs) { if (strs == null) { return "null"; } for (int i = 0; i < strs.length; i++) { CharSequence str = strs[i]; if (isNotBlank(str)) { return str; } } return "null"; } /** * @Method : isEmpty * @author : 陈曦 * @date : 2011-12-3 下午11:41:20 * @Description : 检查给定的字符序列是否为空。 */ public static boolean isEmpty(CharSequence str) { return (str == null || str.length() == 0); } /** * @Method : isEmpty * @author : 陈曦 * @date : 2011-12-3 下午11:41:20 * @Description : 检查给定的字符序列既不是一个null同时长度也不为0。<br> * 注意,如果所有字符都是空白字符的话将会返回true * <pre> * StringUtils.isNotEmpty(null) = false * StringUtils.isNotEmpty("") = false * StringUtils.isNotEmpty(" ") = true * StringUtils.isNotEmpty("Hello") = true * </pre> * @see #isNotBlank(CharSequence) */ public static boolean isNotEmpty(CharSequence str) { return !isEmpty(str); } /** * @Method : getFirstNotEmpty * @author : 陈曦 * @date : 2011-12-3 下午10:36:46 * @Description : 返回第一个有内容的String,如果没有内容则返回字符串"null"。 */ public static CharSequence getFirstNotEmpty(CharSequence... strs) { if (strs == null) { return "null"; } for (int i = 0; i < strs.length; i++) { CharSequence str = strs[i]; if (isNotEmpty(str)) { return str; } } return "null"; } /** * @Method : split * @author : 陈曦 * @date : 2011-12-3 下午10:50:55 * @Description : 将字符串按照给定的分隔字符转换为数组,分隔符可以是*哦! */ public static String[] split(String pattern, String wildcard) { if ("*".equals(wildcard)) wildcard = "\\u002A"; return pattern.split(wildcard); } /** * @Method : upperFirst * @author : 陈曦 * @date : 2011-12-8 下午02:09:01 * @Description : 字符串首字母转换成大写 * @param str 需要转换的字符串 * @return 转换结果 */ public static String upperFirst(String str) { return changeFirstCharacterCase(str, true); } /** * @Method : lowerFirst * @author : 陈曦 * @date : 2011-12-8 下午02:36:47 * @Description : 将字符串首字母小写 * @param str 字符串 * @return 首字母小写后的新字符串 */ public static String lowerFirst(String str) { return changeFirstCharacterCase(str, false); } /** * @Method : changeFirstCharacterCase * @author : 陈曦 * @date : 2011-12-8 下午02:06:59 * @Description : 转换字符串首字母的大小写 * @param str 需要转换的字符串 * @param capitalize true为转换成大写,false为传换成小写 * @return 转换完成的新的字符串 */ private static String changeFirstCharacterCase(String str, boolean capitalize) { if (isEmpty(str)) { return str; } StringBuilder sb = new StringBuilder(str.length()); if (capitalize) { sb.append(Character.toUpperCase(str.charAt(0))); } else { sb.append(Character.toLowerCase(str.charAt(0))); } sb.append(str.substring(1)); return sb.toString(); } /** * @Method : trimWhitespace * @author : 陈曦 * @date : 2011-12-8 下午02:13:36 * @Description : 去除字符串前后的不可见字符 * @param str 需要处理的字符串 * @return 处理结果 */ public static String trimWhitespace(String str) { if (isEmpty(str)) { return str; } StringBuilder sb = new StringBuilder(str); while (sb.length() > 0 && Character.isWhitespace(sb.charAt(0))) { sb.deleteCharAt(0); } while (sb.length() > 0 && Character.isWhitespace(sb.charAt(sb.length() - 1))) { sb.deleteCharAt(sb.length() - 1); } return sb.toString(); } /** * @Method : trimAllWhitespace * @author : 陈曦 * @date : 2011-12-3 下午10:47:38 * @Description : 去掉字符串所有空白字符 */ public static String trimAllWhitespace(String str) { if (isEmpty(str)) { return str; } StringBuilder sb = new StringBuilder(str); int index = 0; while (sb.length() > index) { if (Character.isWhitespace(sb.charAt(index))) { sb.deleteCharAt(index); } else { index++; } } return sb.toString(); } /** * @Method : trimArrayElements * @author : 陈曦 * @date : 2011-12-3 下午10:48:45 * @Description : 去掉数组中所有元素两端的空格,使用的是J2SE的trim方法 * @return 返回的数组是一个新的引用!!!而非传入的引用 */ public static String[] trimArrayElements(String[] array) { if (ObjectUtils.isEmptyArray(array)) { return new String[0]; } String[] result = new String[array.length]; for (int i = 0; i < array.length; i++) { String element = array[i]; result[i] = (element != null ? element.trim() : null); } return result; } /** * @Method : containsWhitespace * @author : 陈曦 * @date : 2011-12-8 下午02:14:31 * @param str 需要检查的字符串 * @Description : 是否包含不可见字符,不可见字符包括换行符啊! * <pre> * containsWhitespace("aaaaa") = false * containsWhitespace("aaa aa") = true * containsWhitespace("aaa\naa") = true * </pre> */ public static boolean containsWhitespace(CharSequence str) { if (isEmpty(str)) { return false; } int strLen = str.length(); for (int i = 0; i < strLen; i++) { if (Character.isWhitespace(str.charAt(i))) { return true; } } return false; } /** * Convert a CSV list into an array of Strings. * * @param str the input String * @return an array of Strings, or the empty array in case of empty input */ public static String[] commaDelimitedListToStringArray(String str) { return delimitedListToStringArray(str, ","); } /** * Convenience method to convert a CSV string list to a set. Note that this * will suppress duplicates. * * @param str * the input String * @return a Set of String entries in the list */ public static Set<String> commaDelimitedListToSet(String str) { Set<String> set = new TreeSet<String>(); String[] tokens = commaDelimitedListToStringArray(str); for (String token : tokens) { set.add(token); } return set; } /** * Take a String which is a delimited list and convert it to a String array. * <p> * A single delimiter can consists of more than one character: It will still * be considered as single delimiter string, rather than as bunch of * potential delimiter characters - in contrast to * <code>tokenizeToStringArray</code>. * * @param str the input String * @param delimiter the delimiter between elements (this is a single delimiter, rather than a bunch individual delimiter characters) * @return an array of the tokens in the list * @see #tokenizeToStringArray */ public static String[] delimitedListToStringArray(String str, String delimiter) { return delimitedListToStringArray(str, delimiter, null); } /** * Take a String which is a delimited list and convert it to a String array. * <p> * A single delimiter can consists of more than one character: It will still * be considered as single delimiter string, rather than as bunch of * potential delimiter characters - in contrast to * <code>tokenizeToStringArray</code>. * * @param str the input String * @param delimiter the delimiter between elements (this is a single delimiter, rather than a bunch individual delimiter characters) * @param charsToDelete a set of characters to delete. Useful for deleting unwanted line breaks: * e.g. "\r\n\f" will delete all new lines and line * feeds in a String. * @return an array of the tokens in the list * @see #tokenizeToStringArray */ public static String[] delimitedListToStringArray(String str, String delimiter, String charsToDelete) { if (str == null) { return new String[0]; } if (delimiter == null) { return new String[] { str }; } List<String> result = new ArrayList<String>(); if ("".equals(delimiter)) { for (int i = 0; i < str.length(); i++) { result.add(deleteAny(str.substring(i, i + 1), charsToDelete)); } } else { int pos = 0; int delPos; while ((delPos = str.indexOf(delimiter, pos)) != -1) { result.add(deleteAny(str.substring(pos, delPos), charsToDelete)); pos = delPos + delimiter.length(); } if (str.length() > 0 && pos <= str.length()) { // Add rest of String, but not in case of empty input. result.add(deleteAny(str.substring(pos), charsToDelete)); } } return toStringArray(result); } /** * Delete any character in a given String. * * @param inString the original String * @param charsToDelete a set of characters to delete. E.g. "az\n" will delete 'a's, 'z's and new lines. * @return the resulting String */ public static String deleteAny(String inString, String charsToDelete) { if (!isNotEmpty(inString) || !isNotEmpty(charsToDelete)) { return inString; } StringBuilder sb = new StringBuilder(); for (int i = 0; i < inString.length(); i++) { char c = inString.charAt(i); if (charsToDelete.indexOf(c) == -1) { sb.append(c); } } return sb.toString(); } /** * Copy the given Collection into a String array. The Collection must * contain String elements only. * * @param collection * the Collection to copy * @return the String array (<code>null</code> if the passed-in Collection * was <code>null</code>) */ public static String[] toStringArray(Collection<String> collection) { if (collection == null) { return null; } return collection.toArray(new String[collection.size()]); } /** * Copy the given Enumeration into a String array. The Enumeration must * contain String elements only. * * @param enumeration * the Enumeration to copy * @return the String array (<code>null</code> if the passed-in Enumeration * was <code>null</code>) */ public static String[] toStringArray(Enumeration<String> enumeration) { if (enumeration == null) { return null; } List<String> list = Collections.list(enumeration); return list.toArray(new String[list.size()]); } /** * 将字符串按半角逗号,拆分成数组,空元素将被忽略 * * @param s * 字符串 * @return 字符串数组 */ public static String[] splitIgnoreBlank(String s) { return splitIgnoreBlank(s, ","); } /** * @Method : splitIgnoreBlank * @author : 陈曦 * @date : 2011-12-8 下午02:18:57 * @Description : 根据一个正则式,将字符串拆分成数组,空元素将被忽略 * @param s 字符串 * @param regex 正则式 * @return 字符串数组 */ public static String[] splitIgnoreBlank(String s, String regex) { if (null == s) return null; String[] ss = s.split(regex); List<String> list = new LinkedList<String>(); for (String st : ss) { if (isBlank(st)) continue; list.add(trimWhitespace(st)); } return list.toArray(new String[list.size()]); } /** * 复制字符串 * * @param cs 字符串 * @param num 数量 * @return 新字符串 */ public static String dup(CharSequence cs, int num) { if (isBlank(cs) || num <= 0) return ""; StringBuilder sb = new StringBuilder(cs.length() * num); for (int i = 0; i < num; i++) sb.append(cs); return sb.toString(); } /** * 复制字符 * * @param c 字符 * @param num 数量 * @return 新字符串 */ public static String dup(char c, int num) { if (c == 0 || num < 1) return ""; StringBuilder sb = new StringBuilder(num); for (int i = 0; i < num; i++) sb.append(c); return sb.toString(); } /** * 将一个数组转换成字符串 * <p> * 所有的元素都被格式化字符串包裹。 这个格式话字符串只能有一个占位符, %s, %d 等,均可,请视你的数组内容而定 * * @param fmt 格式 * @param objs 数组 * @return 拼合后的字符串 */ public static <T> StringBuilder concatBy(String fmt, T[] objs) { StringBuilder sb = new StringBuilder(); for (T obj : objs) sb.append(String.format(fmt, obj)); return sb; } /** * 将一个数组转换成字符串 * <p> * 所有的元素都被格式化字符串包裹。 这个格式话字符串只能有一个占位符, %s, %d 等,均可,请视你的数组内容而定 * <p> * 每个元素之间,都会用一个给定的字符分隔 * * @param ptn 格式 * @param c 分隔符 * @param objs 数组 * @return 拼合后的字符串 */ public static <T> StringBuilder concatBy(String ptn, Object c, T[] objs) { StringBuilder sb = new StringBuilder(); for (T obj : objs) sb.append(String.format(ptn, obj)).append(c); if (sb.length() > 0) sb.deleteCharAt(sb.length() - 1); return sb; } /** * 将一个数组转换成字符串 * <p> * 每个元素之间,都会用一个给定的字符分隔 * * @param c 分隔符 * @param objs 数组 * @return 拼合后的字符串 */ public static <T> StringBuilder concat(Object c, T[] objs) { StringBuilder sb = new StringBuilder(); if (null == objs || 0 == objs.length) return sb; sb.append(objs[0]); for (int i = 1; i < objs.length; i++) sb.append(c).append(objs[i]); return sb; } /** * 将一个长整型数组转换成字符串 * <p> * 每个元素之间,都会用一个给定的字符分隔 * * @param c 分隔符 * @param vals 数组 * @return 拼合后的字符串 */ public static StringBuilder concat(Object c, long[] vals) { StringBuilder sb = new StringBuilder(); if (null == vals || 0 == vals.length) return sb; sb.append(vals[0]); for (int i = 1; i < vals.length; i++) sb.append(c).append(vals[i]); return sb; } /** * 将一个整型数组转换成字符串 * <p> * 每个元素之间,都会用一个给定的字符分隔 * * @param c 分隔符 * @param vals 数组 * @return 拼合后的字符串 */ public static StringBuilder concat(Object c, int[] vals) { StringBuilder sb = new StringBuilder(); if (null == vals || 0 == vals.length) return sb; sb.append(vals[0]); for (int i = 1; i < vals.length; i++) sb.append(c).append(vals[i]); return sb; } /** * 将一个数组的部分元素转换成字符串 * <p> * 每个元素之间,都会用一个给定的字符分隔 * * @param offset 开始元素的下标 * @param len 元素数量 * @param c 分隔符 * @param objs 数组 * @return 拼合后的字符串 */ public static <T> StringBuilder concat(int offset, int len, Object c, T[] objs) { StringBuilder sb = new StringBuilder(); if (null == objs || len < 0 || 0 == objs.length) return sb; if (offset < objs.length) { sb.append(objs[offset]); for (int i = 1; i < len && i + offset < objs.length; i++) { sb.append(c).append(objs[i + offset]); } } return sb; } /** * 将一个数组所有元素拼合成一个字符串 * * @param objs 数组 * @return 拼合后的字符串 */ public static <T> StringBuilder concat(T[] objs) { StringBuilder sb = new StringBuilder(); for (T e : objs) sb.append(e.toString()); return sb; } /** * 将一个数组部分元素拼合成一个字符串 * * @param offset 开始元素的下标 * @param len 元素数量 * @param array 数组 * @return 拼合后的字符串 */ public static <T> StringBuilder concat(int offset, int len, T[] array) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < len; i++) { sb.append(array[i + offset].toString()); } return sb; } /** * 将一个集合转换成字符串 * <p> * 每个元素之间,都会用一个给定的字符分隔 * * @param c 分隔符 * @param coll 集合 * @return 拼合后的字符串 */ public static <T> StringBuilder concat(Object c, Collection<T> coll) { StringBuilder sb = new StringBuilder(); if (null == coll || coll.isEmpty()) return sb; Iterator<T> it = coll.iterator(); sb.append(it.next()); while (it.hasNext()) sb.append(c).append(it.next()); return sb; } /** * Normalize the path by suppressing sequences like "path/.." and inner * simple dots. * <p> * The result is convenient for path comparison. For other uses, notice that * Windows separators ("\") are replaced by simple slashes. * * @param path * the original path * @return the normalized path */ public static String cleanPath(String path) { if (path == null) { return null; } String pathToUse = replace(path, WINDOWS_FOLDER_SEPARATOR, FOLDER_SEPARATOR); // Strip prefix from path to analyze, to not treat it as part of the // first path element. This is necessary to correctly parse paths like // "file:core/../core/io/Resource.class", where the ".." should just // strip the first "core" directory while keeping the "file:" prefix. int prefixIndex = pathToUse.indexOf(":"); String prefix = ""; if (prefixIndex != -1) { prefix = pathToUse.substring(0, prefixIndex + 1); pathToUse = pathToUse.substring(prefixIndex + 1); } if (pathToUse.startsWith(FOLDER_SEPARATOR)) { prefix = prefix + FOLDER_SEPARATOR; pathToUse = pathToUse.substring(1); } String[] pathArray = delimitedListToStringArray(pathToUse, FOLDER_SEPARATOR); List<String> pathElements = new LinkedList<String>(); int tops = 0; for (int i = pathArray.length - 1; i >= 0; i--) { String element = pathArray[i]; if (CURRENT_PATH.equals(element)) { // Points to current directory - drop it. } else if (TOP_PATH.equals(element)) { // Registering top path found. tops++; } else { if (tops > 0) { // Merging path element with element corresponding to top // path. tops--; } else { // Normal path element found. pathElements.add(0, element); } } } // Remaining top paths need to be retained. for (int i = 0; i < tops; i++) { pathElements.add(0, TOP_PATH); } return prefix + collectionToDelimitedString(pathElements, FOLDER_SEPARATOR); } /** * Replace all occurences of a substring within a string with another * string. * * @param inString String to examine * @param oldPattern String to replace * @param newPattern String to insert * @return a String with the replacements */ public static String replace(String inString, String oldPattern, String newPattern) { if (!isNotEmpty(inString) || !isNotEmpty(oldPattern) || newPattern == null) { return inString; } StringBuilder sb = new StringBuilder(); int pos = 0; // our position in the old string int index = inString.indexOf(oldPattern); // the index of an occurrence we've found, or -1 int patLen = oldPattern.length(); while (index >= 0) { sb.append(inString.substring(pos, index)); sb.append(newPattern); pos = index + patLen; index = inString.indexOf(oldPattern, pos); } sb.append(inString.substring(pos)); // remember to append any characters to the right of a match return sb.toString(); } /** * Convenience method to return a Collection as a delimited (e.g. CSV) * String. E.g. useful for <code>toString()</code> implementations. * * @param coll the Collection to display * @param delim the delimiter to use (probably a ",") * @param prefix the String to start each element with * @param suffix the String to end each element with * @return the delimited String */ public static String collectionToDelimitedString(Collection<?> coll, String delim, String prefix, String suffix) { if (CollectionUtils.isEmpty(coll)) { return ""; } StringBuilder sb = new StringBuilder(); Iterator<?> it = coll.iterator(); while (it.hasNext()) { sb.append(prefix).append(it.next()).append(suffix); if (it.hasNext()) { sb.append(delim); } } return sb.toString(); } /** * Convenience method to return a Collection as a delimited (e.g. CSV) * String. E.g. useful for <code>toString()</code> implementations. * * @param coll the Collection to display * @param delim the delimiter to use (probably a ",") * @return the delimited String */ public static String collectionToDelimitedString(Collection<?> coll, String delim) { return collectionToDelimitedString(coll, delim, "", ""); } /** * Convenience method to return a Collection as a CSV String. E.g. useful * for <code>toString()</code> implementations. * * @param coll the Collection to display * @return the delimited String */ public static String collectionToCommaDelimitedString(Collection<?> coll) { return collectionToDelimitedString(coll, ","); } public static String arrayToDelimitedString(Object[] arr, String delim) { if (ObjectUtils.isEmptyArray(arr)) { return ""; } if (arr.length == 1) { return ObjectUtils.nullSafeToString(arr[0]); } StringBuilder sb = new StringBuilder(); for (int i = 0; i < arr.length; i++) { if (i > 0) { sb.append(delim); } sb.append(arr[i]); } return sb.toString(); } public static String arrayToCommaDelimitedString(Object[] arr) { return arrayToDelimitedString(arr, ","); } public static boolean substringMatch(CharSequence str, int index, CharSequence substring) { for (int j = 0; j < substring.length(); j++) { int i = index + j; if (i >= str.length() || str.charAt(i) != substring.charAt(j)) { return false; } } return true; } public static String applyRelativePath(String path, String relativePath) { int separatorIndex = path.lastIndexOf(FOLDER_SEPARATOR); if (separatorIndex != -1) { String newPath = path.substring(0, separatorIndex); if (!relativePath.startsWith(FOLDER_SEPARATOR)) { newPath += FOLDER_SEPARATOR; } return newPath + relativePath; } else { return relativePath; } } /** * Extract the filename from the given path, * e.g. "mypath/myfile.txt" -> "myfile.txt". * @param path the file path (may be <code>null</code>) * @return the extracted filename, or <code>null</code> if none */ public static String getFilename(String path) { if (path == null) { return null; } int separatorIndex = path.lastIndexOf(FOLDER_SEPARATOR); return (separatorIndex != -1 ? path.substring(separatorIndex + 1) : path); } /** * Extract the filename extension from the given path, * e.g. "mypath/myfile.txt" -> "txt". * @param path the file path (may be <code>null</code>) * @return the extracted filename extension, or <code>null</code> if none */ public static String getFilenameExtension(String path) { if (path == null) { return null; } int sepIndex = path.lastIndexOf(EXTENSION_SEPARATOR); return (sepIndex != -1 ? path.substring(sepIndex + 1) : null); } /** * Tokenize the given String into a String array via a StringTokenizer. * Trims tokens and omits empty tokens. * <p>The given delimiters string is supposed to consist of any number of * delimiter characters. Each of those characters can be used to separate * tokens. A delimiter is always a single character; for multi-character * delimiters, consider using <code>delimitedListToStringArray</code> * @param str the String to tokenize * @param delimiters the delimiter characters, assembled as String * (each of those characters is individually considered as delimiter). * @return an array of the tokens * @see java.util.StringTokenizer * @see java.lang.String#trim() * @see #delimitedListToStringArray */ public static String[] tokenizeToStringArray(String str, String delimiters) { return tokenizeToStringArray(str, delimiters, true, true); } /** * Tokenize the given String into a String array via a StringTokenizer. * <p>The given delimiters string is supposed to consist of any number of * delimiter characters. Each of those characters can be used to separate * tokens. A delimiter is always a single character; for multi-character * delimiters, consider using <code>delimitedListToStringArray</code> * @param str the String to tokenize * @param delimiters the delimiter characters, assembled as String * (each of those characters is individually considered as delimiter) * @param trimTokens trim the tokens via String's <code>trim</code> * @param ignoreEmptyTokens omit empty tokens from the result array * (only applies to tokens that are empty after trimming; StringTokenizer * will not consider subsequent delimiters as token in the first place). * @return an array of the tokens (<code>null</code> if the input String * was <code>null</code>) * @see java.util.StringTokenizer * @see java.lang.String#trim() * @see #delimitedListToStringArray */ public static String[] tokenizeToStringArray( String str, String delimiters, boolean trimTokens, boolean ignoreEmptyTokens) { if (str == null) { return null; } StringTokenizer st = new StringTokenizer(str, delimiters); List<String> tokens = new ArrayList<String>(); while (st.hasMoreTokens()) { String token = st.nextToken(); if (trimTokens) { token = token.trim(); } if (!ignoreEmptyTokens || token.length() > 0) { tokens.add(token); } } return toStringArray(tokens); } /** * 计算sub在字符串s中出现的次数 * @param str 需要搜索 的字串. 如果为null则返回0 * @param sub 需要统计的字符串. 如果为null则返回0 */ public static int countOccurrencesOf(String str, String sub) { if (isEmpty(str)||isEmpty(sub)) { return 0; } int count = 0; int pos = 0; int idx; while ((idx = str.indexOf(sub, pos)) != -1) { ++count; pos = idx + sub.length(); } return count; } public static Locale parseLocaleString(String localeString) { String[] parts = tokenizeToStringArray(localeString, "_ ", false, false); String language = (parts.length > 0 ? parts[0] : ""); String country = (parts.length > 1 ? parts[1] : ""); String variant = ""; if (parts.length >= 2) { // There is definitely a variant, and it is everything after the country // code sans the separator between the country code and the variant. int endIndexOfCountryCode = localeString.indexOf(country) + country.length(); // Strip off any leading '_' and whitespace, what's left is the variant. variant = trimLeadingWhitespace(localeString.substring(endIndexOfCountryCode)); if (variant.startsWith("_")) { variant = trimLeadingCharacter(variant, '_'); } } return (language.length() > 0 ? new Locale(language, country, variant) : null); } public static String trimLeadingWhitespace(String str) { if (isBlank(str)) { return str; } StringBuilder sb = new StringBuilder(str); while (sb.length() > 0 && Character.isWhitespace(sb.charAt(0))) { sb.deleteCharAt(0); } return sb.toString(); } public static String trimLeadingCharacter(String str, char leadingCharacter) { if (isBlank(str)) { return str; } StringBuilder sb = new StringBuilder(str); while (sb.length() > 0 && sb.charAt(0) == leadingCharacter) { sb.deleteCharAt(0); } return sb.toString(); } public static String trimCharacter(String str, char character) { if (isBlank(str)) { return str; } StringBuilder sb = new StringBuilder(str); while (sb.length() > 0 && sb.charAt(0) == character) { sb.deleteCharAt(0); } while (sb.length() > 0 && sb.charAt(sb.length() - 1) == character) { sb.deleteCharAt(sb.length() - 1); } return sb.toString(); } /** * 将字符串首字母大写 * * @param s * 字符串 * @return 首字母大写后的新字符串 */ public static String capitalize(CharSequence s) { if (null == s) return null; int len = s.length(); if (len == 0) return ""; char char0 = s.charAt(0); if (Character.isUpperCase(char0)) return s.toString(); return new StringBuilder(len).append(Character.toUpperCase(char0)) .append(s.subSequence(1, len)) .toString(); } }