【问题标题】:Ways to interpret groups of bytes in a curl result as other data types将 curl 结果中的字节组解释为其他数据类型的方法
【发布时间】:2015-08-30 22:03:09
【问题描述】:

我正在尝试编写一个程序,该程序将使用 curl 查询 URL 并检索一串字节。返回的数据比需要解释为各种数据类型;一个 int 后跟一个序列结构。

curl 回写函数必须有一个原型:

size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata);

我见过各种示例,其中返回的数据作为字符直接存储在内存中或作为字符串对象存储在缓冲区中。

如果我有一个字符数组,那么我知道我可以用这样的代码将它的一部分解释为一个结构:

struct mystruct {
    //define struct
};

char *buffer;
//push some data into the buffer
char *read_position;
read_position = buffer + 5;
test = (mystruct *)buffer;

我有两个相关的问题。首先,有没有更好的方法使用 curl 检索二进制数据并将其推送到结构中,而不是直接将其作为字符读入内存。其次,如果将内存作为字符缓冲区读取是可行的方法,那么我的代码是否可以将内存块解释为不同的数据类型?

【问题讨论】:

  • 我看到您在C 标记中进行了编辑。请注意,没有“C/C++”这样的语言。
  • 正如我在编辑中所说的那样,我并不热衷于使用任何一种语言,并且乐于考虑使用任何一种语言的解决方案。我更熟悉 C++,希望有一些有用的抽象,例如流,这可能会有所帮助。

标签: c++ c curl


【解决方案1】:

解释原始结构时需要考虑的事项,尤其是通过网络:

  • 数据类型的大小;
  • 数据类型的字节顺序;
  • 结构填充。

无论使用什么编译器,您都应该只在结构中使用大小正确的数据类型。这意味着对于整数,您应该使用types from <cstdint>

至于字节序,您需要知道数据是以大字节序还是小字节序到达。我喜欢明确一点:

template< class T >
const char * ReadLittleEndian32( const char *buf, T & val )
{
    static_assert( sizeof(T) == 4 );
    val = T(buf[0]) | T(buf[1]) << 8 | T(buf[2]) << 16 | T(buf[3]) << 24;
    return buf + sizeof(T);
}

template< class T >
const char * ReadBigEndian32( const char *buf, T & val )
{
    static_assert( sizeof(T) == 4 );
    val = T(buf[0]) << 24 | T(buf[1]) << 16 | T(buf[2]) << 8 | T(buf[3]);
    return buf + sizeof(T);
}

//etc...

最后,处理潜在的填充差异......我已经很自然地倾向于“反序列化”方法,其中每个值都被显式读取和翻译。结构没有什么不同:

struct Foo
{
    uint16_t a;
    int16_t  b;
    int32_t  c;

    const char * Read( const char * buf );
};

const char * Foo::Read( const char * buf )
{
    buf = ReadLittleEndian16( buf, a );
    buf = ReadLittleEndian16( buf, b );
    buf = ReadLittleEndian32( buf, c );
    return buf;
}

请注意模板处理数据类型中的符号和其他内容,因此我们最终关心的只是大小。另请记住,floatdouble 等数据类型已经具有固有的字节顺序,不应进行翻译——它们可以逐字阅读:

const char * ReadDouble( const char * buf, double & val )
{
    val = *(double*)buf;
    return buf + sizeof(double);
}

【讨论】:

  • 我赞成你的回答,因为它非常有帮助,但我没有接受它,因为它没有解决我问题的卷曲部分。此外,您的回答表明我的代码 sn-p(直接复制内存)非常幼稚,我认为您应该在回答中明确说明这一点。
  • 很公平。您应该意识到,如果您有自定义二进制数据,CURL 并不在意。这只是二进制数据。您可以使用 CURL 提供的任何方法来处理它,并按照 认为合适的方式读取数据。那时它与 CURL 无关。它归结为通过网络处理二进制数据的常见做法,我想我已经解决了。我假设您自己在服务器端序列化这些数据,并在客户端反序列化。
  • 我只是觉得效率低下。 curl 调用一个回写函数并将一个字符指针与读取的字节数一起传递给它,这一定意味着数据已经在内存中。简单地将这些数据从内存的一部分复制到另一部分似乎是不必要的步骤。我想知道是否有更好的方法来处理 curl 返回的数据。是的,我在连接的远程端使用 PHP 进行序列化。谢谢你的帮助。
  • 我没有将 CURL 集成到代码中的直接经验,但我怀疑它不会总是在一个回调中提供整个二进制数据。 I/O 回调通常具有固定的缓冲区大小,并为您提供不超过该缓冲区大小的 一些 数据,并在连续回调中返回剩余数据。在这种情况下,您唯一的选择是将数据复制到某个地方,这绝对是正常的做法。
  • 是的,这正是 curl 的工作原理,多次回调。我知道库有一些对象包装器,我想知道是否可以将返回的数据视为流,让提取运算符根据需要填充不同的数据类型。
猜你喜欢
  • 2012-02-22
  • 1970-01-01
  • 1970-01-01
  • 2015-05-23
  • 1970-01-01
  • 2015-06-23
  • 2019-10-28
  • 1970-01-01
  • 2021-04-17
相关资源
最近更新 更多