首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在Java中使用制表符分隔符标记字符串,同时跳过一些标记

在Java中使用制表符分隔符标记字符串,同时跳过一些标记
EN

Stack Overflow用户
提问于 2012-10-14 04:41:04
回答 5查看 10.7K关注 0票数 3

我有一个包含数据的大型文件(~8 8Gb/~8000万条记录)。每条记录有6-8个属性,这些属性由单个制表符拆分。我想让初学者将一些给定的属性复制到另一个文件中。所以我想要一个比上面更优雅的代码,例如,如果我只想要总共4个令牌中的第二个也是最后一个令牌:

代码语言:javascript
复制
StringTokenizer st = new StringTokenizer(line, "\t");
st.nextToken(); //get rid of the first token
System.out.println(st.nextToken()); //show me the second token
st.nextToken(); //get rid of the third token
System.out.println(st.nextToken()); //show me the fourth token

我要提醒的是,这是一个很大的文件,所以我必须避免任何多余的if检查。

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2012-10-14 07:17:47

你的问题让我对性能产生了疑问。最近,我尽可能地使用Guava的Splitter,只是因为我喜欢它的语法。我从未测量过性能,所以我对四种解析风格进行了快速测试。我把这些放在一起真的很快,所以请原谅在风格和边缘情况正确性方面的错误。他们是基于这样的理解,即我们只对第二和第四项感兴趣。

我发现有趣的是,当解析一个350MB的制表符分隔的文本文件(有四列)时,"homeGrown“(真正粗糙的代码)解决方案是最快的,例如:

代码语言:javascript
复制
head test.txt 
0   0   0   0
1   2   3   4
2   4   6   8
3   6   9   12

当在我的笔记本电脑上运行超过350MB的数据时,我得到了以下结果:

  • 自行开发: 2271ms
  • guavaSplit: 3367ms
  • regex: 7302ms
  • tokenize: 3466ms

鉴于此,我认为在大多数工作中我将坚持使用Guava的拆分器,并考虑为更大的数据集编写自定义代码。

代码语言:javascript
复制
  public static List<String> tokenize(String line){
    List<String> result = Lists.newArrayList();
    StringTokenizer st = new StringTokenizer(line, "\t");
    st.nextToken(); //get rid of the first token
    result.add(st.nextToken()); //show me the second token
    st.nextToken(); //get rid of the third token
    result.add(st.nextToken()); //show me the fourth token
    return result;
  }

  static final Splitter splitter = Splitter.on('\t');
  public static List<String> guavaSplit(String line){
    List<String> result = Lists.newArrayList();
    int i=0;
    for(String str : splitter.split(line)){
      if(i==1 || i==3){
        result.add(str);
      }
      i++;
    }
    return result;
  }

  static final Pattern p = Pattern.compile("^(.*?)\\t(.*?)\\t(.*?)\\t(.*)$");
  public static List<String> regex(String line){
    List<String> result = null;
    Matcher m = p.matcher(line);
    if(m.find()){
      if(m.groupCount()>=4){
        result= Lists.newArrayList(m.group(2),m.group(4));
      }
    }
    return result;
  }

  public static List<String> homeGrown(String line){
    List<String> result = Lists.newArrayList();
    String subStr = line;
    int cnt = -1;
    int indx = subStr.indexOf('\t');
    while(++cnt < 4 && indx != -1){
      if(cnt==1||cnt==3){
        result.add(subStr.substring(0,indx));
      }
      subStr = subStr.substring(indx+1);
      indx = subStr.indexOf('\t');
    }
    if(cnt==1||cnt==3){
      result.add(subStr);
    }
    return result;
  }

请注意,如果有适当的边界检查和更优雅的实现,所有这些都可能会更慢。

票数 3
EN

Stack Overflow用户

发布于 2012-10-14 05:00:59

正如Paul Tomblin所说,您可能应该使用unix cut实用程序。

但是,在Java中,您还可以尝试:

代码语言:javascript
复制
String[] fields = line.split("\t");
System.out.println(fields[1]+" "+fields[3]);

这是否更“优雅”还是个见仁见智的问题。我不知道它在大文件上是否更快-你需要在你的系统上对它进行基准测试。

相对性能还取决于每行有多少个字段,以及您想要哪些字段;split()将一次处理整个行,但StringTokenizer将递增地处理该行(例如,如果您只需要20个域中的2个和4个,这是很好的)。

票数 0
EN

Stack Overflow用户

发布于 2012-10-14 05:01:51

虽然你的数据文件很大,但听起来你的问题更多的是关于如何方便地访问一行文本中的项目,其中这些项目用制表符分隔。我认为对于如此简单的格式,StringTokenizer过于夸张了。

我将使用某种类型的“拆分”来将行转换为标记数组。与String.split相比,我更喜欢commons-lang中的StringUtils split,尤其是在不需要正则表达式的情况下。因为制表符是“空白”,所以您可以使用默认的拆分方法,而不指定分隔符:

代码语言:javascript
复制
String [] items = StringUtils.split(line);
if (items != null && items.length > 6)
{
    System.out.println("Second: " + items[1]  + "; Fourth: " + items[3]);
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/12876801

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档