2013-10-08 36 views
3

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

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

回答

3

对于链接的文件和你的代码,你已经贴吧,我一共218比赛不断了。这当然是不正确的:使用记事本++的计数函数进行验证,该文件应该包含41498匹配。所以,在最后一场比赛结束时,即当Scanner告诉我们没有更多的比赛结果时,Scanner(我认为)出现了问题,并开始调试。这样做我遇到了一个例外,它的私有方法readInput()不是直接抛出,而是保存在一个locale变量中。

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) 

所以我只是尝试,并通过一个字符集的扫描仪的构造函数:

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

它使它工作!

Results found: 41498 in 2431 msecs 

所以问题是扫描仪使用了平台的字符集,它不适合完全解码你的文件。

这个故事告诉我们:

  1. 文本时,务必明确地传递一个字符集。
  2. Scanner一起使用时请检查IOException

PS:有些得心应手的方式来引用一个字符串作为使用正则表达式

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

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

非常感谢您的努力。优秀的答案。 – lochi

+0

@lochi不客气! – A4L

0

这里就是我终于实现了......(你发布你的答案之前) 。这种方法似乎比扫描仪更快。你会建议什么实施?扫描仪或内存映射?对于大文件,内存映射会失败吗?不知道..

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

这种方法看起来不错,但不幸的是它不适用于大型文件,比如你链接的文件(〜250MB)。它由于'DECODER'而与'OutOfMemoryError:Java堆空间'崩溃。decode(mbb)'试图分配一个与文件本身一样大的char缓冲区,即使用'-Xmx'选项增加jvm堆空间也不会避免。我之前尝试过的是使用缓冲读取器并在每行上应用图案,但它运行良好,但比Scanner长4倍。我认为Scanner方法是在运行时避免OOME的最佳选择。扫描仪的缓冲区只有1024! – A4L

+0

请看这个问题的答案(http://stackoverflow.com/questions/7298455/huge-arrays-throws-out-of-memory-despite-enough-memory-available)至于为什么OOME仍然可以尽管使用'-Xmx'设置了 – A4L

+0

此方法与-Xms2000m一起使用。它的速度要快得多 - 对于同一个文件,这是600毫秒,而1900毫秒。但是,有限的记忆会成为问题。我要去扫描仪.. – lochi