【问题标题】:C Systems Program - Read/Write Issues During CopyC 系统程序 - 复制期间的读/写问题
【发布时间】:2013-10-18 19:23:34
【问题描述】:

我正在编写一个从标准 UNIX 存档 ar 中提取并创建它存储的文件的 C 程序。

以下是我在 vim 中打开 ar 的示例:

!<arch>
yo              1382105439  501   20    100644  10        `
test1 lol
yo2             1382105444  501   20    100644  10        `
test2 lol

...其中“test1 lol”和“test2 lol”是每个文件的内容,“yo”和“yo2”是两个不同的文件名,剩下的就是元数据,按照标准ar对应的格式存储.h(在此处阅读更多信息:http://www.lehman.cuny.edu/cgi-bin/man-cgi?ar.h+3

无论如何,我仍在编写函数,但这是我目前所拥有的:

static void extract_files (int argc, char *argv[])
{

  int fd;
  int new_file_fd;
  int num_read = 0;
  int new_file_size;

  struct ar_hdr current_header;

  char name_buffer[16];
  char date_buffer[12];
  char uid_buffer[6];
  char gid_buffer[6];
  char mode_buffer[8];
  char size_buffer[10];
  char fmag_buffer[2];

  // grab the fd #
  fd = open(argv[2], O_RDWR | O_CREAT, 0666);

  // go to the first header
  lseek(fd, SARMAG, SEEK_CUR);

  // store the number of bits read in a struct current_header
  // until its size equal to the size of the entire
  // header, or in other words, until the entire
  // header is read
  while ((num_read = read(fd, (char*) &current_header, 
    sizeof(struct ar_hdr))) == sizeof(struct ar_hdr))
  {

    // scans the current string in header and stores
    // in nameStr array
    sscanf(current_header.ar_name, "%s", name_buffer);
    sscanf(current_header.ar_date, "%s", date_buffer);
    sscanf(current_header.ar_uid, "%s", uid_buffer);
    sscanf(current_header.ar_gid, "%s", gid_buffer);

    int mode;
    sscanf(current_header.ar_mode, "%o", &mode);
    sscanf(current_header.ar_size, "%s", size_buffer);
    int size = atoi(size_buffer);
    sscanf(current_header.ar_fmag, "%s", fmag_buffer);

    // Create a new file
    new_file_fd = creat(name_buffer, mode);
    // Grab new file size
    new_file_size = atoi(size_buffer);

    int io_size; // buffer size
    char buff[size];
    int read_cntr = 0;

    // from copy.c
    while ((io_size = read (fd, buff, new_file_size)) > 0)
    {
      read_cntr++;
      if (read_cntr > new_file_size)
        break;
      write (new_file_fd, buff, new_file_size);
    }

    close(new_file_fd);
    printf("%s\n", name_buffer);
    printf("%s\n", date_buffer);
    printf("%s\n", uid_buffer);
    printf("%s\n", gid_buffer);
    printf("%s\n", mode_buffer);
    printf("%s\n", size_buffer);
    printf("%s\n", fmag_buffer);

    /* Seek to next header. */
    lseek(fd, atoi(current_header.ar_size) + (atoi(current_header.ar_size)%2), SEEK_CUR);
  }

}

我遇到的问题在于上述代码中的第二个 while 循环:

    // from copy.c
while ((io_size = read (fd, buff, new_file_size)) > 0)
{
  read_cntr++;
  if (read_cntr > new_file_size)
    break;
  write (new_file_fd, buff, new_file_size);
}

由于某种原因,在此 while 循环中写入的文件不会运行到 write 指定的长度。标准 read()/write() 的第三个参数应该是要写入的字节数。但由于某种原因,我的代码导致整个存档被读入并写入第一个文件。

如果我打开生成的“yo”文件,我发现整个存档文件都已写入其中

test1 lol
yo2             1382105444  501   20    100644  10        `
test2 lol

而不是在读取 10 个字节并给出预期结果“test1 lol”后终止。

我还可以确认“new_file_size”的值确实是 10。所以我的问题是:关于这个 while 循环,我读错了什么?

注意: 预期的输入是命令行参数,类似于: ./extractor.c -x name_of_archive_file

我认为我需要在此函数中处理的唯一相关信息是我在 extract_files 开头获取 fd 的存档文件的名称。

添加: Misc -- 运行时的输出:

yo
1382105439
501
20
X
10
`

如您所见,它永远不会看到 yo2 文件或打印出它的标题,因为在此之前它会被写入“yo”...因为这个流浪的 while 循环:(

【问题讨论】:

  • 一些提示:编译时启用警告(-Wall -Wextra 使用 gcc)并修复它们。此外,使用代码自动格式化或自动缩进(如果您不知道如何使用编辑器/IDE 进行操作,请学习,如果做不到,请切换到可以做的事情)。
  • 另一件事:当使用任何scanf 函数解析任何输入时,检查返回值以确保它获得了所有参数的值。
  • 另外,你依赖缓冲区溢出没有发生似乎有点大胆。当你sscanf最后一项时,你确定它后面是空格还是源字符串中的'\0',所以sscanf知道停止?另外,你所有的小缓冲区,你确定它们都有足够的空间吗?您应该将目标缓冲区大小指定为sscanf,包括'\0' 的空间!
  • 也许你应该只用调试器单步调试代码... :) 并查看所有scanfreadwrite 等调用的返回值。然后您可以立即查看是否有错误。
  • 不要忘记检查将yo 替换为supercalifragilisticexpialidocious 时会发生什么。不同版本的ar 有不同的技术来处理长文件名。还要确保测试文件长度为偶数和奇数的文件;再次,我的回忆是奇数长度在存档文件中被空填充到偶数长度。只要您阅读了确切的长度就可以了,但是您可能还必须跳过一个(零)字节才能到达下一个文件头的开头。在一些二进制数据(例如目标文件或您正在测试的程序)上检查您的代码也是值得的。

标签: c unix file-io archive systems-programming


【解决方案1】:

您读取一个值 size_buffer,并将其分配给 size 和 new_file_size,您还创建了一个相同大小的 buffer[size]

int size = atoi(size_buffer);
sscanf(current_header.ar_fmag, "%s", fmag_buffer);
//...
new_file_size = atoi(size_buffer);
//...
char buff[size];

Read 返回[0..new_file_size] 范围内的ssize_t 字节数,您将其设置为io_size,意识到read(2) 可能return &lt; new_file_size 字节,这就是您需要while 循环的原因。所以你需要写你读过的所有东西,直到你达到你的写限制。我做了一些cmets来指导你。

// from copy.c
while ((io_size = read (fd, buff, new_file_size)) > 0)
{
    read_cntr++;
    //perhaps you mean read_cntr += io_size;
    //you probably mean to write io_size bytes here, regardless
    //write(new_file_fd, buff, io_size);
    if (read_cntr > new_file_size) //probably you want >= here
        break;
    //you may have broke before you write...
    write (new_file_fd, buff, new_file_size);
}

这个副本的一个更典型的习惯用法是选择一个读/写缓冲区大小,比如4*1024 (4K)16*1024 (16K) 等,然后读取该块大小,直到剩余的块小于该块大小;例如,

//decide how big to make buffer for read()
#define BUFSIZE (16*1024) //16K
//you need min(
#define min(x,y) ( ((x)<(y)) ? (x) : (y) )
ssize_t fdreader(int fd, int ofd, ssize_t new_file_size )
{
    ssize_t remaining = new_file_size;
    ssize_t readtotal = 0;
    ssize_t readcount;
    unsigned char buffer[BUFSIZE];
    for(  ; readcount=read(fd,buffer,min(sizeof(buffer),remaining));  )
    {
        readtotal += readcount;
        if( readcount > remaining ) //only keep remaining
            readcount = remaining;
        write( ofd, buffer, readcount);
        remaining -= readcount;
        if( remaining <= 0 ) break; //done
    }
    return readtotal;
}

试试这个,

#include<stdio.h>
#include<stdlib.h>

void usage(char*progname)
{
    printf("need 2 files\n");
    printf("%s <infile> <outfile>\n",progname);
}

//decide how big to make buffer for read()
#define BUFSIZE (16*1024) //16K
//you need min(
#define min(x,y) ( ((x)<(y)) ? (x) : (y) )
ssize_t fdreader(int fd, int ofd, ssize_t new_file_size )
{
    ssize_t remaining = new_file_size;
    ssize_t readtotal = 0;
    ssize_t readcount;
    unsigned char buffer[BUFSIZE];
    for(  ; readcount=read(fd,buffer,min(sizeof(buffer),remaining));  )
    {
        readtotal += readcount;
        if( readcount > remaining ) //only keep remaining
            readcount = remaining;
        write( ofd, buffer, readcount);
        remaining -= readcount;
        if( remaining <= 0 ) break; //done
    }
    return readtotal;
}

int main(int argc,char**argv)
{
    int i=0; /* the infamous 'i' */
    FILE*infh;
    FILE*outfh;

    if( argc < 3 )
    {
        usage(argv[0]);
        return 0;
    }

    printf("%s %s\n",argv[1],argv[2]); fflush(stdout);
    if( !(infh=fopen(argv[1],"r")) )
    {
        printf("cannot open %s\n",argv[2]); fflush(stdout);
        return(2);
    }
    if( !(outfh=fopen(argv[2],"w+")) )
    {
        printf("cannot open %s\n",argv[3]); fflush(stdout);
        return(3);
    }

    int x = fdreader(fileno(infh), fileno(outfh), 512 );

    return 0;
}

【讨论】:

  • 这似乎解决了我最大的问题。非常感谢!
  • 当我应用你的习语时,它似乎超过了指示的“new_file_size”。知道为什么会发生这种情况吗?
【解决方案2】:

您的 while() 循环后面可能应该有大括号 ({ ... }),否则您只是递增 read_cntr 而没有做任何其他事情。

【讨论】:

  • 好像没有影响输出:/
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-19
  • 1970-01-01
  • 1970-01-01
  • 2018-02-20
  • 2019-04-16
相关资源
最近更新 更多