问题 如何(un)转义C / C ++中的字符串?


鉴于一个  string(字符数组或类似的包装器) std::string),有没有一种“适当”的方法来逃避和/或用C或C ++来解决它,这样“特殊”字符(如空字符)变成C风格转义,“普通”字符保持原样?

或者我必须手动完成?


10862
2017-09-10 03:02


起源

所以你正在寻找某种转换的STL函数 "\n" 成 "\\n" 运行? - Mateen Ulhaq
@muntoo:是的,确实(反之亦然)。它可能是C - 但事实并非如此 有 成为STL(C ++)的一部分。 - Mehrdad
你想要序列: 0  65  66  67 显示为文本 "\0ABC"? - Marlon
你想让它将字符转换成C会理解的东西,或者只是在特殊字符之前添加反斜杠吗? - icktoofay
你的目标是什么?你是否动态生成C代码? - Adam Rosenfield


答案:


这是一个处理单个字符的函数:

/*
** Does not generate hex character constants.
** Always generates triple-digit octal constants.
** Always generates escapes in preference to octal.
** Escape question mark to ensure no trigraphs are generated by repetitive use.
** Handling of 0x80..0xFF is locale-dependent (might be octal, might be literal).
*/

void chr_cstrlit(unsigned char u, char *buffer, size_t buflen)
{
    if (buflen < 2)
        *buffer = '\0';
    else if (isprint(u) && u != '\'' && u != '\"' && u != '\\' && u != '\?')
        sprintf(buffer, "%c", u);
    else if (buflen < 3)
        *buffer = '\0';
    else
    {
        switch (u)
        {
        case '\a':  strcpy(buffer, "\\a"); break;
        case '\b':  strcpy(buffer, "\\b"); break;
        case '\f':  strcpy(buffer, "\\f"); break;
        case '\n':  strcpy(buffer, "\\n"); break;
        case '\r':  strcpy(buffer, "\\r"); break;
        case '\t':  strcpy(buffer, "\\t"); break;
        case '\v':  strcpy(buffer, "\\v"); break;
        case '\\':  strcpy(buffer, "\\\\"); break;
        case '\'':  strcpy(buffer, "\\'"); break;
        case '\"':  strcpy(buffer, "\\\""); break;
        case '\?':  strcpy(buffer, "\\\?"); break;
        default:
            if (buflen < 5)
                *buffer = '\0';
            else
                sprintf(buffer, "\\%03o", u);
            break;
        }
    }
}

这是处理以null结尾的字符串的代码(使用上面的函数):

void str_cstrlit(const char *str, char *buffer, size_t buflen)
{
    unsigned char u;
    size_t len;

    while ((u = (unsigned char)*str++) != '\0')
    {
        chr_cstrlit(u, buffer, buflen);
        if ((len = strlen(buffer)) == 0)
            return;
        buffer += len;
        buflen -= len;
    }
}

10
2017-09-10 05:10



我也可以自己实现它,但我希望有某种标准化的解决方案。但我想还是没有...... +1谢谢。 - Mehrdad
我相信你可以;我相信我不是唯一拥有的人。我没有遇到过“标准”解决方案。我编写了我在2001年9月展示的代码,并在2007年3月更新了它。它只是copy'n'paste,省略了测试代码和版本控制信息。 - Jonathan Leffler
str_cstrlit 对零长度字符串不起作用,缓冲区没有零终止符。我编辑了代码,但无论出于何种原因,同行评审都失败了。只要确保写 buffer[0] = '\0' 在while循环中。 - lama12345
@ lama12345:嗯...是的,你是对的。它也需要加强一些断言(str != 0 && buffer != 0 && buflen != 0, 例如)。在我编写代码的原始上下文中,零长度字符串永远不可能,因此不需要额外的检查。但作为一般解决方案,它需要一些调整。该 void 返回在上下文中也是正常的 - 输入不会造成麻烦。编写通用库代码很难。 - Jonathan Leffler


而不是分配一个新的缓冲区来包含转义的字符串,我喜欢在将它写入流时转义我的字符串。

以下函数可实现可读且简洁的代码。

struct Escaped
{
    const char* str;

    friend inline std::ostream& operator<<(std::ostream& os, const Escaped& e)
    {
        for (const char* char_p = e.str; *char_p != '\0'; char_p++)
        {
            switch (*char_p)
            {
                case '\a':  os << "\\a"; break;
                case '\b':  os << "\\b"; break;
                case '\f':  os << "\\f"; break;
                case '\n':  os << "\\n"; break;
                case '\r':  os << "\\r"; break;
                case '\t':  os << "\\t"; break;
                case '\v':  os << "\\v"; break;
                case '\\':  os << "\\\\"; break;
                case '\'':  os << "\\'"; break;
                case '\"':  os << "\\\""; break;
                case '\?':  os << "\\\?"; break;
                default: os << *char_p;
            }
        }
        return os;
    }
};

int main()
{
    std::cout << Escaped{ "foo\n\tbar" } << std::endl;
}

产生

foo\n   bar

0
2018-03-26 21:07



请阅读问题。 - Mehrdad
@Mehrdad:这个答案出了什么问题? - Lightness Races in Orbit
@Mehrat,您在评论中说过“目标是以已知的,人类可读的形式显示任意字符串”。我的代码实现了这个目标,对吧?我读过这个问题;对您的反馈更有建设性。 - Tim Kuipers
@LightnessRacesInOrbit:字面意思 加粗 在问题中。 - Mehrdad
@Mehrdad:如果你完全拒绝这个答案和蒂姆的阅读技巧是基于这个例子的事实 const char* 而不是 std::string 那似乎有点小气。你可以简单地改变循环前导码。蒂姆回答的重点是输入是否“计数”并不重要。 - Lightness Races in Orbit