【问题标题】:I'd like to apply a regex efficiently to an entire file我想有效地将​​正则表达式应用于整个文件
【发布时间】:2011-01-10 16:54:37
【问题描述】:

我有一个复杂的正则表达式,我想将它与整个大文件的内容相匹配。主要关注的是效率,因为文件确实非常大,而且很可能内存不足。

有没有办法在通过正则表达式匹配器抽取内容时以某种方式“缓冲”内容?

【问题讨论】:

    标签: java regex


    【解决方案1】:

    是的,Pattern.match() 将采用 CharSequence

    如果您的输入已经在一个字符集中,该字符集正好使用 2 个字节来表示一个没有任何“序言”的字符,您只需要:

    ByteBuffer bb = ...; // acquire memory mapped byte buffer
    CharBuffer cb = bb.asCharBuffer();  // get a char[] 'view' of the bytes
    

    ...既然CharBuffer 实现了CharSequence,你就完成了。

    另一方面,如果您需要将字节解码为其他字符集,那么您的工作就完成了,因为CharBuffer 与字符集无关,而CharsetDecorder.decode(ByteBuffer) 在内部大致分配了一个新的CharBuffer与输入字节大小相同。

    您是否能够摆脱较小的缓冲区在很大程度上取决于您的正则表达式以及您想对匹配结果做什么。但基本方法是实现CharSequence 并包装内存映射ByteBuffer、一个较小的CharBuffer 用于“工作空间”和一个CharsetDecoder。您将使用Charset.decode(ByteBuffer,CharBuffer,boolean) 来“按需”解码字节,并希望正则表达式匹配器的大体方向是“向前”,并且您感兴趣的输入来自相当小的块。

    作为一个粗略的开始:

    class MyCharSequence implements CharSequence {
    
        public MyCharSequence(File file, Charset cs, int bufferSize) throws IOException {
    
            FileInputStream input = new FileInputStream(file);
            FileChannel channel = input.getChannel();
            this.fileLength = (int) channel.size();
            this.bytes = channel.map(FileChannel.MapMode.READ_ONLY, 0, fileLength);
            this.charBuffer = CharBuffer.allocate(bufferSize);
            this.decoder = cs.newDecoder();
    
        }
    
        public int length() {
            // ouch! have to decode the lot, even if you don't choose to keep it all handy
        }
    
        public char charAt(final int index) {
            while ( /* not yet decoded target char[] */ )  {
                this.decoder.decode(this.bytes, this.charBuffer, true);
            }
            // don't assume 2-bytes == a char unless that's true for your charset!
        }
    
        public CharSequence subSequence(final int start, final int end) {
            // this'll be fun, too
        }
    
        private long fileLength;
        private MappedByteBuffer bytes;
        private CharBuffer charBuffer;
        private CharsetDecoder decoder;
    
    }
    

    将完全解码的 CharBuffer 包装在您自己的更简单的 CharSequence 包装器中可能是有启发性的,并在您使用大堆运行它时记录如何为您的给定输入实际调用方法你的开发箱。这将使您了解这种方法是否适用于您的特定场景。

    【讨论】:

    • 我刚刚写了类似的答案,看到你已经发布了!
    • 得快点 :-) 另外,[java.sun.com/developer/technicalArticles/releases/nio/] 很有用,尤其是“映射文件”部分。
    • 好的,所以我可以获取文件的 MappedByteBuffer,然后执行 Charset.defaultCharset().newDecoder().decode(buffer) 之类的操作——但这​​不会创建缓冲区?
    • @Jake Er,是的,抱歉。如果您确实需要解码为 默认字符集以外的字符集,则必须跳过几个环节。我已经扩展了我的答案,
    • 感谢您的回答。不幸的是,输入是 ASCII 并没有让我的生活变得更简单。
    【解决方案2】:

    我不懂 Java,但您是否期望匹配文件的全部内容,例如 /^.+$/
    或者文件是否根据您的正则表达式分成多个块,但您不知道在哪里?
    正则表达式引擎很有趣,如果它可以做内存映射文件,那将是一个好的开始。

    让我们看看你的正则表达式。通常,您可以检查正则表达式并确定两个锚点,并将其用作浮动缓冲区的截止点,其中溢出(重叠)被延续,并且窗口在文件中向下移动。

    我已经在我的 Perl 模块中多次这样做了。除了文件开头和结尾的锚点之外,它也很容易做到。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-11-05
      • 2019-10-30
      • 1970-01-01
      • 2019-09-07
      • 2017-03-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多