【问题标题】:Not printing new lines with printf不使用 printf 打印新行
【发布时间】:2018-06-14 20:23:32
【问题描述】:

我目前正在使用printf("'%.*s'\n", length, start); 打印一个字符串,其中startconst char*,长度是int

正在打印的字符串有时包含换行符\n,这会破坏格式,是否可以在打印输出中将它们替换为字符\n,而不是\n 字符. 如果没有,您能否提供一个替换字符的函数?

该字符串是一个 malloc 的字符串,它具有来自不同指针的其他引用,因此无法更改。

编辑:我编写了以下代码,我认为它可以满足我的需要

static void printStr(const char* start, int length) {
  char* buffer = malloc(length * sizeof(char));
  int processedCount = 0;
  for(int i = 0; i < length; i++) {
    char c = start[i];
    if(c == '\n') {
      printf("%.*s\\n", processedCount, buffer);
      processedCount = 0;
    } else {
      buffer[processedCount] = c;
      processedCount++;
    }
  }
  printf("%.*s", processedCount, buffer);
  free(buffer);
}

【问题讨论】:

  • 是的,这是可能的,但不是printf,而是在打印之前对字符串进行预处理。
  • @EugeneSh。我添加了一些代码来预处理字符串,有什么问题吗?
  • 如果您有问题的答案,请使用下面的“回答您的问题”按钮。不要编辑您的问题以包含答案,因为这会让以后阅读它的人感到困惑。
  • 如果你想避免所有不可打印的字符,一些sample code
  • 通常如果要'\n' --> "\n",那么还需要转义一个'\' 然后'\' --> "\\" 以区分a的情况backslashn\n 的字符串。

标签: c printf


【解决方案1】:

不需要分配内存来处理字符串。简单地说,遍历原始字符并根据需要打印字符。例如:

#include <stdio.h>

void print(const char * str, int length)
{
    for (int i = 0; i < length; ++i) {
        if (str[i] == '\n') {
            putchar('\\');
            putchar('n');
        } else
            putchar(str[i]);
    }
}

int main()
{
    print("hello\nworld!", 12);
    return 0;
}

