【问题标题】:Get str repr with double quotes Python用双引号Python获取str repr
【发布时间】:2010-12-13 02:37:11
【问题描述】:

我正在使用一个小的 Python 脚本来生成一些将在 C 头文件中使用的二进制数据。

此数据应声明为char[],如果可以将其编码为字符串(当它们不在 ASCII 可打印字符范围内时使用相关的转义序列)以保持标题更多,那就太好了比十进制或十六进制数组编码更紧凑。

问题是当我打印 Python 字符串的repr 时,它是用单引号分隔的,而 C 不喜欢这样。天真的解决方案是:

'"%s"'%repr(data)[1:-1]

但是当数据中的一个字节恰好是双引号时,这不起作用,所以我也需要对它们进行转义。

我认为一个简单的 replace('"', '\\"') 可以完成这项工作,但也许有更好、更 Python 的解决方案。

加分项

将数据拆分为大约 80 个字符的行也很方便,但同样大小为 80 的 splitting the source string in chunks 的简单方法不起作用,因为每个不可打印字符在转义序列中占用 2 或 3 个字符. 获得 repr 后将列表拆分为 80 个块也无济于事,因为它可以划分转义序列。

有什么建议吗?

【问题讨论】:

    标签: python c escaping


    【解决方案1】:

    最好不要破解repr(),而是从一开始就使用正确的编码。可以直接用编码string_escape获取repr的编码

    >>> "naïveté".encode("string_escape")
    'na\\xc3\\xafvet\\xc3\\xa9'
    >>> print _
    na\xc3\xafvet\xc3\xa9
    

    对于转义“-quotes,我认为在对字符串进行转义编码后使用简单的替换是一个完全明确的过程:

    >>> '"%s"' % 'data:\x00\x01 "like this"'.encode("string_escape").replace('"', r'\"')
    '"data:\\x00\\x01 \\"like this\\""'
    >>> print _
    "data:\x00\x01 \"like this\""
    

    【讨论】:

    【解决方案2】:

    如果您向 python str 询问它的 repr,我认为引用的类型并不是真正可配置的。来自python 2.6.4源码树中的PyString_Repr函数:

        /* figure out which quote to use; single is preferred */
        quote = '\'';
        if (smartquotes &&
            memchr(op->ob_sval, '\'', Py_SIZE(op)) &&
            !memchr(op->ob_sval, '"', Py_SIZE(op)))
            quote = '"';
    

    所以,如果字符串中有单引号,我想使用双引号,但如果字符串中有双引号,则不要使用。

    我会尝试编写自己的类来包含字符串数据,而不是使用内置字符串来完成它。一种选择是从str 派生一个类并编写自己的repr

    class MyString(str):
        __slots__ = []
        def __repr__(self):
            return '"%s"' % self.replace('"', r'\"')
    
    print repr(MyString(r'foo"bar'))
    

    或者,根本不要使用repr

    def ready_string(string):
        return '"%s"' % string.replace('"', r'\"')
    
    print ready_string(r'foo"bar')
    

    如果字符串中已经有转义的引号,这种简单的引用可能不会做“正确”的事情。

    【讨论】:

      【解决方案3】:

      repr() 不是你想要的。有一个基本问题:repr() 可以使用可以作为 Python 评估的字符串的任何表示来生成字符串。这意味着,理论上,它可能会决定使用任何数量的其他在 C 中无效的构造,例如“”“长字符串”“”。

      这段代码可能是正确的方向。我使用了 140 的默认换行,这对于 2009 年来说是一个合理的值,但如果你真的想将代码换行到 80 列,只需更改它。

      如果 unicode=True,它会输出一个 L"wide" 字符串,它可以有意义地存储 Unicode 转义。或者,您可能希望将 Unicode 字符转换为 UTF-8 并转义输出,具体取决于您使用它们的程序。

      def string_to_c(s, max_length = 140, unicode=False):
          ret = []
      
          # Try to split on whitespace, not in the middle of a word.
          split_at_space_pos = max_length - 10
          if split_at_space_pos < 10:
              split_at_space_pos = None
      
          position = 0
          if unicode:
              position += 1
              ret.append('L')
      
          ret.append('"')
          position += 1
          for c in s:
              newline = False
              if c == "\n":
                  to_add = "\\\n"
                  newline = True
              elif ord(c) < 32 or 0x80 <= ord(c) <= 0xff:
                  to_add = "\\x%02x" % ord(c)
              elif ord(c) > 0xff:
                  if not unicode:
                      raise ValueError, "string contains unicode character but unicode=False"
                  to_add = "\\u%04x" % ord(c)
              elif "\\\"".find(c) != -1:
                  to_add = "\\%c" % c
              else:
                  to_add = c
      
              ret.append(to_add)
              position += len(to_add)
              if newline:
                  position = 0
      
              if split_at_space_pos is not None and position >= split_at_space_pos and " \t".find(c) != -1:
                  ret.append("\\\n")
                  position = 0
              elif position >= max_length:
                  ret.append("\\\n")
                  position = 0
      
          ret.append('"')
      
          return "".join(ret)
      
      print string_to_c("testing testing testing testing testing testing testing testing testing testing testing testing testing testing testing testing testing", max_length = 20)
      print string_to_c("Escapes: \"quote\" \\backslash\\ \x00 \x1f testing \x80 \xff")
      print string_to_c(u"Unicode: \u1234", unicode=True)
      print string_to_c("""New
      lines""")
      

      【讨论】:

      • 'elif "\\\"".find(c) != -1' 和 'elif c in "\\\""' 不一样吗?无论如何我同意, repr() 不是这里的解决方案,你必须做这样的事情。
      • 80 列文本推荐不是基于监视器宽度。它来自排版。看一些新闻报纸:看小报看大报。列有多宽?
      【解决方案4】:

      你可以试试json.dumps:

      >>> import json
      >>> print(json.dumps("hello world"))
      "hello world"
      
      >>> print(json.dumps('hëllo "world"!'))
      "h\u00ebllo \"world\"!"
      

      我不确定 json 字符串是否与 C 兼容,但至少它们有一个相当大的公共子集,并且保证与 javascript 兼容;)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-09-18
        • 2020-01-18
        • 2020-03-28
        • 2015-02-08
        • 2013-10-20
        • 2016-03-26
        相关资源
        最近更新 更多