使用 strtok 解析 mmaped 文件?
Parsing mmaped file with strtok?
这是我的问题:我想映射文件 "filename.txt",它基本上每行包含两对字符串:
"string1 string2
string3 string4
string5 string6..."
然后我想使用 strtok 分隔不同的字符串。
所以我这样映射文件:
// open file
if ((fdsrc = open("filename.txt", O_RDONLY)) < 0) {
fprintf(stderr, "src open error");
exit(1);
}
// get the size of the file
if (fstat(fdsrc, &statbuf) < 0) {
fprintf(stderr, "fstat error");
exit(1);
}
// mmap the file
if ((src = mmap(0, statbuf.st_size, PROT_READ, MAP_SHARED, fdsrc, 0)) == (caddr_t) -1) {
fprintf(stderr, "mmap src");
exit(1);
}
当我运行行
printf("src: %s \n", src);
它正确地打印了文件的内容!
但是当我尝试将单词分开时
char* token;
token = strtok(src, " \n");
while (token != NULL) {
token = strtok(NULL, " \n");
}
输出是分段错误。
那为什么我不能使用 StrTok?
strtok()
修改您向其传递指针的 char
数组。
您 mmap
文件处于只读模式,因此当 strtok
试图修改内存时您会遇到违规。
以读写模式mmap
文件是个坏主意,文件会被修改并可能损坏。
strtok
不适合您的目的,编写您自己的匹配函数,不修改其参数数组和 returns 偏移量和长度。
另请注意,mmap
ped 内存不应超出文件大小进行访问,并且不一定 '[=17=]'
终止,因此您不应使用字符串函数搜索它( strchr
、strstr
、strlen
...) 也不会从中复制 (strcpy
).
您的文件映射为只读 PROT_READ
。但是 strtok()
修改了它的第一个参数 src
,并得到了一个分段错误。您需要在使用 strtok
之前制作一个可写副本,或者切换到只读取其输入的机制。在我看来,将该缓冲区的保护更改为 PROT_RW
似乎很奇怪,特别是如果您打算在程序的其他地方使用该文件的未修改内容。
作为替代方案,我建议使用 strstr()
(或不需要空字节终止的替代实现)来定位行尾子字符串,然后开始下一次搜索你找到最后一次出现的地方,加上你的子串的长度。请参阅下面有关空字节终止的注释。一个简化的例子:
const char *delim = "\n";
const char *start = src;
const char *end = NULL;
const int srclen = statbuf.st_size;
const int delim_length = strlen(delim);
while (start && start < (src + srclen)) {
end = strstr(start, delim);
if (NULL == end) {
// use of %.* to print at most X chars from string.
printf("Token: %.*s\n", (int) (src + srclen - start), start);
break;
} else {
printf("Token: %.*s\n", (int) (end - start), start);
start = end + delim_length;
}
}
mmap 区域不能以空字节结尾(如评论所建议)
strstr()
适用于以 null 结尾的字符串。您映射的区域不能以空字节结尾。内核很可能会用 [=18=]
s 擦除最后一个映射内存页面的剩余部分(超过文件末尾)以避免进程之间的数据泄漏,但如果您的文件长度恰好是页面的倍数-size,你在使用 strstr()
时会遇到麻烦——不会有空字节来让你回来。
您可以推出自己的小字符串查找器 strnstr()
。或者强行在最后打上另一个空页。
strtok()
修改它操作的字符串。假设您不想更改文件内容,则需要更改 mmap()
选项。
您正在以只读方式打开和映射文件:
if ((fdsrc = open("filename.txt", O_RDONLY)) < 0) {
...
if ((src = mmap(0, statbuf.st_size, PROT_READ, MAP_SHARED, fdsrc, 0)) == (caddr_t) -1) {
...
将文件映射到 PROT_READ|PROT_WRITE
和 MAP_PRIVATE
:
src = mmap(0, statbuf.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fdsrc, 0);
if (src == (caddr_t) -1) {
您可能需要使用 O_RDWR
而不是 O_RDONLY
打开文件
注意事项:
如果文件大小与用于映射的页面大小的倍数完全匹配,则文件将不是以 NUL 结尾的字符串,并且当 strtok()
尝试读取超过映射结束。
在这种情况下,您可以 mmap()
紧跟在文件映射之后的零填充页面。
这是我的问题:我想映射文件 "filename.txt",它基本上每行包含两对字符串:
"string1 string2
string3 string4
string5 string6..."
然后我想使用 strtok 分隔不同的字符串。
所以我这样映射文件:
// open file
if ((fdsrc = open("filename.txt", O_RDONLY)) < 0) {
fprintf(stderr, "src open error");
exit(1);
}
// get the size of the file
if (fstat(fdsrc, &statbuf) < 0) {
fprintf(stderr, "fstat error");
exit(1);
}
// mmap the file
if ((src = mmap(0, statbuf.st_size, PROT_READ, MAP_SHARED, fdsrc, 0)) == (caddr_t) -1) {
fprintf(stderr, "mmap src");
exit(1);
}
当我运行行
printf("src: %s \n", src);
它正确地打印了文件的内容!
但是当我尝试将单词分开时
char* token;
token = strtok(src, " \n");
while (token != NULL) {
token = strtok(NULL, " \n");
}
输出是分段错误。 那为什么我不能使用 StrTok?
strtok()
修改您向其传递指针的 char
数组。
您 mmap
文件处于只读模式,因此当 strtok
试图修改内存时您会遇到违规。
以读写模式mmap
文件是个坏主意,文件会被修改并可能损坏。
strtok
不适合您的目的,编写您自己的匹配函数,不修改其参数数组和 returns 偏移量和长度。
另请注意,mmap
ped 内存不应超出文件大小进行访问,并且不一定 '[=17=]'
终止,因此您不应使用字符串函数搜索它( strchr
、strstr
、strlen
...) 也不会从中复制 (strcpy
).
您的文件映射为只读 PROT_READ
。但是 strtok()
修改了它的第一个参数 src
,并得到了一个分段错误。您需要在使用 strtok
之前制作一个可写副本,或者切换到只读取其输入的机制。在我看来,将该缓冲区的保护更改为 PROT_RW
似乎很奇怪,特别是如果您打算在程序的其他地方使用该文件的未修改内容。
作为替代方案,我建议使用 strstr()
(或不需要空字节终止的替代实现)来定位行尾子字符串,然后开始下一次搜索你找到最后一次出现的地方,加上你的子串的长度。请参阅下面有关空字节终止的注释。一个简化的例子:
const char *delim = "\n";
const char *start = src;
const char *end = NULL;
const int srclen = statbuf.st_size;
const int delim_length = strlen(delim);
while (start && start < (src + srclen)) {
end = strstr(start, delim);
if (NULL == end) {
// use of %.* to print at most X chars from string.
printf("Token: %.*s\n", (int) (src + srclen - start), start);
break;
} else {
printf("Token: %.*s\n", (int) (end - start), start);
start = end + delim_length;
}
}
mmap 区域不能以空字节结尾(如评论所建议)
strstr()
适用于以 null 结尾的字符串。您映射的区域不能以空字节结尾。内核很可能会用 [=18=]
s 擦除最后一个映射内存页面的剩余部分(超过文件末尾)以避免进程之间的数据泄漏,但如果您的文件长度恰好是页面的倍数-size,你在使用 strstr()
时会遇到麻烦——不会有空字节来让你回来。
您可以推出自己的小字符串查找器 strnstr()
。或者强行在最后打上另一个空页。
strtok()
修改它操作的字符串。假设您不想更改文件内容,则需要更改 mmap()
选项。
您正在以只读方式打开和映射文件:
if ((fdsrc = open("filename.txt", O_RDONLY)) < 0) {
...
if ((src = mmap(0, statbuf.st_size, PROT_READ, MAP_SHARED, fdsrc, 0)) == (caddr_t) -1) {
...
将文件映射到 PROT_READ|PROT_WRITE
和 MAP_PRIVATE
:
src = mmap(0, statbuf.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fdsrc, 0);
if (src == (caddr_t) -1) {
您可能需要使用 O_RDWR
而不是 O_RDONLY
注意事项:
如果文件大小与用于映射的页面大小的倍数完全匹配,则文件将不是以 NUL 结尾的字符串,并且当 strtok()
尝试读取超过映射结束。
在这种情况下,您可以 mmap()
紧跟在文件映射之后的零填充页面。