【问题标题】:Centering strings with printf()使用 printf() 使字符串居中
【发布时间】:2010-03-17 11:06:24
【问题描述】:

默认情况下,printf() 似乎将字符串向右对齐。

printf("%10s %20s %20s\n", "col1", "col2", "col3");
/*       col1                 col2                 col3 */

我也可以像这样将文本向左对齐:

printf("%-10s %-20s %-20s", "col1", "col2", "col3");

有没有一种快速的方法来使文本居中?或者,如果该列的文本宽度为 8,我是否必须编写一个函数,将 test 之类的字符串转换为 (space)(space)test(space)(space)

【问题讨论】:

    标签: c printf


    【解决方案1】:

    printf 本身无法解决问题,但您可以使用“间接”宽度,它通过从参数中读取宽度来指定宽度。让我们试试这个(好吧,不完美)

    void f(char *s)
    {
            printf("---%*s%*s---\n",10+strlen(s)/2,s,10-strlen(s)/2,"");
    }
    int main(int argc, char **argv)
    {
            f("uno");
            f("quattro");
            return 0;
    }
    

    【讨论】:

    • 我在我为创建表而编写的一些 C++ 代码中对此进行了测试,它截断了有效文本,我必须找出原因。我已经根据我的解决方法提供了一个替代答案。
    • 正确,确实我警告说解决方案“不完美”,因为它包含硬编码常量。我的目标只是提供一个提示,您的解决方案完成了这个想法。
    • * 期望 intstrlen() 将返回 size_t。此函数在SIZE_MAX>INT_MAX 时导致UB。可能的解决方案:将其更改为(int) ((10+strlen(s)/2)&INT_MAX)
    • 正确。在大多数情况下,将 strlen 直接转换为 int 就足够了。应该居中的字符串的长度不太可能超过 INT_MAX。
    【解决方案2】:

    @GiuseppeGuerrini 通过建议如何使用打印格式说明符和划分空格来提供帮助。不幸的是,它会截断文本。

    下面解决截断的问题(假设指定的字段实际上大到足以容纳文本)。

    void centerText(char *text, int fieldWidth) {
        int padlen = (fieldWidth - strlen(text)) / 2;
        printf("%*s%s%*s\n", padLen, "", text, padlen, "");
    } 
    

    【讨论】:

    • 请注意,根据fieldWidth - strlen(text) 是偶数还是奇数,得到的总宽度会有所不同
    • 添加了始终打印fieldWidth 字符的修改版本(例如,用于表格等)
    • 行为是当(fieldWidth - strlen(text)) / 2;的结果超出int的范围时定义的实现。 SIZE_MAX>INT_MAXstrlen(text)>fieldWidth 可能是这种情况,导致 (fieldWidth - strlen(text)) == SIZE_MAX-(strlen(text)-fieldWidth-1)
    【解决方案3】:

    没有printf() 格式说明符来居中文本。

    您将需要编写自己的函数或找到提供您正在寻找的功能的库。

    【讨论】:

    • 你有机会多走 9 码来提供解决方案,嗯?
    【解决方案4】:

    您可以尝试为这个问题编写自己的函数。

    /**
     * Returns a sting "str" centered in string of a length width "new_length".
     * Padding is done using the specified fill character "placeholder".
     */
    char *
    str_center(char str[], unsigned int new_length, char placeholder)
    {
        size_t str_length = strlen(str);
    
        // if a new length is less or equal length of the original string, returns the original string
        if (new_length <= str_length)
            return str;
    
        char *buffer;
        unsigned int i, total_rest_length;
    
        buffer = malloc(sizeof(char) * new_length);
    
        // length of a wrapper of the original string
        total_rest_length = new_length - str_length;
    
        // write a prefix to buffer
        i = 0;
        while (i < (total_rest_length / 2)) {
            buffer[i] = placeholder;
            ++i;
        }
        buffer[i + 1] = '\0';
    
        // write the original string
        strcat(buffer, str);
    
        // write a postfix to the buffer
        i += str_length;
        while (i < new_length) {
            buffer[i] = placeholder;
            ++i;
        }
        buffer[i + 1] = '\0';
    
        return buffer;
    }
    

    结果:

    puts(str_center("A", 0, '-')); // A
    puts(str_center("A", 1, '-')); // A
    puts(str_center("A", 10, '-')); // ----A-----
    puts(str_center("text", 10, '*')); // ***text***
    puts(str_center("The C programming language", 26, '!')); // The C programming language
    puts(str_center("The C programming language", 27, '!')); // The C programming language!
    puts(str_center("The C programming language", 28, '!')); // !The C programming language!
    puts(str_center("The C programming language", 29, '!')); // !The C programming language!!
    puts(str_center("The C programming language", 30, '!')); // !!The C programming language!!
    puts(str_center("The C programming language", 31, '!')); // !!The C programming language!!!
    

    【讨论】:

    • 很酷,它是 c/stdlib 唯一的实现,用于教育 C 的观点,但至少用 memcpy()bcopy() 替换前缀/后缀循环
    • 并将 malloc 替换为 alloca.. ewww
    • alloca 在这种情况下不起作用,因为返回的字符串在该内存中 - 您不希望它在“str_center”返回时被释放!但正如 Martin 指出的那样,正如所写的那样,这会泄漏内存。
    【解决方案5】:

    在处理了尝试使用 printf 将表格标题居中的类似问题后,我将减少 2 美分。

    以下宏需要在文本之前/之后打印,并且无论文本本身的长度如何,都将对齐。 请注意,如果我们有奇数长度的字符串,我们将不会按应有的方式对齐(因为正常的划分会导致缺少空格)。 因此需要进行汇总,我认为这是解决该问题的优雅方法:

    #define CALC_CENTER_POSITION_PREV(WIDTH, STR) (((WIDTH + ((int)strlen(STR))) % 2) \
           ? ((WIDTH + ((int)strlen(STR)) + 1)/2) : ((WIDTH + ((int)strlen(STR)))/2))
    #define CALC_CENTER_POSITION_POST(WIDTH, STR) (((WIDTH - ((int)strlen(STR))) % 2) \
           ? ((WIDTH - ((int)strlen(STR)) - 1)/2) : ((WIDTH - ((int)strlen(STR)))/2))
    

    使用示例:

    printf("%*s%*s" , CALC_CENTER_POSITION_PREV(MY_COLUMN_WIDTH, "Header")
                    , "Header"
                    , CALC_CENTER_POSITION_POST(MY_COLUMN_WIDTH, "Header"), "");
    

    【讨论】:

      【解决方案6】:

      是的,您要么必须编写自己的函数来返回“测试”等,例如

      printf("%s %s %s", center("col1", 10), center("col2", 20), center("col3", 20));
      

      或者你有一个 center_print 函数,如下所示:

      void center_print(const char *s, int width)
      {
              int length = strlen(s);
              int i;
              for (i=0; i<=(width-length)/2; i++) {
                      fputs(" ", stdout);
              }
              fputs(s, stdout);
              i += length;
              for (; i<=width; i++) {
                      fputs(" ", stdout);
              }
      }
      

      【讨论】:

      • 第一个建议:如何实现不泄漏内存?
      • 如果您根据一些看起来不合理的标准预分配一些缓冲区(例如,一个 printf 的居中参数不超过 20 个,并且居中的结果都不会超过 200 字节),您可以让中心函数在每次调用时只旋转缓冲区。
      【解决方案7】:

      上面 PADYMKO 函数的更紧凑版本(仍然会泄漏内存):

      char *str_center(char str[], unsigned int new_length, char placeholder)
      {
          size_t str_length = strlen(str);
          char *buffer;
          /*------------------------------------------------------------------
           * If a new length is less or equal length of the original string, 
           * returns the original string 
           *------------------------------------------------------------------*/
          if (new_length <= str_length)
          {
              return(str);
          }
          buffer = malloc(sizeof(char) * (new_length + 1));
          memset(buffer, placeholder, new_length);
          buffer[new_length] = '\0';
          bcopy(str, buffer + (( new_length - str_length) / 2), str_length);
          return(buffer);
      }
      

      这会将整个新分配的缓冲区设置为填充字符,null 终止该字符,然后将要居中的字符串放置在缓冲区的中间 - 没有循环,或者跟踪复制到的位置..

      【讨论】:

        【解决方案8】:

        如果您希望能够为此使用printf() 格式字符串并且您接受限于GNU clib,您可以使用您自己的转换说明符扩展printf() 以使字符串居中。使用register_printf_function() 添加转换说明符。 有关文档,请参见此处:https://www.gnu.org/software/libc/manual/html_node/Customizing-Printf.html 其他答案已经为您提供了如何在中心手动打印字符串的解决方案,您在使用自己的转换说明符时仍然需要它。

        【讨论】:

          【解决方案9】:

          您可以使用以下两个选项之一:

          char name[] = "Name1";
          
          //Option One
          printf("%*s", 40+strlen(name)/2, name, 40-strlen(name)/2, "");
          puts("");//skip one line
          
          //Option two
          printf("%*s", 40+strlen("Name2")/2, "Name2", 40-strlen("Name2")/2, "");
          

          输出是:

          姓名1(中间)
          名称2(中心)

          【讨论】:

          • 首先,这看起来像是对 Giuseppe 答案的抄袭,其次,您拥有的 printf() 参数多于容纳它们的格式说明符。您甚至对此进行了测试吗?
          猜你喜欢
          • 2011-02-12
          • 2011-12-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-09
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多