Как правильно записать в файл используя File :: Map? - PullRequest
0 голосов
/ 07 декабря 2018

Я использую File :: Map часто для отображения особенно небольших текстовых файлов в памяти и, например, обработки некоторых регулярных выражений только для чтения.Теперь у меня есть сценарий использования, в котором мне также нужно заменить некоторый текст в файле, и я подумал, что я все еще могу использовать File::Map, потому что он документирует следующее:

Файлы отображаются впеременная, которая может быть прочитана так же, как и любая другая переменная, и она может быть записана с использованием стандартных методов Perl, таких как regexps и substr.

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

Запись непосредственно в файл с отображением в памяти не рекомендуется

Усечение нового значения до размера карты памяти

Пояснения к обоим предупреждениям читаются так, будто никто не должен писать что-либо, используя File::Map, но это может сработать в тех случаях, когда можно работать с усеченными файлами, или общий размер файла просто не изменяется вообще.Но в первой цитате явно упоминаются записи как поддерживаемые без каких-либо исключений из этого правила.

Итак, есть ли какой-то особый способ безопасной записи с использованием File::Map, например увеличение основного файла и так далее?Первое предупреждение использует формулировку directly, и у меня такое ощущение, что есть какой-то другой, лучше поддерживаемый способ записи?

В настоящее время я просто использую =~ s/// в отображенном виде, что, кажется,неправильный подход.Я даже не смог найти никого, кто пытался бы писать, используя File::Map, только официальные тесты, которые делали именно то, что я делаю, и ожидали предупреждений, которые я получаю.Кроме того, глядя на код, кажется, есть только один вариант использования, в котором написание вообще не приводит к предупреждению, хотя я не понимаю, как я могу вызвать это:

static int mmap_write(pTHX_ SV* var, MAGIC* magic) {
        struct mmap_info* info = (struct mmap_info*) magic->mg_ptr;
        if (!SvOK(var))
                mmap_fixup(aTHX_ var, info, NULL, 0);
        else if (!SvPOK(var)) {
                STRLEN len;
                const char* string = SvPV(var, len);
                mmap_fixup(aTHX_ var, info, string, len);
        }
        else if (SvPVX(var) != info->fake_address)
                mmap_fixup(aTHX_ var, info, SvPVX(var), SvCUR(var));
        else
                SvPOK_only_UTF8(var);
        return 0;
}

https://metacpan.org/source/LEONT/File-Map-0.55/lib/File/Map.xs#L240

В конце концов, если вообще следует избегать записи, почему в документах прямо упоминается, что она поддерживается?Мне не кажется поддерживаемым, если это приводит, по крайней мере, к предупреждению во всех случаях, кроме одного.

Ответы [ 2 ]

0 голосов
/ 07 декабря 2018

mmap - это отображение фиксированного размера части файла в памяти.

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

Правильный способ работы с mmap - модифицировать строковый буфер, а не заменять его.

  • Подходит все, что изменяет строковый буфер без изменения его размера.

    $ perl -e'print "\0"x16' >scratch
    
    $ perl -MFile::Map=map_file -we'
       map_file my $map, "scratch", "+<";
       $map =~ s/\x00/\xFF/g;             # ok
       substr($map, 6, 2, "00");          # ok
       substr($map, 8, 2) = "11";         # ok
       substr($map, 7, 2) =~ s/../22/;    # ok
    '
    
    $ hexdump -C scratch
    00000000  ff ff ff ff ff ff 30 32  32 31 ff ff ff ff ff ff  |......0221......|
    00000010
    
  • Все, что заменяет строковый буфер (например, назначениескаляр) не в порядке.

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

    $ perl -e'print "\0"x16' >scratch
    
    $ perl -MFile::Map=map_file -we'
       map_file my $map, "scratch", "+<";
       $map = "4" x 16;  # Effectively: substr($map, 0, 16, "4" x 16)
    '
    Writing directly to a memory mapped file is not recommended at -e line 3.
    
    $ hexdump -C scratch
    00000000  34 34 34 34 34 34 34 34  34 34 34 34 34 34 34 34  |4444444444444444|
    00000010
    

    Помимо предупреждения можно отключить с помощью no warnings qw( substr );, [1] Единственным недостатком является то, что для этого способа необходимо использовать memcpy для копирования length($map) байтов, а для использования substr($map, $pos, length($repl), $repl) требуется только копирование length($repl) байтов.

  • Все, что изменяет размер строкового буфера, не в порядке.

    $ perl -MFile::Map=map_file -we'
       map_file my $map, "scratch", "+<";
       $map = "5" x 32;  # Effectively: substr($map, 0, 16, "5" x 16)
    '
    Writing directly to a memory mapped file is not recommended at -e line 3.
    Truncating new value to size of the memory map at -e line 3.
    
    $ hexdump -C scratch
    00000000  35 35 35 35 35 35 35 35  35 35 35 35 35 35 35 35  |5555555555555555|
    00000010
    

