【问题标题】:How to struct unpack c null terminated string?如何构造解包 c 空终止字符串?
【发布时间】:2014-09-26 12:54:20
【问题描述】:

我使用 tcp 向 python 服务器发送数据。数据如下:

struct protocol
{
    unsigned char prot;
    int id;
    char name[32];
}

查看name字段,它是一个以空结尾的字符串,最大大小为32。现在我使用strcpy

protocol p;
memset(&p, 0, sizeof(p));
strcpy(name, "abc");

现在我用 python 解压它。

prot,id,name = struct.unpack("@Bi32s")

现在len(name)是32。但是当长度为3时我需要得到"abc"的字符串。

我该怎么做?

【问题讨论】:

    标签: python c


    【解决方案1】:

    解压后你可以做一个:

    name = name.split('\0', 1)[0]
    

    您也可以使用ctypes 模块:

    name = ctypes.create_string_buffer(name).value
    

    【讨论】:

    • 我愿意:name = name.split('\0', 1)[0]
    • +1 我认为这可能是最易读的方式。它可能不如 falsetru 或我的答案那么有效,但对于 32 个字符长的内容可能并不重要
    • 就我而言,我必须使用二进制字符串进行拆分,即mystr = mybuff.split(b'\0', 1)[0]。否则我会得到:TypeError: a bytes-like object is required, not 'str'.
    • @djlauk:在 Python 3 中,struct.unpack() 返回 bytesbytearray 对象,这与 Python 2 中的字符串不同——所以这就是必要的原因。您应该在问题中提到这一点并将其标记为“python-3.x”而不是通用“python”。
    • @martineau:感谢您的洞察力。我最近刚开始从 python 2 迁移到 3,所以有时我会偶然发现这些细节。但是,为了清楚起见:我没有问这个问题(因此没有标记它)。尽管如此,您关于适当标记的提示对于包括我在内的任何人来说肯定是合理的建议,我可能不会想到它。
    【解决方案2】:

    只需将子字符串获取到第一个\0

    prot,id,name = struct.unpack("@Bi32s")
    name= name[:name.index("\0")]
    

    如果字符串中没有出现\0,它将检查并失败(抛出ValueError)。

    【讨论】:

      【解决方案3】:

      解压后用空字符('\0')进行分区:

      >>> prot, id, name = struct.unpack('@Bi32s', b'\0\0\0\0\0\0\0\0abc' + b'\0' * 29)
      >>> name, _, _ = name.partition('\0')
      >>> name
      'abc'
      

      替代使用ctypes:

      >>> from ctypes import *
      >>>
      >>> class Protocol(Structure):
      ...     _fields_ = [("prot", c_char),
      ...                 ("id", c_int),
      ...                 ('name', c_char * 32)]
      ...
      >>> # sock.recv_into(buf) in real program
      ... buf = create_string_buffer(b'\0\0\0\0\0\0\0\0abc' + b'\0' * 29)
      >>> p = cast(buf, POINTER(Protocol))
      >>> p[0].name
      'abc'
      

      【讨论】:

        【解决方案4】:

        如何使用 rstrip 去除填充?

        prot,id,name = struct.unpack("@Bi32s")
        name = name.rstrip(b'\0')
        

        这也将允许您使用所有 32 个字节来存储名称而无需零终止符。但是,这确实依赖于将所有填充字节设置为零。

        【讨论】:

          猜你喜欢
          • 2012-01-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-01-11
          • 2021-06-12
          • 2011-06-04
          • 2010-12-04
          相关资源
          最近更新 更多