【问题标题】:C, format file for data of HTTP responseC、HTTP响应数据的格式文件
【发布时间】:2013-09-11 05:45:04
【问题描述】:

我没有使用fscanf() 的经验,也很少使用FILE 的功能。我有正确确定客户端是否请求现有文件的代码(使用stat(),它还确保它不是目录)。我将省略这部分,因为它工作正常。

我的目标是向客户端发送一个带有 HTTP 标头(一个字符串)和正确读取的数据的字符串,我想它必须在某个时候变成一个字符串,以便与标头连接以发送回。我知道+ 不是有效的C,但为了简单起见,我想发送这个:headerString+dataString

下面的代码似乎适用于文本文件,但不适用于图像。我希望单独阅读每个字符可以解决问题,但事实并非如此。当我将浏览器 (Firefox) 指向我的服务器以查找图像时,它告诉我“图像(图像的名称)无法显示,因为它包含错误。”。

这是应该将文件读入httpData的代码:

int i = 0;
FILE* file;
file = fopen(fullPath, "r");
if (file == NULL) errorMessageExit("Failed to open file");
while(!feof(file)) {
    fscanf(file, "%c", &httpData[i]);
    i++;
}
fclose(file);
printf("httpData = %s\n", httpData);

编辑:这是我发送的:

char* httpResponse = malloc((strlen(httpHeader)+strlen(httpData)+1)*sizeof(char));
strcpy(httpResponse, httpHeader);
strcat(httpResponse, httpData);
printf("HTTP response = %s\n", httpResponse);

数据部分产生 ????对于图像,但对于 html 文件正确的 html。

【问题讨论】:

  • 读取二进制文件需要:file = fopen(fullPath, "rb");
  • 我刚试过(并在上面更新),图像产生????
  • 您需要担心数据中的空字节; %s 格式将在第一个停止。您应该使用while (fscanf(file, "%c", &httpData[i]) == 1) i++; (在循环中使用feof() 几乎总是一个错误——特别是当您不检查来自fscanf() 的返回时,如您显示的代码中的错误)。您需要考虑 CRLF 行尾; HTTP 要求它们用于文本文件。您不能将strcpy() 用于数据;您需要使用memmove()memcpy()
  • @Jonathan Leffler,您能否发表评论的两部分作为答案。我想我理解第一部分,但我不知道如何使用第二部分。

标签: c file scanf


【解决方案1】:

图像包含二进制数据。 256 个不同的 8 位模式中的任何一个都可能出现在图像中,特别是包括空字节、0x00 或'\0'。在某些系统(尤其是 Windows)上,您需要在标准 I/O fopen() 调用中使用字母 b 区分文本文件和二进制文件(在 Unix 和 Windows 上都可以正常工作)。鉴于二进制数据可以包含空字节,您不能使用strcpy() 等来复制数据块,因为str*() 函数在第一个空字节处停止复制。因此,您必须使用mem*() 函数,该函数采用起始位置和长度或等效值。

应用于您的代码,使用%s 打印二进制httpData 将无法正常工作; %s 将在第一个空字节处停止。由于您已使用stat() 来验证文件的存在,因此您也有文件的大小。假设您不必处理动态更改的文件,这意味着您可以将 httpData 分配为正确的大小。您还可以将大小传递给阅读代码。这也意味着读取代码可以使用fread(),写入代码可以使用fwrite(),节省了逐个字符的I/O。

因此,我们可能有一个函数:

int readHTTPData(const char *filename, size_t size, char *httpData)
{
    FILE *fp = fopen(filename, "rb");
    size_t n;
    if (fp == 0)
        return E_FILEOPEN;
    n = fread(httpData, size, 1, fp);
    fclose(fp);
    if (n != 1)
        return E_SHORTREAD;
    fputs("httpData = ", stdout);
    fwrite(httpData, size, 1, stdout);
    putchar('\n');
    return 0;
}

函数在成功时返回 0,在失败时返回一些预定义的(负数?)错误号。由于内存分配是在调用例程之前完成的,因此非常简单:

  • 打开文件;如果失败则报告错误。
  • 在单个操作中读取文件。
  • 关闭文件。
  • 如果读取未获得预期的所有数据,则报告错误。
  • 报告读取的数据(仅调试 - 并将二进制数据打印到标准输出 raw 并不是世界上最好的主意,但它与问题中的代码的作用相似)。
  • 成功报告。

在原代码中,有一个循环:

int i = 0;
...
while(!feof(file)) {
    fscanf(file, "%c", &httpData[i]);
    i++;
}

这个循环有很多问题:

  1. 您不应该使用feof() 来测试是否还有更多数据要读取。它报告是否给出了 EOF 指示,而不是是否给出。
  2. 因此,当最后一个字符被读取时,feof() 报告'false',但fscanf() 尝试读取下一个(不存在的)字符,将其添加到缓冲区中(可能是这样的字母如 ÿ、y-元音变音、0xFF、U+00FF、带分音符号的拉丁文小写字母 Y)。
  3. 代码不检查已读取的字符数,因此无法防止缓冲区溢出。
  4. getc() 相比,使用fscanf() 读取单个字符的开销很大。

这是一个更接近正确的代码版本,假设size 是分配给httpData 的字节数。

int i = 0;
int c;

while ((c = getc(file)) != EOF && i < size)
    httpData[i++] = c;

您可以检查是否在您期望的时候获得了 EOF。请注意,fread() 代码在fread() 函数内部进行大小检查。此外,按照我编写参数的方式,这是一个全有或全无的命题——要么读取所有 size 字节,要么将所有内容视为缺失。如果您想要字节计数并且愿意容忍或处理短读取,您可以反转大小参数的顺序。如果您想确保所有内容都已写入,您还可以检查来自fwrite() 的返回,但人们往往不太注意检查输出是否成功。 (不过,检查您是否获得了预期的输入几乎总是至关重要的——不要吝啬输入检查。)

在某些时候,对于纯文本数据,您需要考虑 CRLF 与 NL 行尾。文本文件会自动处理;二进制文件没有。如果要传输的数据是image/png 或类似的,你可能不需要担心这个。如果你在 Unix 上处理text/plain,你可能不得不担心 CRLF 行尾(但我不是这方面的专家——我最近没有做过低级 HTTP 的东西(不是在这个千年) ,所以规则可能已经改变)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-28
    • 2015-10-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-29
    相关资源
    最近更新 更多