Java StringTokenizer分隔符拆分字符串
作者:韩长奇
StringTokenizer的成员变量
// 以下七个参数是在三个参数的构造方法中设置的 // 当前位置 private int currentPosition; // 下一个要处理的字符的索引 private int newPosition; // 最大位置,即被分割字符串的长度 private int maxPosition; // 要分割的字符串 private String str; // 分隔符,字符串中的每个字符都是一个分隔符 private String delimiters; // 指示是否将分隔符作为令牌返回的标志。 private boolean retDelims; // 分隔符是否改变的表示,true表示分隔符发生改变,false表示分隔符没发生改变,默认为false private boolean delimsChanged; // 以下三个参数是在构造方法的setMaxDelimCodePoint()方法中设置 // 保存分隔符中ASCII码值最大的的字符 private int maxDelimCodePoint; // 是否包含代理,即是否涉及到UTF-16和Unicode编码,一般不会涉及到。 private boolean hasSurrogates = false; // 如果涉及到代理(即涉及到UTF-16和Unicode编码),会将所有的分隔符转换成码点值保存到int数组中 private int[] delimiterCodePoints;
StringTokenizer 的构造方法
1、StringTokenizer(String str)只有一个入参的构造函数。
// 单个参数的构造函数,参数为需要被解析的字符串 public StringTokenizer(String str) { // 调用三个参数的构造方法: // 1、需要被解析的字符串; // 2、Java默认的分隔符,包括空格、制表符、换行符、回车符、\f; // 3、指示是否将分隔符作为令牌返回的标志。false表示分隔符本身不会被视为令牌 this(str, " \t\n\r\f", false); }
2、StringTokenizer(String str, String delim)两个入参的构造函数
// 两个参数的构造方法,str:需要被解析的字符串 delim:分隔符字符串,每一个字符都是一个分隔符 public StringTokenizer(String str, String delim) { // 1、需要被解析的字符串; // 2、使用指定的字符作为分割符 // 3、指示是否将分隔符作为令牌返回的标志。false表示分隔符本身不会被视为令牌 this(str, delim, false); }
3、StringTokenizer(String str, String delim, boolean returnDelims)三个入参的构造函数
public StringTokenizer(String str, String delim, boolean returnDelims) { // 当前位置设为0,即索引下标为0 currentPosition = 0; // 下个要处理的位置 newPosition = -1; // 分隔符是否改变的表示,true表示分隔符发生改变,false表示分隔符没发生改变,默认为false delimsChanged = false; // 要分割的字符串 this.str = str; // 最大位置,即被分割字符串的长度 maxPosition = str.length(); // 设置分隔符,字符串中的每个字符都是一个分隔符 delimiters = delim; // 指示是否将分隔符作为令牌返回的标志。 retDelims = returnDelims; // 设置分割符中ASCII码值最大字符 setMaxDelimCodePoint(); }
StringTokenizer public修饰的方法
hasMoreTokens() 判断是否被分割字符串中是否有更多可用的标记
注意:该方法为public修饰的方法。
1、返回true,表示后续调用nextToken()将成功返回一个token(即分割好的字符串)。
2、返回false,表示后续调用nextToken()将会抛出异常:NoSuchElementException
// 如果retDelims为false(不将分隔符作为令牌返回的标志),判断当前位置及当前位置之后是否还有非分隔符的字符 // 如果retDelims为true(将分隔符作为令牌返回的标志),判断当前位置是否小于被分割字符串的长度 public boolean hasMoreTokens() { /* * Temporarily store this position and use it in the following * nextToken() method only if the delimiters haven't been changed in * that nextToken() invocation. * * 临时存储此位置,并仅在nextToken()调用中分隔符未更改时在以下nextToken()方法中使用它。 */ // 如果retDelims为false(不将分隔符作为令牌返回的标志),则返回当前位置或当前位置之后第一个非分隔符的字符索引 // 如果retDelims为true(将分隔符作为令牌返回的标志),返回当前位置字符的索引 newPosition = skipDelimiters(currentPosition); // 当前位置小于被分割字符串的长度 return (newPosition < maxPosition); }
nextToken()获取下一个根据分隔符拆分的字符串
注意:该方法为public关键字修饰的方法
// 获取下一个根据分隔符拆分的字符串 public String nextToken() { /* * If next position already computed in hasMoreElements() and * delimiters have changed between the computation and this invocation, * then use the computed value. * 如果下一个位置已经在hasMoreElements()中计算过,并且分隔符在计算和调用之间发生了变化,则使用计算值。 */ // 当下一个要处理的字符索引 >= 0 并且 分隔符未发生改变时,将下一个要处理的字符索引 赋值给 当前位置(当前要处理的字符索引) // 否则重新计算下一个要处理的字符索引 currentPosition = (newPosition >= 0 && !delimsChanged) ? newPosition : skipDelimiters(currentPosition); /* Reset these anyway */ // 将分隔符是否改变的标志设置为false,表示分隔符不再变化 delimsChanged = false; // 重新将下一个要处理的字符索引设置为-1 newPosition = -1; // 如果当前位置 >= 最大位置,抛出异常 if (currentPosition >= maxPosition) throw new NoSuchElementException(); // 将当前位置设置为截取字符串的开始下标 int start = currentPosition; // 获取下一个分隔符的索引。并且currentPosition(当前位置)变为分隔符所在位置的索引 currentPosition = scanToken(currentPosition); // 截取字符串,从第一个非分隔符的索引开始,到下一个分隔符的索引结束。 return str.substring(start, currentPosition); }
nextToken(String delim)根据新的分隔符获取下一个被拆分出来的字符串
注意:该方法为public修饰的方法
// 根据新的分隔符获取下一个被拆分出来的字符串 public String nextToken(String delim) { // 设置分隔符,字符串中的每个字符都是一个分隔符 delimiters = delim; /* delimiter string specified, so set the appropriate flag. * 创建对象后重新设置分隔符字符串,需要将此标志设置为true。 */ delimsChanged = true; // 需改分隔符字符串后需要重新设置设置maxDelimCodePoint(分隔符中的最高字符,即ASCII码值最大的的字符) setMaxDelimCodePoint(); // 根据分隔符获取下一个拆分出来的字符串 return nextToken(); }
countTokens()获取被分割的字符串中还能拆分出几个字符串。
注意:该方法为public修饰的方法,随这nextToken()方法的调用,该方法返回值会发生变化。
// 获取被分割的字符串中还能拆分出几个字符串。随这nextToken()方法的调用,该方法返回值会发生变化 public int countTokens() { int count = 0; // currentPosition为成员变量,所以会随着其他方法的调用发生变化,因此该方法的返回值会发生变化 int currpos = currentPosition; // 当前位置字符 < 被分割字符串长度 while (currpos < maxPosition) { // 获取当前位置或当前位置之后第一个非分隔符的字符索引 currpos = skipDelimiters(currpos); // 如果 当前位置 >= 被分割字符串长度 则跳出循环 if (currpos >= maxPosition) break; // 获取下一个分隔符的索引 currpos = scanToken(currpos); count++; } return count; }
StringTokenizer private修饰的方法
setMaxDelimCodePoint()设置分隔符中的最高字符,即ASCII码值最大的的字符。
注意:该方法为private修饰的方法。
// 设置maxDelimCodePoint的值为分隔符中的最高字符,即ASCII码值最大的的字符。 private void setMaxDelimCodePoint() { // 如果分隔符为null, if (delimiters == null) { maxDelimCodePoint = 0; return; } int m = 0; int c; int count = 0; // Character.charCount(c) 如果c大于等于 Unicode最小补充码点 返回2,否则返回1 for (int i = 0; i < delimiters.length(); i += Character.charCount(c)) { // delimiters分隔符字符串中下标为i的字符,使用int接收,返回的是ASCII值 c = delimiters.charAt(i); // 如果 c >= Unicode编码的最小高代理 并且 c <= Unicode编码的最大低代理。与UTF-16相关。 if (c >= Character.MIN_HIGH_SURROGATE && c <= Character.MAX_LOW_SURROGATE) { // 获取分隔符字符串 下标为i处的字符的Unicode码点值 c = delimiters.codePointAt(i); // 将是否使用代理设置为true hasSurrogates = true; } // 比较每个分隔符的ASCII值,将最大ASCII值赋值给m if (m < c) m = c; // count表示 count++; } // 将最大ASCII值赋值给maxDelimCodePoint maxDelimCodePoint = m; // 如果有代理 if (hasSurrogates) { // 如果涉及到UTF-16和Unicode编码,将所有分隔符转换成 Unicode码点值 delimiterCodePoints = new int[count]; for (int i = 0, j = 0; i < count; i++, j += Character.charCount(c)) { c = delimiters.codePointAt(j); delimiterCodePoints[i] = c; } } }
skipDelimiters()获取下一个要处理字符的索引
注意:该方法为private修饰的方法
// 如果retDelims为false(不将分隔符作为令牌返回的标志),则返回当前位置或当前位置之后第一个非分隔符的字符索引 // 如果retDelims为true(将分隔符作为令牌返回的标志),返回当前位置字符的索引 private int skipDelimiters(int startPos) { // 分隔符字符串非空校验 if (delimiters == null) throw new NullPointerException(); // 记录字符串下标位置,即当前判断的位置 int position = startPos; // retDelims 一般设置为false,表示不将分隔符作为令牌返回的标志。 while (!retDelims && position < maxPosition) { // hasSurrogates默认为false,表示没有代理,即不涉及UTF-16和Unicode编码 if (!hasSurrogates) { // 从被分割字符串str中获取当前位置的字符 char c = str.charAt(position); // 如果 当前位置的字符 大于 分隔符中的最大字符 或者 分隔符字符串中没有当前字符 if ((c > maxDelimCodePoint) || (delimiters.indexOf(c) < 0)) // 跳出循环 break; // 当前位置 +1 position++; } else { // 存在代理(涉及UTF-16和Unicode编码),获取当前位置字符的Unicode码点值 int c = str.codePointAt(position); // 如果当前位置的字符码点值大于分隔符中的最大字符 或者 当前码点至对应的字符不是分隔符 if ((c > maxDelimCodePoint) || !isDelimiter(c)) { // 跳出本次循环 break; } // 如果c大于等于 Unicode最小补充码点 当前位置+2,否则,当前位置+1 position += Character.charCount(c); } } return position; }
isDelimiter()判断码点值对应的字符是不是分隔符
注意:该方法为private修饰的方法
// 判断码点值对应的字符是不是分隔符,返回true表示当前码点值是一个分隔符,返回false表示当前的码点值不是分隔符的码点值 private boolean isDelimiter(int codePoint) { // 涉及到代理(即涉及到UTF-16和Unicode编码),从分隔符成码点值数组中遍历 for (int delimiterCodePoint : delimiterCodePoints) { // 如果分隔符成码点值数组中的某一个值等于当前码点值 if (delimiterCodePoint == codePoint) { // 返回true return true; } } // 返回false return false; }
scanToken(int startPos)获取下一个分隔符的索引
注意:该方法为private修饰的方法
// 从startPos跳过并返回遇到的下一个分隔符字符的索引,如果没有找到这样的分隔符,则返回maxPosition。 private int scanToken(int startPos) { int position = startPos; // 当要操作的字符索引小于被分割字符串的长度 while (position < maxPosition) { // 当不包含代理,即不涉及到UTF-16和Unicode编码。 if (!hasSurrogates) { // 获取当前索引位置的字符 char c = str.charAt(position); // 当前字符 <= 分隔符最大字符 并且 当前字符是一个分隔符。找到分隔符时推出 if ((c <= maxDelimCodePoint) && (delimiters.indexOf(c) >= 0)) // 退出循环 break; // 当前索引位置+1 position++; } else { // 获取当前索引位置对应的字符的Unicode码值 int c = str.codePointAt(position); // 当前字符 <= 分隔符最大字符 并且 当前字符是一个分隔符。找到分隔符时推出 if ((c <= maxDelimCodePoint) && isDelimiter(c)) break; // 涉及到Unicode编码,需要判断当前字符是占一个位置还是两个位置 position += Character.charCount(c); } } // 需要将分隔符作为令牌返回的标志,并且 当前位置与起始位置一直 if (retDelims && (startPos == position)) { // 当不包含代理,即不涉及到UTF-16和Unicode编码。 if (!hasSurrogates) { char c = str.charAt(position); // 当前字符 <= 分隔符最大字符 并且 当前字符是一个分隔符。 if ((c <= maxDelimCodePoint) && (delimiters.indexOf(c) >= 0)) position++; } else { int c = str.codePointAt(position); // 当前字符 <= 分隔符最大字符 并且 当前字符是一个分隔符。 if ((c <= maxDelimCodePoint) && isDelimiter(c)) // // 涉及到Unicode编码,需要判断当前字符是占一个位置还是两个位置 position += Character.charCount(c); } } // 返回分隔符的索引值 return position; }
StringTokenizer 类的使用
1、可以使用以下方式创建一个spring容器
public static void main(String[] args) { ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext(); // 设置配置文件路径,多个位置使用逗号、分号、空格、制表符、换行 五种方式分割。 classPathXmlApplicationContext.setConfigLocation("classpath*:/*.xml"); classPathXmlApplicationContext.refresh(); }
2、main方法中创建ClassPathXmlApplicationContext对象后会调用setConfigLocation(String location)方法。
该方法是AbstractRefreshableConfigApplicationContext类中的方法。源代码如下:
public void setConfigLocation(String location) { // 将传入的字符串,根据逗号、分号、空格、制表符、换行 五种字符拆分成数组 setConfigLocations(StringUtils.tokenizeToStringArray(location, CONFIG_LOCATION_DELIMITERS)); }
该方法中的CONFIG_LOCATION_DELIMITERS是ConfigurableApplicationContext接口中定义的常量。
String CONFIG_LOCATION_DELIMITERS = ",; \t\n";
3、setConfigLocation(String location)方法会调用StringUtils中的方法。源代码如下:
// 根据 delimiters字符串中的字符作为分隔符,将str拆分成数组 public static String[] tokenizeToStringArray(@Nullable String str, String delimiters) { return tokenizeToStringArray(str, delimiters, true, true); }
4、调用StringUtils中重载的setConfigLocation方法,源代码如下:
1)该方法中调用了StringTokenizer类中的hasMoreTokens()方法作为循环条件。
2)该方法中调用StringTokenizer类中的nextToken()方法获取下一个根据分隔符拆分的字符串。
通过这两个方法,将设置配置文件的字符串根据指定的分隔符拆分。
// str 要处理的字符串 // delimiters 分隔符,每个字符都是一个分隔符 // trimTokens 拆分出来的字符串是否要去掉首尾的空格。true表示去掉 // ignoreEmptyTokens 是否忽略空字符串。true表示忽略 public static String[] tokenizeToStringArray( @Nullable String str, String delimiters, boolean trimTokens, boolean ignoreEmptyTokens) { if (str == null) { return EMPTY_STRING_ARRAY; } // 将要操作的字符串和分隔符字符串设置到StringTokenizer对象中 StringTokenizer st = new StringTokenizer(str, delimiters); List<String> tokens = new ArrayList<>(); while (st.hasMoreTokens()) { String token = st.nextToken(); if (trimTokens) { token = token.trim(); } if (!ignoreEmptyTokens || token.length() > 0) { tokens.add(token); } } // List集合转数组 return toStringArray(tokens); }
5、直接使用ClassPathXmlApplicationContext(String configLocation)有参构造,直接设置了配置文件路径,拆分方式与无参构造不同。
到此这篇关于Java StringTokenizer分隔符拆分字符串的文章就介绍到这了,更多相关Java StringTokenizer拆分字符串内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!