ПРЕДУПРЕЖДЕНИЕ: модуль не предупреждает, если вы уменьшаете буфер, даже еслиэто не имеет никакого эффекта, кроме как заточить один из байтов с помощью NUL.

$ perl -e'print "\0"x16' >scratch

$ perl -MFile::Map=map_file -we'
   map_file my $map, "scratch", "+<";
   substr($map, 0, 16, "6" x 16);
   substr($map, 14, 2, "");
'

$ hexdump -C scratch
00000000  36 36 36 36 36 36 36 36  36 36 36 36 36 36 00 36  |66666666666666.6|
00000010

Я отправил тикет .


  1. Этоявляется несколько ироничным, видя, что он более или менее предупреждает, когда не используется substr, но я полагаю, что это также предупреждает при использовании substr «неправильно».
0 голосов
/ 07 декабря 2018

первая цитата ,

Файлы отображаются в переменную, которая может быть прочитана так же, как любая другая переменная, и она может быть записана в стандартные методы Perl, такие какregexps и substr.

находятся под заголовком «Простота».

И это правда: вы можете просто написать код Perl, который манипулирует строками, и данные окажутся вфайл.

Однако в разделе Предупреждения имеется:

Запись непосредственно в файл с отображением в памяти не рекомендуется

Из-зак тому, как perl работает внутри, невозможно написать реализацию отображения, которая позволяет прямое назначение, но при этом хорошо работает.В качестве компромисса File :: Map способен исправить ситуацию, если вы все же это сделаете, но предупредит вас, что вы делаете то, что не должны делать.Это предупреждение выдается только тогда, когда действует use warnings 'substr'.

То есть запись через переменную mmap'd неэффективна, если только модификация строкового буфера не может быть выполнена на месте (строкасначала должен быть собран и сохранен в памяти, а затем скопирован в файл).Если вы согласны с этим, вы можете отключить предупреждение с помощью no warnings 'substr'.

Кроме того, глядя на код, кажется, есть только один вариант использования, в котором запись не приводит кпредупреждение вообще, хотя я не понимаю, как я могу вызвать это.

Это тот случай, когда вы пытаетесь записать буфер в себя.Это происходит, когда скаляр действительно изменяется на месте.Другие случаи - это обходные пути для замены строкового буфера (например, потому что он перезаписан: $foo = $bar).Для реальной модификации на месте не требуется никакой дополнительной работы, и вы не получите предупреждение.

Но это не поможет вам, потому что растущая строка не может быть сделана на месте с сопоставленным буфером фиксированного размера.

Изменение размера файла невозможно.Это не из-за File :: Map, а из-за того, что базовый системный вызов mmap работает с сопоставлениями фиксированного размера и не предоставляет никакой опции для автоматического изменения размера файлов.

Если вам нужнодля редактирования файлов (особенно небольших файлов) я рекомендую использовать edit в Path :: Tiny вместо.

...