开发者

standard c library for escaping a string

开发者 https://www.devze.com 2022-12-28 15:26 出处:网络
Is there a standard C library function to escape C-strings? For example, if I had the C string: char example[] = \"first line\\nsecond line: \\\"inner quotes\\\"\";

Is there a standard C library function to escape C-strings?

For example, if I had the C string:

char example[] = "first line\nsecond line: \"inner quotes\"";

And I wanted to pri开发者_如何学Cnt

"first line\nsecond line: \"inner quotes\""

Is there a library function that will do that transformation for me? Rolling my own just seems a little silly.

Bonus points if I can give it a length to escape (so it stops before or beyond the \0).


If you were writing GPL stuff you might use http://git.savannah.gnu.org/gitweb/?p=gnulib.git;a=blob;f=lib/quotearg.c;hb=HEAD


There is no standard C library function for this.

When you use the declaration

char example[] = "first line\nsecond line: \"inner quotes\"";

the escape sequences will be interpreted and replaced by the compiler. You will have to "un-interpret" the characters that C escapes. Here's a quick-n-dirty example:

#include <stdio.h>
#include <ctype.h>

void print_unescaped(char* ptr, int len) {
    if (!ptr) return;
    for (int i = 0; i < len; i++, ptr++) {
        switch (*ptr) {
            case '\0': printf("\\0");  break;
            case '\a': printf("\\a");  break;
            case '\b': printf("\\b");  break;
            case '\f': printf("\\f");  break;
            case '\n': printf("\\n");  break;
            case '\r': printf("\\r");  break;
            case '\t': printf("\\t");  break;
            case '\v': printf("\\v");  break;
            case '\\': printf("\\\\"); break;
            case '\?': printf("\\\?"); break;
            case '\'': printf("\\\'"); break;
            case '\"': printf("\\\""); break;
            default:
                if (isprint(*ptr)) printf("%c",     *ptr);
                else               printf("\\%03o", *ptr);
        }
    }
}


#include <string.h>    
/* int c_quote(const char* src, char* dest, int maxlen)
 *
 * Quotes the string given so that it will be parseable by a c compiler.
 * Return the number of chars copied to the resulting string (including any nulls)
 *
 * if dest is NULL, no copying is performed, but the number of chars required to 
 * copy will be returned.
 *
 * maxlen characters are copied. If maxlen is negative, 
 * strlen is used to find the length of the source string, and the whole string
 * including the NULL-terminator is copied.
 *
 * Note that this function will not null-terminate the string in dest.
 * If the string in src is not null-terminated, or maxlen is specified to not
 * include the whole src, remember to null-terminate dest afterwards.
 *
 */
int c_quote(const char* src, char* dest, int maxlen) {
    int count = 0;
    if(maxlen < 0) {
        maxlen = strlen(src)+1;      /* add 1 for NULL-terminator */
    }

    while(src  && maxlen > 0) {
        switch(*src) {

            /* these normal, printable chars just need a slash appended */
            case '\\':
            case '\"':
            case '\'':
                if(dest) {
                    *dest++ = '\\';
                    *dest++ = *src;
                }
                count += 2;
                break; 

            /* newlines/tabs and unprintable characters need a special code.
             * Use the macro CASE_CHAR defined below.
             * The first arg for the macro is the char to compare to,
             * the 2nd arg is the char to put in the result string, after the '\' */
#define CASE_CHAR(c, d) case c:\
    if(dest) {\
        *dest++ = '\\'; *dest++ = (d);\
        }\
count += 2;\
break;
            /* --------------  */
            CASE_CHAR('\n', 'n');
            CASE_CHAR('\t', 't');
            CASE_CHAR('\b', 'b');
            /*  ------------- */

#undef CASE_CHAR


            /* by default, just copy the char over */
            default:
                if(dest) {
                    *dest++ = *src;
                }
                count++;
        }

        ++src;
        --maxlen;
    }
    return count;
}


