我要求从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,
为什么我问这个问题:
答案 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);
}
关于你的问题:
StringTokenizer
是一个相对简单的类,可能不应该用于此。Pattern
关于^
和$
的课程,不情愿的量词,当然还有积极的外观和积极的前瞻。