Существует ли функция стандартной библиотеки для C для выхода из струн до?
Например, если у меня была струна до:
char example[] = "first line\nsecond line: \"inner quotes\"";
И я хотел распечатать
"first line\nsecond line: \"inner quotes\""
Существует ли библиотечная функция, которая сделает то преобразование для меня? Прокрутка моего собственного просто кажется немного глупой.
Бонусные очки, если я могу дать ему длину для выхода (таким образом, это останавливается прежде или вне \0
).
Если вы писали материал под GPL, вы могли бы использовать http://git.savannah.gnu.org/gitweb/?p=gnulib.git;a=blob;f=lib/quotearg.c;hb= HEAD
while(*src++)
{
if(*src == '\\' || *src == '\"' || *src == '\'')
*dest++ = '\\';
*dest++ = *src++;
}
Для этого не существует стандартной библиотечной функции C.
Когда вы используете объявление
char example[] = "first line\nsecond line: \"inner quotes\"";
, escape-последовательности интерпретируются и заменяются компилятором. Вам придется «неинтерпретировать» символы, которые экранирует C. Вот пример быстрого и грязного:
#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);
}
}
}
Никакой стандартной функции C, но не слишком сложно накатить свою собственную
Ничего особенного, кроме: -
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;
}
}
Вы только что упомянули, что хотите напечатать строку.
char example[] = "first line\nsecond line: \"inner quotes\"";
size_t len = 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]);
}
}
Мне показалось, что мой предыдущий ответ был обманом, потому что функция, которая записывает в буфер, намного полезнее, чем функция, которая просто записывает в stdout
, поэтому вот альтернативное решение, которое определяет, сколько памяти нужно, если dst
равно NULL
, а также останавливается на dstLen
согласно требованиям.
Это, вероятно, немного неэффективно со всеми проверками if (dst)
.
#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;
}
#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;
}