改进JAVA字符串分解的方法

一、概述

大多数Java程序员都曾经使用过java.util.StringTokenizer类。它是一个很方便的字符串分解器,主要用来根据分隔符把字符串分割成标记(Token),然后按照请求返回各个标记。这个过程称为Tokenization,实际上就是把字符序列转换成应用程序能够理解的多个标记。

虽然StringTokenizer用起来很方便,但它的功能却很有限。这个类只是简单地在输入字符串中查找分隔符,一旦找到了分隔符就分割字符串。它不会检查分隔符是否在子串之中这类条件,当输入字符串中出现两个连续的分隔符时,它也不会返回""(字符串长度为0)形式的标记。

为了突破这些局限,Java 2平台提供了BreakIterator类,它是在StringTokenizer之上改进的字符串分解器。由于JDK 1.1.x没有提供这个类,为了满足自己的需要,开发者经常花费很多时间从头开始编写分解器。在涉及到数据格式化处理的大型工程中,这类定制的字符串分解器有时随处可见,而且这种情况并不罕见。

本文的目标是帮助你利用现有的StringTokenizer类,编写一个高级字符串分解器。

二、StringTokenizer的局限

你可以用以下三种构造函数中的任意一种创建StringTokenizer分解器:

StringTokenizer(String sInput):以空白字符(“ ”,“\t”,“\n”)为分隔符分割字符串。

StringTokenizer(String sInput, String sDelimiter):以sDelimiter为分隔符分割字符串。

StringTokenizer(String sInput, String sDelimiter, boolean bReturnTokens):以sDelimiter为分隔符分割字符串,但如果bReturnTokens为true,则分隔符也作为标记返回。

第一个构造函数不检查输入字符串是否包含子串。例如,如果以空白字符为分隔符分割“hello. Today \"I am \" going to my home town”,则字符串分解结果是hello.、Today、"I、am、"、going等,而不是hello.、Today、"I am "、going等。

第二个构造函数不检查两个分隔符连续出现的情况。例如,如果以“,”为分隔符分割“book, author, publication,,,date published”这个字符串,则StringTokenizer返回book、author、publication和date published这四个标记,而不是book、author、publication、""、""和date published这6个标记(其中""表示0长度字符串)。要得到6个标记的答案,你必须把StringTokenizer的bReturnTokens参数设置为true。

允许设置值为true的bReturnTokens参数是一个重要的功能,因为它考虑到了分隔符连续出现的情况。例如,使用第二个构造函数时,如果数据是动态收集得到而且要用来更新数据库中的表,输入字符串中的标记对应着表里面列的值,那么当我们不能确定哪一个列应该设置为""时,我们就无法把输入串中的标记映射到数据库列。假设我们要把记录插入到一个有6个列的表,而输入数据中包含两个连续的分隔符。此时,StringTokenizer的分解结果是5个标记(两个连续的分隔符代表""标记,它将被StringTokenizer忽略),而我们却有6个字段需要设置。同时,我们也不知道连续分隔符在哪里出现,所以也就不知道哪一个列应该设置成""。

当标记本身等同于分隔符(无论是长度还是值)且位于子串之内时,第三个构造函数无效。例如,如果我们要以“,”为分隔符分解字符串“book, author, publication,\",\",date published”(这个字符串包含一个“,”标记,它与分隔符一样),结果是book、author、publication、"、"、date published这六个标记,而不是book、author、publication、,(逗号字符)、date published这五个标记。再提醒一下,即使我们把StringTokenizer的bReturnTokens参数设置设置成了true,在这种情况下也没有什么帮助。

三、高级字符串分解器

在编写代码之前,你必须搞清楚一个好的分解器有哪些基本要求。因为Java开发者已经习惯于使用StringTokenizer类,所以一个好的分解器应该提供StringTokenizer类提供的所有实用方法,比如hasMoreTokens()、nextToken()、countTokens()。

本文提供的代码很简单,而且大部分代码足以自我解释。在这里,我主要利用了StringTokenizer类(创建类实例时bReturnTokens参数设置为true),并提供了上面提到的几个方法。大多数时候标记与分隔符不同,有些时候分隔符却要作为标记输出(尽管非常罕见),此时如果出现了对标记的请求,分解器要把分隔符作为标记输出。创建PowerfulTokenizer对象时,你只需要提供输入字符串和分隔符这两个参数,PowerfulTokenizer将在内部使用bReturnTokens设置成true的StringTokenizer。(这么做的原因在于,如果不是用bReturnTokens设置成true的方式创建StringTokenizer,那么它将在解决先前提出的问题时受到限制)。为了正确地控制分解器,代码在几个地方(计算标记的总数量以及nextToken())检查bReturnTokens是否设置成了true。