【讨论】:

    【解决方案2】:

    我会稍微不同地实现您的自定义打印功能:

    #include <stdlib.h>
    #include <ctype.h>
    #include <stdio.h>
    #include <errno.h>
    
    static int output_escaped(FILE *out, const char *str, const char *end)
    {
        int  count = 0;
        while (str < end)
            switch (*str) {
            case '\\': fputs("\\\\", out); count++; break;
            case '\a': fputs("\\a", out);  count++; break;
            case '\b': fputs("\\b", out);  count++; break;
            case '\t': fputs("\\t", out);  count++; break;
            case '\n': fputs("\\n", out);  count++; break;
            case '\v': fputs("\\v", out);  count++; break;
            case '\f': fputs("\\f", out);  count++; break;
            case '\r': fputs("\\r", out);  count++; break;
            default:
                if (isprint((unsigned char)(*str)))
                    fputc(*str, out);
                else {
                    fprintf(out, "\\x%02x", (unsigned char)(*str));
                    count += 3; /* Note: incremented by one later */
                }
            }
            str++;
            count++;
        }
        return count;
    }
    

    带有包装函数

    int escape(const char *str)
    {
        if (!stdout || !str) {
            errno = EINVAL;
            return -1;
        } else
            return output_escaped(stdout, str, str + strlen(str));
    }
    
    int escapen(const char *str, const size_t len)
    {
        if (!stdout || !str) {
            errno = EINVAL;
            return -1;
        } else
            return output_escaped(stdout, str, str + len);
    }
    
    int fescape(FILE *out, const char *str)
    {
        if (!out || !str) {
            errno = EINVAL;
            return -1;
        } else
            return output_escaped(out, str, str + strlen(str));
    }
    
    int fescapen(FILE *out, const char *str, const size_t len)
    {
        if (!out || !str) {
            errno = EINVAL;
            return -1;
        } else
            return output_escaped(out, str, str + len);
    }
    

    当您想要将整个内容或仅一些 len 的第一个字符打印到 stdout 或特定流时,这四个包装器可以满足您的需求。全部返回输出的字符数,如果发生错误(并且设置了 errno),则返回 -1。

    请注意,这会将\ 打印为\\,以及使用十六进制转义序列\x00\xFF 的其他不可打印字符。如果您想要八进制(\001\377)而不是十六进制转义,请使用

                else {
                    fprintf(out, "\\%03o", (unsigned char)(*str));
                    count += 3; /* Note: incremented by one later */
                }
    

    改为。

    (unsigned char) 转换确保字符值永远不会为负。

    【讨论】:

    • @chux:好点;谢谢!现在修好了。我总是记错八进制转义规则;唯一的例外,\0,让我失望。 (这让我觉得单个八进制数字代码都是这样表示的,但事实并非如此。除了零之外,所有八进制数字都是长的。)
    • @chux:是的,当我编辑代码并添加注释时,我做了一个很好的掌上电脑,我也意识到了这一点。 :) 然后我意识到我错过了第一个,并做了另一个。现在我的额头是红色的。为我提供了一个不好的例子。但再次感谢您发现错误!我真的很感激。
    • 您不应将 char 值传递给 isprint()。它仅针对uni=signed char 类型和值EOF 范围内的参数值定义。一个简单的解决方法是isprint((unsigned char)*str)
    • 有对字符进行分类的函数,如isanum()linux.die.net/man/3/isspace)。我用它代替笨拙的switch()...
    • @chqrlie:我怎么会错过这个?我的额头开始酸痛……无论如何,谢谢您指出这一点;现在修复了!事实上,我相信在多年前的某个时候,当我使用 ISO 8859-1/15 语言环境和本地化程序进行 isprint() 或类似检查时,我已经被这一点困扰了。
    【解决方案3】:

    这本质上是一样的,但它使用内存中的strchr 来查看我们可以在转义序列之前准确写入多少个chars,以及fwrites 将它们合二为一。这可能比一次输出一个字符更快,(可能与https://stackoverflow.com/a/37991361/2472827相关。)(未彻底测试。)

    #include <stdlib.h> /* EXIT_ */
    #include <stdio.h>  /* perror fwrite fputs */
    #include <string.h> /* strchr */
    #include <assert.h>
    
    /** Prints the string, escaping newlines to "\n"; a newline will be added at
     the end.
     @return A non-negative number, otherwise EOF; in the latter case, it shall
     set an error indicator for the stream and (may) set {errno} (IEEE Std
     1003.1-2001-conforming systems?) */
    static int printStr(const char* start) {
        const char *so_far = start, *nl;
        size_t nchars;
        assert(start);
        while((nl = strchr(so_far, '\n'))) {
            nchars = nl - so_far;
            if(fwrite(so_far, 1, nchars, stdout) != nchars
                || fwrite("\\n", 1, 2, stdout) != 2) return EOF;
            so_far = nl + 1;
        }
        /* The rest. */
        return fputs(so_far, stdout) == EOF || fputc('\n', stdout) == EOF ? EOF : 1;
    }
    
    int main(void) {
        return printStr("Lalala\n\nlalala\n\n") == EOF
            ? perror("string"), EXIT_FAILURE : EXIT_SUCCESS;
    }
    

    它不需要length,但你可以通过检查nchars来输入,(如果你的字符串不是null-终止的?)

    【讨论】:

      【解决方案4】:

      您在问题中发布的代码似乎有效,但如果内存分配失败可能会出现未定义的行为。可以简化:不需要临时的buffer,可以直接从提供的数组打印,避免分配:

      static void printStr(const char *s, int length) {
          int i, j;
          for (i = j = 0; i < length; i++) {
              if (s[i] == '\n') {
                  printf("%.*s\\n", i - j, s + j);
              }
          }
          if (i > j) {
              printf("%.*s", i - j, s + j);
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2016-08-11
        • 2012-01-21
        • 2015-10-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-06-04
        相关资源
        最近更新 更多