当前位置: 首页 > news >正文

java:java.util.StringTokenizer实现字符串切割

java:java.util.StringTokenizer实现字符串切割

1 前言

java.util工具包提供了字符串切割的工具类StringTokenizer,Spring等常见框架的字符串工具类(如Spring的StringUtils),常见此类使用。

例如Spring的StringUtils下的方法:

public static String[] tokenizeToStringArray(@Nullable String str, String delimiters, boolean trimTokens, boolean ignoreEmptyTokens) {if (str == null) {return EMPTY_STRING_ARRAY;}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);}}return toStringArray(tokens);
}

又如定时任务框架Quartz中,cron表达式类CronExpression,其中的buildExpression方法是为了处理cron表达式的,cron表达式有7个子表达式,空格隔开,cron表达式字符串的切割也使用到了StringTokenizer类,方法如下:

protected void buildExpression(String expression) throws ParseException {this.expressionParsed = true;try {if (this.seconds == null) {this.seconds = new TreeSet();}if (this.minutes == null) {this.minutes = new TreeSet();}if (this.hours == null) {this.hours = new TreeSet();}if (this.daysOfMonth == null) {this.daysOfMonth = new TreeSet();}if (this.months == null) {this.months = new TreeSet();}if (this.daysOfWeek == null) {this.daysOfWeek = new TreeSet();}if (this.years == null) {this.years = new TreeSet();}int exprOn = 0;for(StringTokenizer exprsTok = new StringTokenizer(expression, " \t", false); exprsTok.hasMoreTokens() && exprOn <= 6; ++exprOn) {String expr = exprsTok.nextToken().trim();if (exprOn == 3 && expr.indexOf(76) != -1 && expr.length() > 1 && expr.contains(",")) {throw new ParseException("Support for specifying 'L' and 'LW' with other days of the month is not implemented", -1);}if (exprOn == 5 && expr.indexOf(76) != -1 && expr.length() > 1 && expr.contains(",")) {throw new ParseException("Support for specifying 'L' with other days of the week is not implemented", -1);}if (exprOn == 5 && expr.indexOf(35) != -1 && expr.indexOf(35, expr.indexOf(35) + 1) != -1) {throw new ParseException("Support for specifying multiple \"nth\" days is not implemented.", -1);}StringTokenizer vTok = new StringTokenizer(expr, ",");while(vTok.hasMoreTokens()) {String v = vTok.nextToken();this.storeExpressionVals(0, v, exprOn);}}if (exprOn <= 5) {throw new ParseException("Unexpected end of expression.", expression.length());} else {if (exprOn <= 6) {this.storeExpressionVals(0, "*", 6);}TreeSet<Integer> dow = this.getSet(5);TreeSet<Integer> dom = this.getSet(3);boolean dayOfMSpec = !dom.contains(NO_SPEC);boolean dayOfWSpec = !dow.contains(NO_SPEC);if ((!dayOfMSpec || dayOfWSpec) && (!dayOfWSpec || dayOfMSpec)) {throw new ParseException("Support for specifying both a day-of-week AND a day-of-month parameter is not implemented.", 0);}}} catch (ParseException var8) {throw var8;} catch (Exception var9) {throw new ParseException("Illegal cron expression format (" + var9.toString() + ")", 0);}
}

2 使用

import com.google.common.collect.Lists;import java.util.List;
import java.util.StringTokenizer;/*** @author xiaoxu* @date 2023-10-18* spring_boot:com.xiaoxu.boot.tokenizer.TestStringTokenizer*/
public class TestStringTokenizer {public static void main(String[] args) {print("你 好 吗\t我是 \t你的\t 朋友 \t", " \t", false);}public static void print(String str, String delimiter, boolean isReturnDelims) {System.out.println("切割字符串:【" + str + "】;" + "分隔符:【" + delimiter + "】。");List<String> strs = Lists.newArrayList();String s;boolean x;for (StringTokenizer strToken = new StringTokenizer(str, delimiter, false); strToken.hasMoreTokens(); x = (s != null && strs.add(s))) {s = strToken.nextToken();System.out.println("切割:【" + s + "】");if(s.equals("吗"))s = null;}System.out.println("字符串数组:" + strs);}}

