【问题标题】:FindWithinHorizon Fails to MatchFindWithinHorizo​​n 无法匹配
【发布时间】:2013-10-15 15:34:05
【问题描述】:

我想在文本文件中查找“$$$$”模式的实例数。以下方法适用于某些文件,但不适用于所有文件。例如,它不适用于以下文件(http://www.hmdb.ca/downloads/structures.zip - 它是一个带有 .sdf 扩展名的压缩文本文件)我不知道为什么?我也试图逃避空格。没有运气。当“$$$$”模式超过 35000 个时,它返回 11。请注意,速度至关重要。因此,我不能使用任何较慢的方法。

public static void countMoleculesInSDF(String fileName)
{
    int tot = 0;
    Scanner scan = null;
    Pattern pat = Pattern.compile("\\$\\$\\$\\$");

    try {  
        File file = new File(fileName);
        scan = new Scanner(file);
        long start = System.nanoTime();
        while (scan.findWithinHorizon(pat, 0) != null) {
            tot++;
        }
        long dur = (System.nanoTime() - start) / 1000000;
        System.out.println("Results found: " + tot + " in " + dur + " msecs");
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        scan.close();
}
}

【问题讨论】:

    标签: java regex java.util.scanner


    【解决方案1】:

    对于链接文件和您发布的代码,我一直有218 匹配。这当然是不正确的:使用 notepad++ 的计数功能进行验证,该文件应包含 41498 匹配项。所以Scanner(我认为)肯定有问题,并在最后一场比赛完成时开始在其中调试,即当扫描仪告诉没有更多匹配时。这样做我在它的私有方法readInput() 中遇到了一个异常,它不是直接抛出而是保存在一个语言环境变量中。

    try {
        n = source.read(buf);
    } catch (IOException ioe) {
        lastException = ioe;
        n = -1;
    }
    

    这个异常可以使用Scanner#ioException()方法检索:

    IOException ioException = scanner.ioException();
    if (ioException != null) {
        ioException.printStackTrace();
    }
    

    打印这个异常然后显示some input could not be decoded

    java.nio.charset.UnmappableCharacterException: Input length = 1
        at java.nio.charset.CoderResult.throwException(CoderResult.java:278)
        at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:338)
        at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:177)
        at java.io.Reader.read(Reader.java:100)
        at java.util.Scanner.readInput(Scanner.java:849)
    

    所以我只是尝试将字符集传递给 Scanner 的构造函数:

    scan = new Scanner(file, "utf-8");
    

    它成功了!

    Results found: 41498 in 2431 msecs
    

    所以问题是 Scanner 使用了平台的字符集,不适合完全解码您拥有的文件。

    故事的寓意:

    1. 在处理文本时始终显式传递字符集。
    2. 在使用Scanner 时检查IOException

    PS:一些方便的方法来引用字符串以用作正则表达式

    Pattern pat = Pattern.compile("\\Q$$$$\\E");
    

    Pattern pat = Pattern.compile(Pattern.quote("$$$$"));
    

    【讨论】:

      【解决方案2】:

      这就是我最终要做的...(在您发布答案之前)。这种方法似乎比扫描仪更快。你会建议什么实施?扫描仪还是内存映射?大文件的内存映射会失败吗?不确定..

      private static final Charset CHARSET = Charset.forName("ISO-8859-15");
      private static final CharsetDecoder DECODER = CHARSET.newDecoder();
      
      public static int getNoOfMoleculesInSDF(String fileName) 
          {   
          int total=0;
          try
          {    
          Pattern endOfMoleculePattern = Pattern.compile("\\$\\$\\$\\$");
          FileInputStream fis = new FileInputStream(fileName);
          FileChannel fc = fis.getChannel();
          int fileSize = (int) fc.size();
          MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fileSize);
          CharBuffer cb = DECODER.decode(mbb);
          Matcher matcher = endOfMoleculePattern.matcher(cb);
          while (matcher.find()) {
            total++;
          }
          }
          catch(Exception e)
          {
              LOGGER.error("An error occured while counting molecules in the SD file");
          }
          return total;
          }
      

      【讨论】:

      • 这种方法看起来也不错,但不幸的是它不适用于大文件,例如您链接的文件(~ 250MB)。它会因OutOfMemoryError: Java heap space 而崩溃,因为DECODER.decode(mbb) 试图分配一个与文件本身一样大的字符缓冲区,即使使用-Xmx 选项增加jvm 堆空间也无法避免。我之前尝试过的是使用缓冲阅读器并在每一行上应用模式,它工作得很好,但比扫描仪花费了 4 倍的时间。我认为 Scanner 方法是在运行时避免 OOME 的最佳选择。 Scanner的缓冲区只有1024!
      • 请参阅question 的答案,以了解为什么尽管设置了-Xmx,但仍然会发生OOME
      • 此方法确实适用于 -Xms2000m。它要快得多 - 600 毫秒,而同一文件的 1900 毫秒。但是,有限的内存可能会成为一个问题。我要去扫描仪..
      • 哇,内存太大了!我只上升到 -Xmx1G 没有运气,之后我的系统甚至无法为 jvm 本身分配内存。事实上,您不能依赖这样一个事实,即您的应用程序总是可以拥有这么多内存,而且如果文件更大,那么您最终将需要更多内存!您仍然可以通过增加其缓冲区大小来加速扫描仪,不幸的是该成员是 finalprivate,但 with reflection almost everything is possible ;-)
      猜你喜欢
      • 2017-08-04
      • 2010-10-15
      • 1970-01-01
      • 2014-11-15
      • 1970-01-01
      • 2013-01-04
      • 1970-01-01
      • 2013-07-15
      • 2013-12-02
      相关资源
      最近更新 更多