标记字符串的问题

时间:2015-02-20 14:09:45

标签: java stringtokenizer string-split

我要求从PSV文件中读取数据,该文件包含至少100K记录并将每一行映射到DTO对象。

例如我有一个管道分隔字符串SampleData|1111|9130|23||1257|2014-04-01 18:00:00|2014-04-12 09:00:00||Software Developer|20|Vikas||PATIL 根据DTO值解析和提取每个标记。

我从String Tokenizer开始,它给了我正确的结果,直到我收到一个上面的字符串作为输入。

关于此字符串的特殊之处在于,在几个Pipes之间没有任何字符,例如|23||1257|Vikas||PATIL

当我尝试使用tokenizer拆分时,它给了我比我预期更少的令牌。它只是忽略了空字符,结果是 我将1257值赋给phoneNumber,将InsertDaate值赋给regionCode。

我应该分配值,让我们说sampleData到dto字段dataType,1111到recordID ..和''phone Number,因为输入数据没有phone number的数据但是在23标记生成器读取下一个标记为1257后,我将错误的值1257分配给phonenumber字段。

感谢上帝,我在测试环境中意识到了这个错误。

我尝试了几个选项,最后用String.split()方法解决了这个问题。

import java.util.StringTokenizer;

public class TestSpitingOfString {
    public static void main(String args[]) throws Exception {
        //DTO  dataType|recordID|employeeid|deptID|phoneNumber|regionCode|InsertDate|StartDate|hobby|designation|age|firstName|middleName|lastName
        String str = "SampleData|1111|9130|23||1257|2014-04-01 18:00:00|2014-04-12 09:00:00||Software Developer|20|Vikas||PATIL";

        System.out.println("Original String -> "+str);

        StringTokenizer tokenizer= new StringTokenizer(str,"|");// skips empty values between tokens
        System.out.println("Words With StringTokenizer ");
        while(tokenizer.hasMoreElements()){
            System.out.print(tokenizer.nextToken()+",");
        }
        System.out.println();

        String distributedWithPipe[] =str.split("|");// disaster :(  it splitted every character
        System.out.println("Words With String.split() distributedWithPipe character ->");
        for(String split : distributedWithPipe){
            System.out.print(split+",");
        }

        System.out.println();
        String distributedWithEscapedPipe[] =str.split("\\|"); // This worked for me
        System.out.println("Words With String.split() distributedWithEscapedPipe ->");
        for(String split : distributedWithEscapedPipe){
            System.out.print(split+",");
        }

    }
}

当我运行这个时,我得到输出(我保留,每个令牌之间只是为了理解目的):

Original String -> SampleData|1111|9130|23||1257|2014-04-01 18:00:00|2014-04-12 09:00:00||Software Developer|20|Vikas||PATIL

Words With StringTokenizer

SampleData,1111,9130,23,1257,2014-04-01 18:00:00,2014-04-12 09:00:00,Software Developer,20,Vikas,PATIL,

Words With String.split() distributedWithPipe character ->

,S,a,m,p,l,e,D,a,t,a,|,1,1,1,1,|,9,1,3,0,|,2,3,|,|,1,2,5,7,|,2,0,1,4,-,0,4,-,0,1, ,1,8,:,0,0,:,0,0,|,2,0,1,4,-,0,4,-,1,2, ,0,9,:,0,0,:,0,0,|,|,S,o,f,t,w,a,r,e, ,D,e,v,e,l,o,p,e,r,|,2,0,|,V,i,k,a,s,|,|,P,A,T,I,L,

Words With String.split() distributedWithEscapedPipe ->

SampleData,1111,9130,23,,1257,2014-04-01 18:00:00,2014-04-12 09:00:00,,Software Developer,20,Vikas,,PATIL,

为什么我问这个问题:

  1. 如果有人知道如何使用StringTokenizer我们可以解决这个问题,我很乐意学习它。否则我们可以说它是StringTokenizer的限制。
  2. 如果有人有同样的问题,那么可以使用备用解决方案,无需浪费时间来找出解决方案。
  3. 另外要强调的是,随着StringTokenizer的习惯,我们可能倾向于使用" |"管道(没有转义字符)作为分隔符和String.split()将不会产生预期的输出。

2 个答案:

答案 0 :(得分:3)

StringTokenizer在javadoc中声明了这种行为(尽管我承认它可能更清楚,取决于你如何解释"连续字符"):

  

StringTokenizer的一个实例的行为有两种,具体取决于   是否使用带有值的returnDelims标志创建它   是或否:

     
      
  • 如果标志为false,则分隔符用于分隔标记。 令牌是连续字符的最大序列,不是分隔符

  •   
  • 如果该标志为true,则分隔符字符本身被视为标记。因此,令牌是一个分隔符   字符,或连续字符的最大序列   不是分隔符。

  •   

阅读this bug in JDK Bug Database(或this one)的评论:

  

StringTokenizer将令牌定义为连续的最大序列   不是分隔符的字符。因此,子串中没有令牌" ,,"。

然后你可以使用构造函数StringTokenizer(String str, String delim, true),但要注意这会将分隔符作为每个标记的一部分返回,因此你需要自己删除它们,这是一个很大的负担。

出于所有这些原因,最好只使用String.split

答案 1 :(得分:1)

最好使用String.split()和正则表达式(你需要指出|是一个字符,而不是逻辑OR!):

String str = "SampleData|1111|9130|23||1257|2014-04-01 18:00:00|2014-04-12 09:00:00||Software Developer|20|Vikas||PATIL";
String[] tokens = str.split("[|]");
for (String token : tokens) {
    // or do something else...
    System.out.println(token);
}

或者,对于包含大量分隔符的字符串来说,复杂得多但效率更高:

String str = "SampleData|1111|9130|23||1257|2014-04-01 18:00:00|2014-04-12 09:00:00||Software Developer|20|Vikas||PATIL";
// start or '|', then anything (reluctant) then '|' or end
Matcher m = Pattern.compile("(?<=^|[|]).*?(?=[|]|$)").matcher(str);
while (m.find()) {
    // or do something else...
    String token = m.group();
    System.out.println(token);
}

关于你的问题:

  1. StringTokenizer是一个相对简单的类,可能不应该用于此。
  2. 我没有遇到这个问题,但有时候测试我的正则表达式技能会有所回报,这个解决方案应该可行。请参阅Pattern关于^$的课程,不情愿的量词,当然还有积极的外观和积极的前瞻。
  3. 考虑突出显示:)