`
javaworlds
  • 浏览: 26962 次
  • 性别: Icon_minigender_1
  • 来自: 北京
最近访客 更多访客>>
社区版块
存档分类
最新评论

改进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<aiIndex.length; 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.trim().endsWith("\"")) { 
sToken += sNextToken; 
sNextToken = oTokenizer.nextToken(); 

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()方法检查已经返回的标记数量是否小于标记的总数量。
分享到:
评论

相关推荐

    字符串转换为计算公式的若干种方法

    - **词法分析**:将输入的字符串分解成一系列有意义的符号或标记,如数字、操作符等。 - **语法分析**:根据定义的语法规则,检查并组织这些标记,构建出表达式的结构树,以便于后续的计算。 #### 2. **逆波兰...

    JavaUser-Agent解析库

    这个库提供了一种简单而有效的方式来分解和理解User-Agent字符串。它包含了对常见浏览器和设备的识别,帮助开发者快速识别用户所使用的设备类型(如移动设备、桌面电脑)、浏览器类型(如Chrome、Firefox)及其版本...

    Java虚拟机规范(Java SE 7).pdf

    4. **字符串优化**:在Java 7中,字符串拼接性能得到了提升,通过引入String的内置缓冲区,减少了创建新对象的次数,提高了字符串操作的效率。 5. **文件系统API更新**:NIO.2引入了新的文件系统API,提供了一种更...

    Java JDK 7学习笔记 PDF

    9. **改进的Switch语句**:在JDK 7中,switch语句可以接受字符串作为参数,使得代码更加简洁。 10. **集合改进**:对`Collections`类和`Arrays`类进行了扩展,增加了新的方法,如`copyOf`, `copyOfRange`等,方便...

    开发必备:Java_SDK_11

    5. **改进的字符串处理**:包括对`String`类的一些增强,如`strip()`、`stripLeading()`和`stripTrailing()`方法,这些方法在处理空白字符时更加灵活。 6. **ZGC:一个低延迟的垃圾收集器**:Java 11引入了Z ...

    04747 Java程序设计(一) 自考 考点 大纲(自己总结)

    - **StringTokenizer类**:用于分解字符串。 - `StringTokenizer(String s)`:构造方法接受一个字符串。 - `nextToken()`:返回下一个标记。 - `hasMoreTokens()`:判断是否还有更多的标记。 - `countTokens()`...

    java api1.6、1.7

    3. **字符串in switch语句**:可以直接在switch语句中使用字符串,简化了条件判断。 4. **try-with-resources**:自动关闭资源,如文件流和数据库连接,避免资源泄漏。 5. **改进的类型推断**:通过`@Functional...

    java JDK 1.7.0_80 x64

    此外,Java 7还引入了改进的异常处理、字符串改进(如in-place替换和空字符串检查)、垃圾收集器优化等。尽管Java 1.7.0_80已经是一个较旧的版本,但它仍然是许多遗留系统和项目的基础,理解其特性和配置对于维护...

    java_jdk1.7x64

    - **字符串in-place替换**:新增`String::replaceFirst`和`String::replaceAll`方法,可以在原字符串上进行替换操作,无需创建新对象。 - **try-with-resources**:这个语法糖使得资源管理更加方便,确保资源在...

    Java-JDK-7.rar_jdk7

    3. **字符串相关改进**:Java 7中对字符串进行了优化,比如新增`String.join()`方法用于高效地连接字符串数组,以及`split()`方法支持正则表达式,提高了处理字符串的灵活性。 4. **文件系统API**:NIO.2(New IO ...

    Java虚拟机规范(Java SE 7版).

    3. 字符串常量池:在Java SE 7中,字符串常量池被移至方法区,提高了字符串操作的效率。这使得字符串的比较和引用更加高效,减少了内存中的重复对象。 4. 多线程:Java SE 7提供更强大的并发工具,如Fork/Join框架...

    Java安装环境所需的JDK1.7

    5. **字符串增强**:添加了新的字符串方法,如`join()`, `split()`的正则表达式优化,以及`isBlank()`等。 6. **类型推断**:在局部变量声明和匿名类中,编译器能够自动推断出类型,简化了代码。 7. **NIO.2**:...

    java1.7版本API中文版最新

    3. **字符串API增强**:增加了`String.join()`方法,可以方便地将多个字符串用指定分隔符连接起来;还有`String.indexOf()`和`String.lastIndexOf()`的新重载版本,支持正则表达式查询。 4. **类型推断(Type ...

    JAVA经典算法收集整理 以及Java算法大全(近100种算法打包).rar

    "JAVA经典算法收集整理 以及Java算法大全(近100种算法打包)" 是一个宝贵的资源库,涵盖了各种基础到高级的算法,对于学习和提升Java编程能力具有极大的价值。 这个压缩包中的文件列表可能包括了各种算法的实现...

    Java 9 揭秘

    还优化了字符串拼接操作,提高了代码执行效率。 #### 六、命令行工具和工具链改进 为了提升开发者的体验,Java 9 对命令行工具和工具链进行了多项改进。例如,增加了新的 jshell 工具,该工具支持交互式开发模式,...

    12203060134刘纪玲java9.tgz

    `String`类增加了新的方法,如`repeat()`用于重复字符串,`strip()`和`stripIndent()`用于去除字符串两端的空白字符。 10. **并行Full GC(ZGC)**: 虽然不是Java 9的标准部分,但ZGC(Z Garbage Collector)是...

    java jdk1.7

    4. **字符串in switch**:在switch语句中可以直接使用字符串,使得基于字符串的条件判断更为方便。 5. **Fork/Join框架**:这是并发编程的一个重要改进,通过将大任务分解为小任务并行处理,提高了程序的执行效率。...

    Java 7编程高级进阶.zip

    3. **字符串inswitch**:在Java 7中,字符串可以直接用于switch语句,增强了switch语句的适用性。 4. **类型推断 for泛型方法的钻石操作符**:在创建匿名对象时,不再需要指定泛型的实际类型参数,编译器可以根据上...

    java帮助文档,java API

    5. **Strings in switch**:Java 7开始,switch语句可以直接处理字符串,这增强了代码的可读性和实用性。 6. **改进的数组初始化**:可以使用内联初始化语法创建多维数组,例如`int[][] arr = {{1, 2}, {3, 4}};` ...

    jdk1.7Java的开发环境

    2. **字符串连接优化**:通过`String`类的新的`concat()`方法,字符串连接性能得到提升,尤其在处理大量字符串连接时。 3. **Try-with-resources**:这是一个新的异常处理结构,确保资源在使用后被正确关闭,即使在...

Global site tag (gtag.js) - Google Analytics