执行结果:

切割字符串:【你 好 吗	我是 	你的	 朋友 	】;分隔符:【 	】。
切割:【你】
切割:【好】
切割:【吗】
切割:【我是】
切割:【你的】
切割:【朋友】
字符串数组:[,, 我是, 你的, 朋友]

源码片段分析:

public StringTokenizer(String str, String delim, boolean returnDelims) {currentPosition = 0;newPosition = -1;delimsChanged = false;this.str = str;maxPosition = str.length();delimiters = delim;retDelims = returnDelims;setMaxDelimCodePoint();
}
private void setMaxDelimCodePoint() {if (delimiters == null) {maxDelimCodePoint = 0;return;}int m = 0;int c;int count = 0;for (int i = 0; i < delimiters.length(); i += Character.charCount(c)) {c = delimiters.charAt(i);if (c >= Character.MIN_HIGH_SURROGATE && c <= Character.MAX_LOW_SURROGATE) {c = delimiters.codePointAt(i);hasSurrogates = true;}if (m < c)m = c;count++;}maxDelimCodePoint = m;if (hasSurrogates) {delimiterCodePoints = new int[count];for (int i = 0, j = 0; i < count; i++, j += Character.charCount(c)) {c = delimiters.codePointAt(j);delimiterCodePoints[i] = c;}}
}

调用setMaxDelimCodePoint()方法,源码可知,切割时设置int maxDelimCodePoint,是为了优化分隔符的检测(取的是分隔字符串中char的ASCII码值最大的字符的ASCII值,存入maxDelimCodePoint中。在方法int scanToken(int startPos)中,若满足条件(c <= maxDelimCodePoint) && (delimiters.indexOf© >= 0),意即该字符的ASCII码值小于等于最大的maxDelimCodePoint,那么这个字符可能存在于分隔字符串中,再检测delimiters分隔字符串中是否包含该字符,反之,若ASCII码值大于分隔字符串中最大的maxDelimCodePoint,也就是说该字符一定不存在于分隔字符串里,&&直接跳过delimiters.indexOf的检测,也就达到了优化分隔符检测的效果了)。

private int scanToken(int startPos) {int position = startPos;while (position < maxPosition) {if (!hasSurrogates) {char c = str.charAt(position);if ((c <= maxDelimCodePoint) && (delimiters.indexOf(c) >= 0))break;position++;} else {int c = str.codePointAt(position);if ((c <= maxDelimCodePoint) && isDelimiter(c))break;position += Character.charCount(c);}}if (retDelims && (startPos == position)) {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))position += Character.charCount(c);}}return position;
}

scanToken方法即跳过分隔字符串,只要某此循环时,该字符包含在分隔字符串里,那么position不再自增,以此时的position值作为实际切割获取字符串的末索引, 因为subString方法是左闭右开的,该值是实际获取字符串的末索引值+1,所以可以截取到完整的不包含分隔符的字符串片段。

skipDelimiters方法类似,即过滤连续包含于分隔字符串中的字符,获取实际需要切割获取的字符串的开始索引值。

private int skipDelimiters(int startPos) {if (delimiters == null)throw new NullPointerException();int position = startPos;while (!retDelims && position < maxPosition) {if (!hasSurrogates) {char c = str.charAt(position);if ((c > maxDelimCodePoint) || (delimiters.indexOf(c) < 0))break;position++;} else {int c = str.codePointAt(position);if ((c > maxDelimCodePoint) || !isDelimiter(c)) {break;}position += Character.charCount(c);}}return position;
}

上述分析可知,只要待切割字符串中的字符,在分隔字符串中出现,那么就会做一次切割(也就是不论分隔字符串中的每个char或字符串片段的顺序,只要连续包含在分隔字符串里,就切割)。

演示如下(注意countTokens()方法不要在循环中和nextToken()一同使用):

