Я хотел бы эффективно применить регулярное выражение ко всему файлу - PullRequest
7 голосов
/ 10 января 2011

У меня сложное регулярное выражение, и я хотел бы сопоставить его с содержимым всего огромного файла. Основной проблемой является эффективность, так как файл действительно очень большой, и нехватка памяти является вполне вероятной возможностью.

Есть ли способ, которым я могу каким-то образом "буферизовать" содержимое при прокачке через регулярное выражение?

Ответы [ 2 ]

6 голосов
/ 10 января 2011

Да, 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 и записать, как на самом деле вызываются методы для заданного вами ввода, когда вы запускаете его с большой кучей на ваша коробка разработки. Это даст вам представление, будет ли этот подход работать для вашего конкретного сценария.

0 голосов
/ 10 января 2011

Я не знаю Java, но ожидаете ли вы соответствия всего содержимого файла, например /^.+$/?
Или файл разбивается на куски в зависимости от вашего регулярного выражения, но вы не знаете, где?
Движки Regex забавны, если они могут сделать файл с отображенной памятью, то это было бы хорошим началом.

Давайте посмотрим на ваше регулярное выражение. Как правило, вы можете проверить регулярное выражение и определить две точки привязки и использовать их в качестве отсечки для плавающего буфера, где переполнение (перекрытие) переносится, и окно перемещается дальше вниз по файлу.

Я делал это несколько раз в моих модулях Perl. И для всего, кроме якорей в начале и конце файла, это легко сделать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...