【问题标题】:Converting escape sequence in C [duplicate]在C中转换转义序列[重复]
【发布时间】:2017-04-06 17:27:13
【问题描述】:

我有一个来自某些用户输入的名为 ArrayA 的字符数组,其中可能包含转义序列字符。我想将 ArrayA 逐个字符复制到 ArrayB 中。话虽如此,我将如何将 ArrayA 中的转义字符表示为 ArrayB?如果我要正确打印 ArrayB,简单地复制字符实际上不会将 \t 转换为选项卡吗?它只是将 \t 作为一个字符而不是正确的?我可以在 ArrayB 中输入 0x09 作为 \t 转义序列,以便在打印 ArrayB 时实际在数组中的特定位置打印一个选项卡吗?

【问题讨论】:

  • 我看到了,但我有点困惑,因为我认为需要使用十六进制字节将转义序列转换回制表符(在这种情况下为 \t)
  • 如果它是ArrayA 中的转义符,例如'\t',那么它也已经是0x09,一个选项卡。所以你直接把它复制过来。也就是说,我假设ArrayA 是一个'\t',它是0x09 的别名,而不是两个字符'\''t'
  • 因此,如果用户输入 \\t 它将是转义序列 \t 将存储到我的 char 数组中对吗?在那种情况下,如果我将 char 数组复制到另一个 char 数组并打印出来,我不需要做任何奇怪的转换来让它在打印这个新的 char 数组时输出一个选项卡?
  • 一次读取一个字节。如果不是反斜杠,请复制它。否则,查看下一个字节是什么。如果那是t,则发出'\t',否则会因错误而中止。

标签: c regex linux unix


【解决方案1】:

函数cstrlit_chr()

这个怎么样:

/* Convert C Character Literal in (str..end] (excluding surrounding quotes) */
/* to character, returning converted char or -1 if string is invalid. */

/* Convert string containing C character literal to character value */
/* Returns -1 if character literal is invalid, otherwise 0x00..0xFF */
/* Does not support extension \E for ESC \033. */
/* Does not support any extension for DEL \177. */
/* Does not support control-char notation ^A for CTRL-A \001. */
/* Accepts \z as valid z when z is not otherwise special. */
/* Accepts \038 as valid CTRL-C \003; next character starts with the 8. */
/* Accepts \x3Z as valid CTRL-C \003; next character starts with the Z. */
/* Treats invalid octal escape \8 or \9 as 8 or 9 */
int cstrlit_chr(const char *str, const char *end, char const ** const eptr)
{
    unsigned char u;
    int rv;

    if (str >= end)
        rv = -1;    /* String contains no data */
    else if ((u = *str++) != '\\')
        rv = u;
    else if (str == end)
        rv = -1;    /* Just a backslash - invalid */
    else if ((u = *str++) == 'x')
    {
        /**
        ** Hex character constant - \xHH or \xH, where H is a hex digit.
        ** Technically, can be \xHHH too, if CHAR_BIT > 8; this nicety
        ** is being studiously ignored.
        */
        int x1;
        int x2;
        if (str == end)
            rv = -1;
        else if ((x1 = basedigit(*str++, 16)) < 0)
        {
            rv = -1;        /* Invalid hex constant */
            str--;
        }
        else if (str == end)
            rv = x1;        /* Single digit hex constant */
        else if ((x2 = basedigit(*str++, 16)) < 0)
        {
            rv = x1;        /* Single-digit hex constant */
            str--;
        }
        else
            rv = (x1 << 4) | x2;    /* Double-digit hex constant */
    }
    else if (isdigit(u))
    {
        /**
        ** Octal character constant - \O or \OO or \OOO, where O is an
        ** octal digit.  Technically, the constant extends for an
        ** indefinite number of octal digits; this nicety is being
        ** studiously ignored.  Treat \8 as 8 and \9 as 9.
        */
        int o1;
        int o2;
        int o3;
        if ((o1 = basedigit(u, 8)) < 0)
            rv = u; /* Invalid octal constant (\8 or \9) */
        else if (str == end)
            rv = o1;    /* Single-digit octal constant */
        else if ((o2 = basedigit(*str++, 8)) < 0)
        {
            rv = o1;    /* Single-digit octal constant */
            str--;
        }
        else if (str == end)
            rv = (o1 << 3) | o2;    /* Double-digit octal constant */
        else if ((o3 = basedigit(*str++, 8)) < 0)
        {
            rv = (o1 << 3) | o2;    /* Double-digit octal constant */
            str--;
        }
        else if (o1 >= 4)
            rv = -1;                /* Out of range 0x00..0xFF (\000..\377) */
        else
            rv = (((o1 << 3) | o2) << 3) | o3;
    }
    else
    {
        /* Presumably \a, \b, \f, \n, \r, \t, \v, \', \", \? or \\ - or an error */
        switch (u)
        {
        case 'a':
            rv = '\a';
            break;
        case 'b':
            rv = '\b';
            break;
        case 'f':
            rv = '\f';
            break;
        case 'n':
            rv = '\n';
            break;
        case 'r':
            rv = '\r';
            break;
        case 't':
            rv = '\t';
            break;
        case 'v':
            rv = '\v';
            break;
        case '\"':
            rv = '\"';
            break;
        case '\'':
            rv = '\'';
            break;
        case '\?':
            rv = '\?';
            break;
        case '\\':
            rv = '\\';
            break;
        case '\0':  /* Malformed: solitary backslash followed by NUL */
            rv = -1;
            break;
        default:
            rv = u; /* Nominally invalid: \X but X not special; return X. */
            break;
        }
    }
    if (eptr != 0)
        *eptr = str;
    return(rv);
}