public static void print2(String str, String delimiter, boolean isReturnDelims) {StringTokenizer strTokenizer = new StringTokenizer(str, delimiter);System.out.println("总数目:" + strTokenizer.countTokens());int count;String[] strs = new String[count = strTokenizer.countTokens()];// 注意:不要在循环里写 int i = 0; i < strTokenizer.countTokens();// 因为  countTokens方法需要使用currentPosition,而每次执行nextToken方法时,currentPosition会一直往下偏移计算,// 会导致循环中, i < strTokenizer.countTokens();发生改变,这里应该是常量总数目for (int i = 0; i < count; i++) {String s = strTokenizer.nextToken();strs[i] = s;}System.out.println(Arrays.toString(strs));
}

countTokens源码如下:

public int countTokens() {int count = 0;int currpos = currentPosition;while (currpos < maxPosition) {currpos = skipDelimiters(currpos);if (currpos >= maxPosition)break;currpos = scanToken(currpos);count++;}return count;
}

执行:

print2("1a2b3c4ca5bc6ba7abc8acbbaba9", "abc", false);

结果如下所示:

总数目:9
[1, 2, 3, 4, 5, 6, 7, 8, 9]

相关文章:

java:java.util.StringTokenizer实现字符串切割

java&#xff1a;java.util.StringTokenizer实现字符串切割 1 前言 java.util工具包提供了字符串切割的工具类StringTokenizer&#xff0c;Spring等常见框架的字符串工具类&#xff08;如Spring的StringUtils&#xff09;&#xff0c;常见此类使用。 例如Spring的StringUtil…...

IPV6 ND协议--源码解析【根源分析】

ND协议介绍 ND介绍请阅读上一篇文章&#xff1a;IPv6知识 - ND协议【一文通透】11.NDP协议分析与实践_router solicitation报文中不携带source link-layer address-CSDN博客 ND协议定义了5种ICMPv6报文类型&#xff0c;如下表所示&#xff1a; NS/NA报文主要用于地址解析RS/…...

Python学习笔记——存储容器

食用说明&#xff1a;本笔记适用于有一定编程基础的伙伴们。希望有助于各位&#xff01; 列表 列表类似数组&#xff0c;其中可以包含不同类型的元素&#xff0c;写法如下&#xff1a; list1 [Google, Runoob, 1997, 2000] list2 [1, 2, 3, 4, 5 ] list3 ["a", …...

Android DI框架-Hilt

到底该如何理解<依赖注入> 模版代码&#xff1a;食之无味&#xff0c;弃之可惜 public class MainActivity extends Activity {Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);TextView mTextView(TextView) findVi…...

基于寄生捕食优化的BP神经网络(分类应用) - 附代码

基于寄生捕食优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于寄生捕食优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.寄生捕食优化BP神经网络3.1 BP神经网络参数设置3.2 寄生捕食算法应用 4.测试结果…...

【Java常见的几种设计模式】

Java常见的几种设计模式 1. 单例模式&#xff08;Singleton Pattern&#xff09;2. 工厂模式&#xff08;Factory pattern&#xff09;3. 抽象工厂模式&#xff08;Abstract Factory Pattern&#xff09;4. 建造者模式&#xff08;Builder Pattern&#xff09;5. 原型模式&…...

jupyter崩溃进不去,报错module ‘mistune‘ has no attribute ‘BlockGrammar‘

