当前位置: 首页 > 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;这是一个主要的烦恼来…...

Linux链表操作全解析

Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表&#xff1f;1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现

摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序&#xff0c;以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务&#xff0c;提供稳定高效的数据处理与业务逻辑支持&#xff1b;利用 uniapp 实现跨平台前…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互

引擎版本&#xff1a; 3.8.1 语言&#xff1a; JavaScript/TypeScript、C、Java 环境&#xff1a;Window 参考&#xff1a;Java原生反射机制 您好&#xff0c;我是鹤九日&#xff01; 回顾 在上篇文章中&#xff1a;CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...

uniapp微信小程序视频实时流+pc端预览方案

方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度​WebSocket图片帧​定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐​RTMP推流​TRTC/即构SDK推流❌ 付费方案 &#xff08;部分有免费额度&#x…...

rnn判断string中第一次出现a的下标

# coding:utf8 import torch import torch.nn as nn import numpy as np import random import json""" 基于pytorch的网络编写 实现一个RNN网络完成多分类任务 判断字符 a 第一次出现在字符串中的位置 """class TorchModel(nn.Module):def __in…...

华为OD机考-机房布局

import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...

【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案

目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后&#xff0c;迭代器会失效&#xff0c;因为顺序迭代器在内存中是连续存储的&#xff0c;元素删除后&#xff0c;后续元素会前移。 但一些场景中&#xff0c;我们又需要在执行删除操作…...

Oracle11g安装包

Oracle 11g安装包 适用于windows系统&#xff0c;64位 下载路径 oracle 11g 安装包...

Spring Security 认证流程——补充

一、认证流程概述 Spring Security 的认证流程基于 过滤器链&#xff08;Filter Chain&#xff09;&#xff0c;核心组件包括 UsernamePasswordAuthenticationFilter、AuthenticationManager、UserDetailsService 等。整个流程可分为以下步骤&#xff1a; 用户提交登录请求拦…...

Elastic 获得 AWS 教育 ISV 合作伙伴资质,进一步增强教育解决方案产品组合

作者&#xff1a;来自 Elastic Udayasimha Theepireddy (Uday), Brian Bergholm, Marianna Jonsdottir 通过搜索 AI 和云创新推动教育领域的数字化转型。 我们非常高兴地宣布&#xff0c;Elastic 已获得 AWS 教育 ISV 合作伙伴资质。这一重要认证表明&#xff0c;Elastic 作为 …...