【问题标题】:Converting special characters (like \n) to their escaped versions将特殊字符(如 \n)转换为其转义版本
【发布时间】:2010-12-07 13:52:13
【问题描述】:

如何在 C(++) 中将 "A\r\nB\tC\nD" 转换为 "A\\r\\nB\\tC\\nD"

最好只使用标准库,并为纯 C 和纯 C++ 解决方案提供额外的支持。

【问题讨论】:

  • 保存的原始字符串是什么? \r/\n 是例如字符(即 0x0D 和 0x0A)还是字符序列(即 \ 后跟 n)?

标签: c++ c string


【解决方案1】:

当然,如果您使用的是宽字符串,请将@​​987654321@ 替换为wchar_t,将std::string 替换为std::wstring

std::string input(/* ... */);
std::string output;
for(std::string::const_iterator it = input.begin(); it != input.end(); ++it)
{
    char currentValue = *it;
    switch (currentValue)
    {
    case L'\t':
        output.append("\\t");
        break;
    case L'\\':
        output.append("\\\\");
        break;
    //.... etc.
    default:
        output.push_back(currentValue);
    }
}

您可以在 C 中执行此操作,但这会更加困难,因为您事先不知道缓冲区大小(尽管您可以在最坏的情况下猜测原始字符串大小的 2 倍)。即

//Disclaimer; it's been a while since I've written pure C, so this may
//have a bug or two.
const char * input = // ...;
size_t inputLen = strlen(input);
char * output = malloc(inputLen * 2);
const char * inputPtr = input;
char * outputPtr = output;
do
{
    char currentValue = *inputPtr;
    switch (currentValue)
    {
    case L'\t':
        *outputPtr++ = '\\';
        *outputPtr = 't';
        break;
    case L'\\':
        *outputPtr++ = '\\';
        *outputPtr = '\\';
        break;
    //.... etc.
    default:
        *outputPtr = currentValue;
    }
} while (++outputPtr, *inputPtr++);

(记得在 C 版本中添加错误处理,例如 malloc 失败;))

【讨论】:

  • 这就是为什么我希望有一些库函数;不过谢谢。
  • @mbq:那就用C++版本就好了;我已经使用它多年并且知道它没有错误 :)(见鬼,如果您使用 C++,那么您正在使用std::string,对吧?:))
  • @Bill 当然,除非我因为与其他代码交互而不得不弄乱 C 代码并且问题很简单,比如 strlen。
  • @mbq:嗯?我不明白。
  • @Bill 例如,您从一个 C 库中获取一个 C 字符串,并希望在使用库将其传递给其他 C 字符串之前检查其大小——此处仅转换为 std::string获取长度是开销。此类事情发生在低级 GUI 和网络上。
【解决方案2】:

这是我想出的……

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

inline char needs_escaping(char val) {
        switch(val) {
                case '\n': return 'n';
                case '\r': return 'r';
                case '\t': return 't';
        }
        return 0;
}

char *escape_string(char *in) {
        unsigned int needed = 0, j = 0, length = strlen(in), i;
        for(i = 0; i < length; i++) {
                if(needs_escaping(in[i])) needed++;
        }

        char *out = malloc(length + needed + 1);
        for(i = 0; i < length; i++) {
                char escape_val = needs_escaping(in[i]);
                if(escape_val) {
                        out[j++] = '\\';
                        out[j++] = escape_val;
                }
                else {
                        out[j++] = in[i];
                }
        }
        out[length + needed] = '\0';    
        return out;
}

int main() {
        char *in  = "A\r\nB\tC\nD";
        char *out = escape_string(in);
        printf("%s\n", out);
        free(out);
        return 0;
}

【讨论】:

  • +1 用于使用数组索引而不是指针的有趣解决方案(尽管如果您在实际代码中使用它,请摊销 strlen 调用 - 对于类似的方法不需要订购 n 平方时间复杂度这个)。
【解决方案3】:

我怀疑是否有任何标准库函数可以直接执行此操作。最有效的方法是简单地逐个字符地迭代输入缓冲区,有条件地复制到输出缓冲区,并使用一些特殊的状态机逻辑来处理 '\' 等。

我确信有多种方法可以通过 strchr() 等的各种组合来做到这一点,但在一般情况下它可能效率较低。

【讨论】:

    【解决方案4】:

    我将创建一个包含 32 个 const char* 文字的查找表,每个控制代码(ASCII 0 到 ASCII 31)一个。然后,我将遍历原始字符串,将非控制字符(ASCII >= 32)复制到输出缓冲区,并将查找表中的值替换为 ASCII 0--31。

    注意 1:ASCII 0 显然对于 C 字符串是特殊的(对于 C++ 则不是。)

    注意 2:查找表将包含 C 转义序列,用于包含它们的代码(\n\r 等)和反斜杠加上十六进制/八进制/十进制代码。

    【讨论】:

      【解决方案5】:

      这是 C# 中的一个算法。也许你可以把它当成伪代码,然后转换成 C++。

      公共静态字符串 EscapeChars(字符串输入) { 字符串输出 = "";

      foreach (char c in Input)
      {
          switch (c)
          {
              case '\n':
                  Output += "\\n";
                  break;
              case '\r':
                  Output += "\\r";
                  break;
              case '\t':
                  Output += "\\t";
                  break;
              default:
                  Output += c;
                  break;
          }                
      }
      return Output;
      

      }

      【讨论】:

      • 这个算法不起作用。例如,\n 不会被转换,因为\n 实际上在源字符串中没有斜杠。这会将“\\n”改为“\\\\n”。
      • 恕我不同意。我实际上在发布之前运行了这个。刚刚尝试使用 \\n 作为输入,它按预期工作 - 它输出 \\n 而不是 \\\\n。 switch...case 语句确保输出中不包含双斜杠,因为当前字符 (c) 必须是“转义字符”。
      • @Bob:但这就是我的观点。输入不是“\n”,它是一个换行符,没有斜杠。示例程序:pastebin.com/M2b1Mv4w
      • 免责声明 - 我将其作为 C# 运行,因为这是一个 C++ 问题,所以我可能没有任何业务要​​做。 ;-)
      • @Bob:我发布的链接也是 C# —— 这两种语言都是错误的。
      猜你喜欢
      • 2015-04-28
      • 2016-07-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-17
      • 2019-05-09
      • 2013-03-08
      • 1970-01-01
      相关资源
      最近更新 更多