转义 C++ 字符串

Posted

技术标签:

【中文标题】转义 C++ 字符串【英文标题】:Escaping a C++ string 【发布时间】:2010-03-10 14:26:50 【问题描述】:

将 C++ std::string 转换为另一个 std::string 的最简单方法是什么,其中所有不可打印的字符都已转义?

例如,对于两个字符的字符串 [0x61,0x01],结果字符串可能是“a\x01”或“a%01”。

【问题讨论】:

最简单的逃生方法是通过\0旁边的紧急舱口。 【参考方案1】:

看看 Boost 的String Algorithm Library。您可以使用它的is_print 分类器(连同它的运算符!重载)来挑选不可打印的字符,它的find_format() 函数可以用您希望的任何格式替换这些字符。

#include <iostream>
#include <boost/format.hpp>
#include <boost/algorithm/string.hpp>

struct character_escaper

    template<typename FindResultT>
    std::string operator()(const FindResultT& Match) const
    
        std::string s;
        for (typename FindResultT::const_iterator i = Match.begin();
             i != Match.end();
             i++) 
            s += str(boost::format("\\x%02x") % static_cast<int>(*i));
        
        return s;
    
;

int main (int argc, char **argv)

    std::string s("a\x01");
    boost::find_format_all(s, boost::token_finder(!boost::is_print()), character_escaper());
    std::cout << s << std::endl;
    return 0;

【讨论】:

【参考方案2】:

假设执行字符集是 ASCII 的超集并且 CHAR_BIT 是 8。对于 OutIter 传递一个 back_inserter(例如em>vector 或其他字符串)、ostream_iterator 或任何其他合适的输出迭代器。

template<class OutIter>
OutIter write_escaped(std::string const& s, OutIter out) 
  *out++ = '"';
  for (std::string::const_iterator i = s.begin(), end = s.end(); i != end; ++i) 
    unsigned char c = *i;
    if (' ' <= c and c <= '~' and c != '\\' and c != '"') 
      *out++ = c;
    
    else 
      *out++ = '\\';
      switch(c) 
      case '"':  *out++ = '"';  break;
      case '\\': *out++ = '\\'; break;
      case '\t': *out++ = 't';  break;
      case '\r': *out++ = 'r';  break;
      case '\n': *out++ = 'n';  break;
      default:
        char const* const hexdig = "0123456789ABCDEF";
        *out++ = 'x';
        *out++ = hexdig[c >> 4];
        *out++ = hexdig[c & 0xF];
      
    
  
  *out++ = '"';
  return out;

【讨论】:

我认为 && 是一个非常好的运算符。你甚至可以使用它而不需要额外的头文件。 您也可以在标准 C++ 中使用没有标头的 。这是从另一个项目中复制的,我忘记更改以弥补 MSVC 的不足。 你为什么写成需要back_inserter 才能传入?不是简单地按值返回一个字符串(这意味着无论如何都要移动它)就可以了吗?【参考方案3】:

假设“最简单的方法”意味着简短且易于理解,而不依赖于任何其他资源(如库),我会这样:

#include <cctype>
#include <sstream>

// s is our escaped output string
std::string s = "";
// loop through all characters
for(char c : your_string)

    // check if a given character is printable
    // the cast is necessary to avoid undefined behaviour
    if(isprint((unsigned char)c))
        s += c;
    else
    
        std::stringstream stream;
        // if the character is not printable
        // we'll convert it to a hex string using a stringstream
        // note that since char is signed we have to cast it to unsigned first
        stream << std::hex << (unsigned int)(unsigned char)(c);
        std::string code = stream.str();
        s += std::string("\\x")+(code.size()<2?"0":"")+code;
        // alternatively for URL encodings:
        //s += std::string("%")+(code.size()<2?"0":"")+code;
    

【讨论】:

我非常喜欢这个答案,尽管摆弄了 stringstream、std::hex 和多个强制转换。 else 块中的 char hex[5] = ""; ssize_t len = snprintf(hex, 5, "\\x%02x", c); s += std::string(hex, len); 之类的东西也可以工作吗,还是有一些我没有看到的问题? OP 没有特别要求纯 C++ 解决方案,但我认为这听起来像是他更喜欢 C++。所以我把自己限制在这一点上。但是,是的,就我而言,您的代码可以正常工作。 (而且会短一些。) 解决方案中存在错误。 isprint 方法需要一个 int >-1 和 127 的字符会遇到问题,这意味着一个负数 char c 这是我的版本:gist.github.com/timmi-on-rails/173c496a9c5a33ad9df7c6428b9a077b @Tom 你是对的。我在 stringstream 部分正确地转换了字符,但不在isprint 内部。尽管测试了更高的 ASCII 代码,但我错过了这一点,因为 gcc 的实现似乎总是返回 false ,除非它是可打印的字符。尽管如此,未定义的行为是邪恶的,所以我更正了我的原始代码。【参考方案4】:

一个人的不可打印字符是另一个人的多字节字符。因此,您必须先定义编码,然后才能确定哪些字节映射到哪些字符,以及哪些是不可打印的。

【讨论】:

【参考方案5】:

你看过关于如何Generate Escaped String Output Using Spirit.Karma的文章吗?

【讨论】:

以上是关于转义 C++ 字符串的主要内容,如果未能解决你的问题,请参考以下文章

C++中如何禁止转义字符转义?比如"C:\node"代表一个在C盘下的node文件夹。

小白学习C++ 教程九C++中字符型字符串和转义字符

在 C++ 中定义 std::string 没有转义字符

C++入门:第十一个程序

在 C++ 中解释转义的 unicode 字符

使用 C++ 在运行时使用转义序列