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++;
}
精彩评论