是python包引起的问题 [E 2023-10-14 08:40:25.414 ServerApp] Uncaught exception GET /api/nbconvert?1697244025327 (127.0.0.1) HTTPServerRequest(protocol‘http’, host‘localhost:8090’, method‘GET’, uri‘/api/nbconvert?1697244025327’, version‘HTTP/1.1’…...

windows terminal鼠标右键打开

如果在官网上下载的是zip文件的 需要在注册表修改鼠标右键才能出来 注册表修改如下&#xff1a; 1.先windowsR&#xff0c;在命令框中输入regedit 打开注册表 2.在路径’计算机\HKEY_CLASSES_ROOT\directory\background\shell’下新建一个wt&#xff0c;wt下新建commond 这里…...

HTML5播放 M3U8的hls流地址

在HTML5页面上播放M3U8的hls流地址 <!DOCTYPE html> <html> <head> <meta charset"UTF-8"> <title>视频播放</title> <script src"https://cdn.jsdelivr.net/npm/hls.jslatest"></script> &…...

leetcode:101.对称二叉树

借用二叉树是否相同的代码改动左右孩子相等对应关系&#xff0c;即为是否对称。 /*** Definition for a binary tree node.* struct TreeNode {* int val;* struct TreeNode *left;* struct TreeNode *right;* };*/bool isSameTree(struct TreeNode* p, struct Tr…...

UI自动化的适用场景,怎么做?

经常有人会问&#xff0c;什么样的项目才适合进行UI自动化测试呢&#xff1f;UI自动化测试相当于模拟手工测试&#xff0c;通过程序去操作页面上的控件。而在实际测试过程中&#xff0c;经常会遇到无法找到控件&#xff0c;或者因控件定义变更而带来的维护成本等问题。 哪些场…...

SpringFramewrok (1)

1、框架的概念与理解 在现实生活中&#xff0c;框架可以比喻为我们搭建房子的框架。 在框架的基础上&#xff0c;我们可以专注于我们自己的工作&#xff0c;而不用在意这些底层工作如何实现。 框架的优点包括以下几点&#xff1a; 1. 提高开发效率&#xff1a;框架提供了许多…...

电商独立站小程序开发方案

随着移动互联网的迅速发展&#xff0c;电商行业也逐渐向小程序平台转移。开发一款电商小程序对于拓展销售渠道、提高用户体验、增加用户忠诚度等方面都有着重要的意义。本文将围绕电商小程序的开发背景、需求分析、技术选型、开发流程、风险控制、商业模式和市场前景等方面进行…...

数据库安全运维是什么意思?数据库安全运维系统用哪家好?

我们大家都直到数据在某些情况下容易丢失或被破坏&#xff0c;攻击者可能通过对数据库进行破坏或勒索等手段获取利益。所以保障数据库安全至关重要。今天我们就来聊聊数据库安全运维是什么意思&#xff1f;数据库安全运维系统用哪家好&#xff1f; 数据库安全运维是什么意思&…...

小程序的console中出现:。。。不在以下 request 合法域名列表中,请参考文档:。。。的报错解决

报错效果&#xff1a; 其实这个报错不代表自己的代码有问题 但是本强迫症研究了一下&#xff0c;按照以下方法关掉就不会显示这个报错了。 点微信开发者工具中的右上角的详情。点本地设置。勾选不校验。。。HTTPS证书。 即可关闭该报错&#xff1a;...

计算机网络基础(三):IPv4编址方式、子网划分、IPv4通信的建立与验证及ICMP协议

**IPv4地址是一个32位长的二进制数。**而这个32位二进制数又通常会表示为4个用点隔开的十进制数。那么&#xff0c;这个32位二进制数要如何通过4个十进制数表示出来呢&#xff1f; 我们在配置IPv4地址时&#xff0c;同时配置的“掩码”又有何用途&#xff1f; 1.IPv4编址方式…...

Error: GlobalConfigUtils setMetaData Fail Cause:java.lang.NullPointerException

文章目录 1、在开发中会出现这样的错误。2、其次&#xff0c;再看其他错误&#xff1a; 1、在开发中会出现这样的错误。 完整错误&#xff1a;Caused by: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: Error: GlobalConfigUtils setMetaData Fail ! Cause…...

OpenHarmony 应用全局的 UI 状态存储:AppStorage

AppStorage 是应用全局的 UI 状态存储&#xff0c;是和应用的进程绑定的&#xff0c;由 UI 框架在应用程序启动时创建&#xff0c;为应用程序 UI 状态属性提供中央存储。 和 AppStorage 不同的是&#xff0c;LocalStorage 是页面级的&#xff0c;通常应用于页面内的数据共享。而…...

外置告警蜂鸣器使用小坑

告警蜂鸣器调试小坑 昨天调试新产品&#xff0c;由于IMO、MSC组织和IEC标准规定&#xff0c;不能使用带红色指示灯的蜂鸣器&#xff0c;于是更换了个不带灯。然而奇怪的现象出现了两次短响的程序在有的页面正常&#xff0c;有的页面就变成一声了。搞了一天&#xff0c;把各种寄…...

SSO身份验证如何帮助加强密码安全性

单点登录 &#xff08;SSO&#xff09; 是一种身份验证服务&#xff0c;可帮助用户使用一组凭据快速安全地访问所有应用程序。在员工需要访问多个应用程序才能完成工作的企业环境中&#xff0c;每次需要访问时都必须为每个应用程序输入登录凭据&#xff0c;这是一个主要的烦恼来…...

轻量安全的密码管理工具Vaultwarden

一、Vaultwarden概述 Vaultwarden主要作用是提供一个自托管的密码管理器服务。它是Bitwarden密码管理器的第三方轻量版&#xff0c;由国外开发者在Bitwarden的基础上&#xff0c;采用Rust语言重写而成。 &#xff08;一&#xff09;Vaultwarden镜像的作用及特点 轻量级与高性…...

[10-1]I2C通信协议 江协科技学习笔记(17个知识点)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17...

claude3.7高阶玩法,生成系统架构图,国内直接使用

文章目录 零、前言一、操作指南操作指导 二、提示词模板三、实战图书管理系统通过4o模型生成系统描述通过claude3.7生成系统架构图svg代码转换成图片 在线考试系统通过4o模型生成系统描述通过claude3.7生成系统架构图svg代码转换成图片 四、感受 零、前言 现在很多AI大模型可以…...

dvwa11——XSS(Reflected)

LOW 分析源码&#xff1a;无过滤 和上一关一样&#xff0c;这一关在输入框内输入&#xff0c;成功回显 <script>alert(relee);</script> MEDIUM 分析源码&#xff0c;是把<script>替换成了空格&#xff0c;但没有禁用大写 改大写即可&#xff0c;注意函数…...

Linux实现线程同步的方式有哪些?

什么是线程同步&#xff1f; 想象一下超市收银台&#xff1a;如果所有顾客&#xff08;线程&#xff09;同时挤向同一个收银台&#xff08;共享资源&#xff09;&#xff0c;场面会一片混乱。线程同步就是给顾客们发"排队号码牌"&#xff0c;确保&#xff1a; 有序访…...

【自然语言处理】大模型时代的数据标注(主动学习)

文章目录 A 论文出处B 背景B.1 背景介绍B.2 问题提出B.3 创新点 C 模型结构D 实验设计E 个人总结 A 论文出处 论文题目&#xff1a;FreeAL: Towards Human-Free Active Learning in the Era of Large Language Models发表情况&#xff1a;2023-EMNLP作者单位&#xff1a;浙江大…...

基于微信小程序的作业管理系统源码数据库文档

作业管理系统 摘 要 随着社会的发展&#xff0c;社会的方方面面都在利用信息化时代的优势。互联网的优势和普及使得各种系统的开发成为必需。 本文以实际运用为开发背景&#xff0c;运用软件工程原理和开发方法&#xff0c;它主要是采用java语言技术和微信小程序来完成对系统的…...

spring中的@KafkaListener 注解详解

KafkaListener 是 Spring Kafka 提供的一个核心注解&#xff0c;用于标记一个方法作为 Kafka 消息的消费者。下面是对该注解的详细解析&#xff1a; 基本用法 KafkaListener(topics "myTopic", groupId "myGroup") public void listen(String message)…...

code-server安装使用,并配置frp反射域名访问

为什么使用 code-server是VSCode网页版开发软件&#xff0c;可以在浏览器访问编程&#xff0c;可以使用vscode中的插件。如果有自己的服务器&#xff0c;使用frp透传后&#xff0c;域名访问在线编程&#xff0c;使用方便&#xff0c;打开的服务端口不需要单独配置&#xff0c;可…...

Spring Boot + Thymeleaf 防重复提交

在 Spring Boot 与 Thymeleaf 结合的 Web 应用中&#xff0c;防止重复提交可以采用token 机制 客户端禁用按钮的方式实现&#xff0c;在高并发场景下&#xff0c;考虑使用 Redis 存储 token 而非 Session。 第一步&#xff1a;后端实现 Controller public class FormControl…...