sscanf 读取一个字符而不是整个字符串 (mmap)

sscanf reads one character instead of the entire string (mmap)

我在使用内存映射到"edit"一个文件并将结果打印到另一个文件时遇到了这种现象。我的理解是 sscanf (类似于 printf)读取一个字符串,直到遇到空格或换行符。这正是我的策略,当我在 mmap 的源文件中使用它来过滤掉注释并将其他所有内容打印到第二个文件时。对于注释行如何开始的给定参数(字符串,例如“#”、“@”、“//”、"whatever"),我使用 sscanf 来检查一行是否以该词开头.令我惊讶的是,当使用 mmap 时,指向该虚拟内存的指针似乎没有得到相同的处理。 (注意 while 循环之前的测试 printf)。

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/mman.h>
#include <unistd.h>

static void     fatalError(char *message);

int     main(int argc, char *argv[])
{
    int     fd1, fd2;
    struct stat     stats;
    char    *source, *dest;


    assert(argc == 4);

    if ((fd1 = open(argv[1], O_RDONLY)) < 0) {
        if (errno == ENOENT)
            fatalError("First file nonexistent");
        else 
            fatalError("open error");
    }


    if ((fd2 = open(argv[2], O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH)) < 0) {
        if (errno == EACCES)
            printf("Creating second file\n");
        else
            fatalError("open error");
    }


    if (fstat(fd1, &stats) < 0)
        fatalError("fstat error");

    if (lseek(fd2, stats.st_size-1, SEEK_SET) < 0)
        fatalError("lseek error");

    if (write(fd2, "", 1) != 1)
        fatalError("write error");


    if ((source = mmap(0, stats.st_size, PROT_READ, MAP_SHARED, fd1, 0)) == MAP_FAILED)
        fatalError("mmap failed");

    if ((dest = mmap(0, stats.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd2, 0)) == MAP_FAILED)
        fatalError("mmap failed");

    char    word[100];
    int     flag, count;
    char    *tmp;

    tmp = source;

    char    test[50] = "firstWord will only be read by sscanf";
    sscanf(test, "%s", word);
    printf("%s\n", word);

    while ((count = sscanf(source, "%s", word)) > 0) {
        if (!flag) {
            // in comment
            if (!strcmp(word, argv[3])) {
                flag = 1;
                source += count;
                continue;
            }
            sprintf(dest, "%s", word);
            source += count;
        } else {
            // not in comment
            if (word[strlen(word) - 1] == '\n') {
                sprintf(dest, "\n");
                source += count;
            }

            source += count;

        }

    }

    source = tmp;

    if (munmap(source, stats.st_size) < 0)
        fatalError("munmap error");

    if (munmap(dest, stats.st_size) < 0)
        fatalError("munmap error");

    if (close(fd1) < 0)
        fatalError("close return");

    if (close(fd2) < 0)
        fatalError("close error");




    return 0;
}


static void     fatalError(char *message) {
    perror(message);
    exit(EXIT_FAILURE);
}

输入文件(“#”为注释符号):

npwhitespacea
asdasdasdasd
# asdasdasdasmdaosimda
asdasd
kmflsdkfms
#
oioiasjdoaisd
i
# asoidaosid

sscanf 读取直到空 '[=11=]' 字符。

source += count; 是有问题的代码,因为它与 source++; 相同,因为 count 在这里是 1。

很可能在匹配时,代码应该按字符串的长度而不是转换计数前进。

  // in comment
  if (!strcmp(word, argv[3])) {
    flag = 1;
    // source += count;
    source += strlen(argv[3]);
    continue;
  }

代码永远不会是真的

word[strlen(word) - 1] == '\n' 永远不会为真,因为 sscanf(source, "%s", word) 不会将 white-space 放入 word.


sscanf (similar to printf) reads a string until a whitespace or newline is encountered.

OP 的概括范围太广。 1) "until" 取决于格式。 2) 对于格式"%s"sscanf() 读取白色-spaces 并跳过其他它们,然后它读取非白色-spaces 并保存它们。这一直持续到找到白色 space(其中包括 '\n')。在这种情况下,sscanf()scanf/fscanf 不同,因为 scanf/fscanf 读取 空字符 是 read/saved 就像任何非白色-space。对于 sscanf() 空字符 类似于文件结尾。它不是 "read/saved",但会停止扫描。


OP的整体策略有弱点。 OP 正在寻找 评论行 "%s" 失去对 '\n' 的所有意识。

代码可以使用下面的代码来读取 ,但是对于空行或很长的行需要做更多的工作。

char line[260];
int n = 0;
sscanf(source, "%259[^\n]%n", line, &n);
if (line > 0) {
  // success, now read potential \n or more line
  ...
} else {
  // read potential \n (a short line)
  ...
}

// process line

// next line
source += n;

可能存在其他问题。