perl - 如何使用 File::Map 正确写入文件?
问题描述
我经常使用File::Map将特别小的文本文件映射到内存中,例如在这些文件上处理一些只读正则表达式。现在我有一个用例,我还需要替换文件中的一些文本,并认为我仍然可以使用File::Map
,因为它记录了以下内容:
文件被映射到一个可以像任何其他变量一样读取的变量,并且可以使用标准 Perl 技术(例如 regexps 和 substr)写入它。
虽然我有兴趣替换的数据在文件中被正确替换,但我正在丢失数据,因为文件保持其原始大小并且数据最终被截断。新数据比旧数据大一点。这两件事都被警告,如使用以下句子记录的那样:
不建议直接写入内存映射文件
将新值截断为内存映射的大小
对这两个警告的解释读起来就像一个人不应该使用 写任何东西File::Map
,但它可能适用于可以使用截断文件或根本没有改变整体文件大小的情况。但是第一个引用明确提到支持写入,该规则没有任何例外。
那么,是否有一些特殊的方法可以安全地使用 using 进行编写File::Map
,例如增加底层文件等?第一个警告使用了措辞directly
,我觉得还有其他更好的支持方式来编写?
我=~ s///
目前只是在映射视图上使用,这似乎是错误的方法。我什至找不到任何人尝试使用 using 进行编写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
毕竟,如果完全应该避免写作,为什么文档明确提到它是受支持的?如果它至少在所有情况下都导致警告,但对我来说似乎不受支持。
解决方案
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
我已经提交了一张票。
- 这有点讽刺,因为它在不使用时或多或少会发出警告
substr
,但我想它在substr
“不正确”使用时也会发出警告。
推荐阅读
- django - Django Forms:RelatedManager' 对象没有属性
- shopify - 根据客户位置自动履行订单 - 应用建议?
- sql-server-2016 - 在不考虑周六和周日的情况下生成指标
- mysql - Laravel GroupBy 和 OrderBy 排序问题
- can-bus - 在 SDO 块传输中可以使用的最大大小是多少?如何使用?
- mysql - 从星期日生成日期,除了现有值,在 1 周范围内
- python-3.x - 重新分配具有相同数组的多个列
- angular - 是否有可能在 Angular 中使我的
是一个html标签? - azure - 在 Asp.net Core 中将图像保存到文件系统
- android - 如何使用 ContentProviderOperation 更新多个电话号码