【问题标题】:Using sscanf() function to parse archive file使用 sscanf() 函数解析归档文件
【发布时间】:2015-10-23 01:42:54
【问题描述】:

我正在编写一个程序,它读取存档文件的标题,并使用 sscanf() 函数打印出它的重要数据。效果应该和 ls shell 命令差不多。

一个示例存档文件:

!<arch>
file1/          1445559998  17099 88    33188   4         `
one
file2/          1445485080  17099 88    33188   5         `
Two.

第一行是一个 8 字符的“魔术字符串”,用于将文件标识为存档,第二和第四行是标题,第三和第五行是文件的内容。

通过使用右填充(空格),标头保持正好 60 个字节的长度(在 ' 是 \n 字符之后)。

来自 ar.h:

#include <ar.h>
struct  ar_hdr       /* file member header */
 {
     char    ar_name[16];    /* '/' terminated file member name */
     char    ar_date[12];    /* file member date */
     char    ar_uid[6]       /* file member user identification */
     char    ar_gid[6]       /* file member group identification */
     char    ar_mode[8]      /* file member mode (octal) */
     char    ar_size[10];    /* file member size */
     char    ar_fmag[2];     /* header trailer string */
 };

现在开始我的代码。对于初学者,我希望能够打印出文件名:

file1
file2

我有这个代码来完成这个,除了我遇到分段错误。

void parseArc(int fd_ar)    // fd of archive
{
        struct stat statbuf;
        fstat(fd_ar, &statbuf);

        char buf[statbuf.st_blksize];
        char *filename;
        int where;

        where = 8;

        while (where <= statbuf.st_size)
        {
                lseek(fd_ar, where, SEEK_SET);

                read(fd_ar, buf, 60);    // read the header

                sscanf(buf, "%s/          ", filename);

                printf("%s\n", filename);

                where = where+60+4;    // 60 for header length, 4 for file1 length
        }
}

一个明显的错误是最后一行的 +4。理想情况下,这将被替换为包含文件长度的变量,这是通过正确使用 sscanf() 获得的。

我的主要问题是,为什么会出现段错误?我假设它在 sscanf() 函数上。我已经阅读了函数 API,但我无法弄清楚我是如何滥用它的。谢谢。

【问题讨论】:

  • 虽然存档文件头是一个字符串结构,但我希望使用fread() 来读取头的正确字节数。注意长文件名——即超过 15 个字节。它们是允许的,但处理方式不同。

标签: c


【解决方案1】:

您从未初始化指针filename。所以sscanf 正在尝试将从buf 提取的字符串写入内存中的随机位置。因此出现段错误。

要么将filename 设为一个数组,要么调用malloc 为其分配一些空间。

单独使用sscanf%s 通常很危险,因为您可能会得到一个长字符串,超出您为该字符串分配的空间。这是一个缓冲区溢出,当您的客户开始受到攻击时,它将导致需要一个紧急安全补丁。请改用%42s 之类的东西。

(你可能认为你是安全的,因为buf 最多有 60 个字节长,但如果它不是以 null 结尾的呢?)

大多数编译器应该能够注意到您正在使用 filename 未初始化,并警告您。 (gcc -Wall will.)您的编译器中是否打开了警告?如果没有,那就太丢脸了。如果是,并且您收到了警告但忽略了它,那真是太丢脸了。

【讨论】:

  • 或者,在 POSIX 系统上,您可以在格式字符串中使用%ms,在参数列表中使用&amp;filenamescanf() 将为您分配正确的内存量。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-07-23
  • 2021-12-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-25
  • 1970-01-01
相关资源
最近更新 更多