java - 在跳过某些标记的同时在 Java 中使用制表符分隔符对字符串进行标记

标签 java tokenize stringtokenizer

我有一个包含数据的巨大文件(~8Gb/~8000 万条记录)。每条记录有 6-8 个属性,由单个选项卡分隔。我想让初学者在另一个文件中复制一些给定的属性。所以我想要一个比上面更优雅的代码,例如,如果我只想要总共 4 个中的第二个和最后一个标记:

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 检查。

最佳答案

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

我发现有趣的是,“自制”(非常粗糙的代码)解决方案在解析 350MB 制表符分隔的文本文件(有四列)时是最快的,例如:

head test.txt 
0   0   0   0
1   2   3   4
2   4   6   8
3   6   9   12

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

  • 自主开发:2271 毫秒
  • guavaSplit:3367 毫秒
  • 正则表达式:7302ms
  • 标记化:3466 毫秒

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

  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;
  }

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

关于java - 在跳过某些标记的同时在 Java 中使用制表符分隔符对字符串进行标记,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12876801/

相关文章:

java - 字符串分词

java - 标记化字节数组

java - 打印 int 类型的变量(不包括函数)

java - 无法加载包装器属性,属性文件包含xml中的代码

java - 当 float 除以零时,我可以强制java抛出错误吗?

lucene - ElasticSearch 词干提取

java - 我需要将 java 代码的字符串标记为其 block

Java StringTokenizer,空的空标记

java - 使用二进制信号量

java - 为什么在 Java 8 中对方法参数进行未经检查的转换时,返回类型的泛型会被删除?