你可能已经发现,PowerfulTokenizer实现了Enumeration接口,从而也就实现了hasMoreElements()和nextElement()这两个方法,而这两个方法又分别把调用直接委托给hasMoreTokens()和nextToken()。(由于实现了Enumeration接口,PowerfulTokenizer实现了与StringTokenizer的向后兼容。)

我们来看一个例子,假设输入字符串是“hello, Today,,, \"I, am \", going to,,, \"buy, a, book\"”,分隔符是“,”。用分解器分割这个字符串时返回结果如表1所示:

表1:字符串分解结果

输入字符串包含11个逗号(,)字符,其中3个在子串里面、4个连续出现(“Today,,,”中包含两个连续逗号,第一个逗号是Today的分隔符)。下面是PowerfulTokenizer计算标记总数的算法:

如果bReturnTokens=true,把子串中的分隔符数量乘以2,再从实际总数量减去该数字,就得到了标记的总数。理由是,对于子串“buy, a, book”,StringTokenizer将返回5个标记(即“buy:,:a:,:book”),而PowerfulTokenizer将返回一个标记(即“buy, a, book”),两者的差值是4(即,2乘以子串中的分隔符数量)。这个公式对于所有包含分隔符的子串都有效。

类似地,对于bReturnTokens=false的情形,我们从实际总数(19)减去表达式[分隔符总数(11)- 连续分隔符数量(4) + 子串中的分隔符数量(3)]。由于这时我们不返回分隔符,它们(非连续出现或在子串内部)对我们来说没有用,上面的公式为我们返回了标记的总数量(9)。

请记住这两个公式,它们是PowerfulTokenizer的核心。这两个公式适用于几乎所有它们各自条件下的情形。但是,如果你有更复杂的要求,不能使用这两个公式,那么你应该在编写代码之前分析各种可能出现的情况,并设计出自己的公式。

// 检查分隔符是否位于子串之内