它处理 C89 字符序列;它不处理 Unicode(通用)字符(\uXXXX\U00XXXXXX)。

函数basedigit()

/*
** Convert character to digit in given base,
** returning -1 for invalid bases and characters.
*/
int basedigit(char c, int base)
{
    int             i;

#if (('z' - 'a') != 25 || ('Z' - 'A') != 25)
#error Faulty Assumption
This code assumes the code set is ASCII, ISO 646, ISO 8859, or something similar.
#endif /* Alphabet test */
    if (base < 2 || base > 36)
        i = -1;
    else if (c >= '0' && c <= '9')
        i = c - '0';
    else if (c >= 'A' && c <= 'Z')
        i = c - 'A' + 10;
    else if (c >= 'a' && c <= 'z')
        i = c - 'a' + 10;
    else
        i = -1;
    return((i < base) ? i : -1);
}

示例用法

/* Sample usage */

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

int main(void)
{
    const char  data[] = "ab\\xFF\\03\\7\\377\\t\\?\\'\\\\yz";
    const char *end    = data + strlen(data);
    const char *start  = data;
    const char *next;
    int   c;
    while ((c = cstrlit_chr(start, end, &next)) != -1)
    {
        char buffer[20];
        snprintf(buffer, sizeof(buffer), "[[%.*s]]", (int)(next-start), start);
        printf("%3d (0x%.2X) %-10s - [[%s]]\n",  c, c & 0xFF, buffer, next);
        start = next;
    }
    return 0;
}

请注意,扫描的范围由指向开始字符的指针标识,结束由指向范围结束后字符的指针标识(在示例中,字符串末尾的'\0',但该函数适用于任意数据,并且不需要以空值结尾。输入字符串在源代码中具有双倍的反斜杠,因此实际字符串包含单个反斜杠。

如果c == -1,则转换失败。否则,c 包含字符,end 是指向转换完成位置的指针。

【讨论】:

  • 次要位:建议if (isdigit(u) &amp;&amp; (u &lt; '8')) 而不是if (isdigit(u))。 C11 6.4.4.4 1 意味着八进制序列限制为 3。第 7 段说“每个八进制或十六进制转义序列是可以构成转义序列的最长字符序列。”所以这样的序列有额外的限制。不错的答案,但可以在 endeptr 上使用一些指导。
  • @chux:代码将八进制常量限制为 3 位。它将错误数据"\\8\\9" 视为等同于"89";它可以升级为将它们视为错误。在较长的序列中,例如"\\038",它将解释为"\03" (Control-C) 后跟 8。请注意,十六进制字符串不受语法限制为 2 个十六进制数字。你的旅费可能会改变;它对我来说效果很好。
猜你喜欢
  • 1970-01-01
  • 2014-08-15
  • 1970-01-01
  • 2020-06-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多