You just mentioned that you wanted to print the string.

char example[] = "first line\nsecond line: \"inner quotes\"";
size_t length = strlen(example);
size_t i;

static const char *simple = "\\\'\"";
static const char *complex = "\a\b\f\n\r\t\v";
static const char *complexMap = "abfnrtv";

for (i = 0; i < length; i++)
{
    char *p;
    if (strchr(simple, example[i]))
    {
        putchar('\\');
        putchar(example[i]);
    }
    else if ((p = strchr(complex, example[i]))
    {
        size_t idx = p - complex;
        putchar('\\');
        putchar(complexMap[idx]);
    }
    else if (isprint(example[i]))
    {
        putchar(example[i]);
    }
    else
    {
        printf("\\%03o", example[i]);
    }
}


No standard C function, but not too hard to roll your own

nothing too pretty but :-

void escape_str(char *dest, char *src)
{
     *dest = 0;
     while(*src)
     {
         switch(*src)
         {
             case '\n' : strcat(dest++, "\\n"); break;
             case '\"' : strcat(dest++, "\\\""); break;
             default:  *dest = *src;
         }
         *src++;
         *dest++;
         *dest = 0;                     
     }
}


I felt like my previous answer was cheating because a function that writes to a buffer is much more useful than one that simply writes to stdout, so here's an alternative solution which works out how much memory one needs if dst is NULL, and also stops at dstLen as per the requirement.

It's probably a little inefficient with all the if(dst) checking.

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

size_t str_escape(char *dst, const char *src, size_t dstLen)
{
    const char complexCharMap[] = "abtnvfr";

    size_t i;
    size_t srcLen = strlen(src);    
    size_t dstIdx = 0;

    // If caller wants to determine required length (supplying NULL for dst)
    // then we set dstLen to SIZE_MAX and pretend the buffer is the largest
    // possible, but we never write to it. Caller can also provide dstLen
    // as 0 if no limit is wanted.
    if (dst == NULL || dstLen == 0) dstLen = SIZE_MAX;

    for (i = 0; i < srcLen && dstIdx < dstLen; i++)
    {
        size_t complexIdx = 0;

        switch (src[i])
        {
            case '\'':
            case '\"':
            case '\\':
                if (dst && dstIdx <= dstLen - 2)
                {
                    dst[dstIdx++] = '\\';
                    dst[dstIdx++] = src[i];
                }
                else dstIdx += 2;
                break;

            case '\r': complexIdx++;
            case '\f': complexIdx++;
            case '\v': complexIdx++;
            case '\n': complexIdx++;
            case '\t': complexIdx++;
            case '\b': complexIdx++;
            case '\a':
                if (dst && dstIdx <= dstLen - 2)
                {
                    dst[dstIdx++] = '\\';
                    dst[dstIdx++] = complexCharMap[complexIdx];
                }
                else dstIdx += 2;
                break;

            default:
                if (isprint(src[i]))
                {
                    // simply copy the character
                    if (dst)
                        dst[dstIdx++] = src[i];
                    else
                        dstIdx++;
                }
                else
                {
                    // produce octal escape sequence
                    if (dst && dstIdx <= dstLen - 4)
                    {
                        dst[dstIdx++] = '\\';
                        dst[dstIdx++] = ((src[i] & 0300) >> 6) + '0';
                        dst[dstIdx++] = ((src[i] & 0070) >> 3) + '0';
                        dst[dstIdx++] = ((src[i] & 0007) >> 0) + '0';
                    }
                    else
                    {
                        dstIdx += 4;
                    }
                }
        }
    }

    if (dst && dstIdx <= dstLen)
        dst[dstIdx] = '\0';

    return dstIdx;
}


while(*src++)
{
  if(*src == '\\' || *src == '\"' || *src == '\'')
    *dest++ = '\\';

  *dest++ = *src++;
}
0

精彩评论

暂无评论...
验证码 换一张
取 消