for (int i=1; i/td>

{

iIndex = sInput.indexOf(sDelim, iIndex+1);

if (iIndex == -1)

break;

// 如果分隔符位于子串之内,则向前分析直至子串结束

while (sInput.substring(iIndex-iLen, iIndex).equals(sDelim))

{

iNextIndex = sInput.indexOf(sDelim, iIndex+1);

if (iNextIndex == -1)

break;

iIndex = iNextIndex;

}

aiIndex[i] = iIndex;

//System.out.println("aiIndex[" + i + "] = " + iIndex);

if (isWithinQuotes(iIndex))

{

if (bIncludeDelim)

iTokens -= 2;

else

iTokens -= 1;

}

}

countTokens()方法检查子串是否包含双引号。如果包含,那么它减少总数并把索引值修改为字符串中下一个双引号出现的位置(如上面的代码片断所示)。如果bReturnTokens是false,那么它从总数减去输入字符串中出现的非连续分隔符的数量。

// 如发现多个连续的分隔符,则返回""作为标记

if ( (sPrevToken.equals(sDelim)) && (sToken.equals(sDelim)) ) {

sPrevToken = sToken;

iTokenNo++;

return "";

}

// 检查标记本身是否等于分隔符

if ( (sToken.trim().startsWith("\"")) && (sToken.length() == 1) ) {

// 标记本身等于分隔符的特殊情况

String sNextToken = oTokenizer.nextToken();

while (!sNextToken

sToken += sNextToken;

sPrevToken = sToken;

iTokenNo++;

return sToken.substring(1, sToken.length()-1);

}

// 检查字符串中是否包含子串

else if ( (sToken.trim().startsWith("\""))

&& (!((sToken.trim().endsWith("\""))

&& (!sToken.trim().endsWith("\"\"")))) )

{

if (oTokenizer.hasMoreTokens())

{

String sNextToken = oTokenizer.nextToken();

// 检查"\"\""

while (!((sNextToken.trim().endsWith("\""))

&& (!sNextToken.trim().endsWith("\"\""))) )

{

sToken += sNextToken;

if (!oTokenizer.hasMoreTokens())

{

sNextToken = "";

break;

}

sNextToken = oTokenizer.nextToken();

}

sToken += sNextToken;

}

}

nextToken()方法通过StringTokenizer.nextToken方法获取标记,并检查标记中的双引号字符。如果发现了这些字符,它继续获取标记直至不能再找到带有双引号的标记。另外,它还把标记保存到一个变量(sPrevToken,参见本文后面完整的源代码)以检查连续出现的分隔符。如果nextToken()发现等同于分隔符的连续多个标记,那么它返回""(长度为0的字符串)作为标记。

按照类似的方法,hasMoreTokens()方法检查已经返回的标记数量是否小于标记的总数量。

【结束语】本文为你介绍了如何轻松地编写一个强大的字符串分解器。根据本文介绍的原理,你能够迅速编写出复杂的字符串分解器,节省大量的开发时间

java字符串逐个分解_改进JAVA字符串分解的方法相关推荐

  1. java项目----教务管理系统_基于Java的教务管理系统

    java项目----教务管理系统_基于Java的教务管理系统 2022-04-22 18:18·java基础 最近为客户开发了一套学校用教务管理系统,主要实现学生.课程.老师.选课等相关的信息化管理功 ...

  2. java去掉字符串的空格_如何去掉字符串中的空格?

    问题阐述 去掉字符串首尾的空格非常简单,使用trim()方法即可实现,但是如果要去掉字符串中的空格,应该怎么实现呢? 专家解答 Java 中没有提供直接去除字符串中空格的方法,要想去除字符串中的空格, ...

  3. java 字符串输出字符_输出java字符串

    1-1 字符串排序 用Java编写一个能对一组字符串按字典序升序排序的程序 输入为N和N行字符串,需要按行输出字符串升序排序的结果 如输入 3 Abc Abe Abd 输出: Abc Abd Abe ...

  4. java获取字符串最长_基于java查找最长字符串代码实例

    题目:编写程序,针对输入的N个字符串,输出其中最长的字符串 输入样例: 4 zhaoming zhangming sunming liming 输出样例: 最长的字符串:zhangming 代码如下 ...

  5. java string最大长度_一个Java字符串中到底有多少个字符?

    作者:鸟窝 依照Java的文档, Java中的字符内部是以UTF-16编码方式表示的,最小值是 (0),最大值是(65535), 也就是一个字符以2个字节来表示,难道Java最多只能表示 65535个 ...

  6. java字符串底层实现_「JAVA」细述合理创建字符串,分析字符串的底层存储,你不该错过...

    Java基础之字符串操作--String 字符串 什么是字符串?如果直接按照字面意思来理解就是多个字符连接起来组合成的字符序列.为了更好的理解以上的理论,我们先来解释下字符序列,字符序列:把多个字符按 ...

  7. java jcombobox 获取值_从java中的JComboBox获取字符串值

    我正在做一个testJComboBox程序.一旦我选择了jCombobox的输出,我就会得到我需要的字符串值.但是,它不起作用. 这是我的代码: import java.awt.*; import j ...

  8. java 查找大写字母_在Java中查找字符串的所有大写字母

    因此,我试图在用户输入的字符串中查找所有大写字母,但始终出现此运行时错误: Exception in thread "main" java.lang.StringIndexOutO ...

  9. java字符串转日期_在java中如何将一个字符串转换为日期?

    小编典典 在特定情况下," 2010年1月2日"作为输入字符串: "一月"是全文月份,因此请使用MMMM模式 " 2"是较短的月份,因此请使 ...

最新文章

  1. JAVA中LinkedLockingQueue的简单使用
  2. python处理excel表格实例-python读写Excel表格的实例代码
  3. 二十、SPI设备驱动及应用(一)
  4. python找出只出现一次的数字_Python数组中只出现一次的数字
  5. 【tensorFlow】——图像数据增强、读取图像、保存图像
  6. 浏览器原理(组成、引擎、渲染机制)
  7. 抖音电商“双11”:品质国货和地方农特产成亮点
  8. centos 6.8 挂载NTFS移动硬盘
  9. 撸一个vue的双向绑定
  10. shell脚本:判断本地和远程文件是否存在
  11. java包含某个字符串_JavaScript判断一个字符串是否包含指定子字符串的方法
  12. 这次是16.7亿元!新能源汽车骗补何时休?
  13. 数据挖掘问题进行细分,主要分为哪四类问题
  14. 投资:螺纹钢研究框架
  15. C语言——if-else语句嵌套
  16. 程序设计基础java_Java程序设计基础
  17. [原创]反汇编之一:和Taskmgr过不去篇(无厘头版)
  18. CH340G常见故障的处理
  19. Windows 7个性化配置,关闭Win7动画效果,设置窗口背景为“ 豆绿色”,移动“我的文档”...
  20. oracle安装界面空白,在windows 2012中安装oracle 12c R2界面空白挂起无响应CPU达到100%的问题...

热门文章

  1. DataReader方式 获取数据
  2. python flask 设置个性化的响应体 响应头 状态码
  3. docker 镜像名 tag 为none 的解决方案
  4. linux 杀软 clamav 帮助选项翻译
  5. python3 环境变量
  6. python3 bytes和bytearray总结
  7. Windows内核新手上路3——挂钩KeUserModeCallBack
  8. 一个程序员的成长的六个阶段(转载)
  9. Paste模块的世界
  10. matlab 定义string_MATLAB开发自动驾驶第十课-创建用于加载自定义地面真值数据源的类...