【问题标题】:Cannot read binary video files in GNU/Linux无法在 GNU/Linux 中读取二进制视频文件
【发布时间】:2009-10-06 15:45:37
【问题描述】:

我被一段看似无害的代码困住了。我正在尝试将整个 flv 视频文件读入 uint8_t 数组,但毫无理由只读取前 10 个字节。

contents = malloc(size + 1);

if (read(fd, contents, size) < 0)
{
    free(contents);
    log_message(WARNING, __func__, EMSG_READFILE);
    return (NULL);
}

我也尝试过使用 fopen 和“rb”,但似乎 Glibc 忽略了额外的“b”或其他东西。有什么线索吗?

提前致谢。

编辑:也许它读取的是 EOF 字符?

PS。 'size' 是一个包含使用 stat() 的实际文件大小的变量。

【问题讨论】:

  • 尺寸是多少?并且你检查过malloc的返回值不为null吗?
  • 我相信 fopen 的 'b' 标志只在 Windows 中需要。它在 Linux 中被忽略,因此您可以将其包含在内以实现可移植性。
  • 你检查过返回的错误吗?见 strerror(int errnum)

标签: c linux file


【解决方案1】:

似乎原始代码正确读取了整个内容。

问题似乎在于使用该二进制数据 - 将其打印出来会在第一个空值处截断,看起来只有 10 个字节存在。 您不能使用任何用于字符串或字符数组的方法来输出二进制数据,因为它们会在第一个空字节处截断,使数组看起来比实际短。

查看与查看十六进制数据相关的其他一些问题: how do I print an unsigned char as hex in c++ using ostream? Converting binary data to printable hex

如果你想把它附加到一个字符串 - 以什么格式?十六进制?基数64?原始字节不起作用。

这是我发布的原始代码。一些小的改进,加上一些更好的诊断代码:

int ret, size = 4096; /* Probably needs to be much bigger */
uint8_t *contents;
contents = malloc(size + 1);

if(contents == NULL)
{
    log_message(WARNING, __func__, EMSG_MEMORY);
    return (NULL);
}

ret = read(fd, contents, size);
if(ret < 0)
{
    /* Error reading file */
    free(contents);
    log_message(WARNING, __func__, EMSG_READFILE);
    return (NULL);
}

for(i = 0;i < ret;++i)
{
    printf("%c", contents[i]);
    /* printf("%0.2X", (char) contents[i]); /* Alternatively, print in hex */
}

现在,ret 真的是 10 吗?还是当您尝试打印输出时只得到 10 个字节?

【讨论】:

  • 哇... read() 实际上返回整个大小,但在 gdb 中我只得到这个: $2 = (uint8_t *) 0xb65f2008 "FLV\001\005"
  • 这可能是直到第一个空字节之前的缓冲区。试试我刚刚添加的 printf...如果您的声明不同,也可能是指针问题...
  • 那么您正在阅读整个文件。 gdb 将 char 数组视为字符串,并将在第一个空字节处截断输出。试试“p/x 内容”。
  • 它会 - printf() 只打印一个字符串,直到第一个空值。尝试循环返回的数据,将每个字符打印为 %u
  • @Manuel: strlen 对二进制数据毫无用处,而 FLV 视频显然是这样。
【解决方案2】:

C 库中的“read()”函数不一定会一次性返回整个读取。事实上,如果您要读取大量数据,它通常不会在一次调用中将其提供给您。

解决这个问题的方法是在循环中调用 read(),继续请求更多数据,直到你得到所有数据,或者直到 read 返回错误,由负返回值指示,或 end-of-文件,由零返回值指示。

类似于以下内容(未经测试):

contents = malloc(size + 1);

bytesread = 0;
pos = 0;
while (pos < size && (bytesread = read(fd, contents + pos, size - pos)) > 0)
{
    pos += bytesread;
}

if (bytesread < 0)
{
    free(contents);
    log_message(WARNING, __func__, EMSG_READFILE);
    return (NULL);
}

/* Go on to use 'contents' now, since it's been filled.  Should probably 
   check that pos == size to make sure the file was the size you expected. */

请注意,大多数 C 程序员会以不同的方式执行此操作,可能使 'pos' 成为移动的指针,而不是每次通过循环时从 'contents' 偏移。但我认为这种方法可能更清晰。

【讨论】:

  • +1 用于在循环中调用“读取”。应该检查 malloc 是否由于到期而失败。
  • 是的,代码应该检查 malloc 是否失败。在实践中,malloc 实际上(呵呵)在 Linux 或其他现代操作系统上永远不会失败,如果它失败了,那么只要取消引用内容指针,程序就会出现段错误。奇怪的是,如果 malloc 确实失败了,您唯一的选择就是无论如何都要中止程序。但是,为了正确起见,应该检查 malloc 的返回值。
【解决方案3】:

成功时,read() 返回读取的字节数(可能少于您要求的,此时您应该要求其余的。)在 EOF 上它将返回 0,在错误时它将返回 - 1.对于某些错误,您可能需要考虑重新发出读取(例如,EINTR,当您在读取期间收到信号时会发生这种情况。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-05-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-04
    • 2011-01-19
    • 1970-01-01
    相关资源
    最近更新 更多