Centrar cadenas con printf()

6 minutos de lectura

Por defecto, printf() parece alinear las cadenas a la derecha.

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

También puedo alinear el texto a la izquierda de esta manera:

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

¿Hay una forma rápida de centrar el texto? ¿O tengo que escribir una función que convierta una cadena como test dentro (space)(space)test(space)(space) si el ancho del texto para esa columna es 8?

avatar de usuario
Giuseppe Guerrini

printf por sí solo no puede hacer el truco, pero podría jugar con el ancho “indirecto”, que especifica el ancho leyéndolo desde un argumento. Probemos esto (bueno, no perfecto)

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;
}

  • Probé esto en un código C++ que escribí para crear una tabla, y estaba truncando texto válido, y tenía que averiguar por qué. He proporcionado una respuesta alternativa basada en cómo la resolví.

    – luz clara

    25 de noviembre de 2018 a las 15:57

  • Correcto, de hecho, advertí que la solución “no es perfecta”, debido a las constantes codificadas que contiene. Mi objetivo era solo dar una pista, su solución completa la idea.

    – Giuseppe Guerrini

    26 de noviembre de 2018 a las 18:21

  • los * espera un int, strlen() regresará size_t. Esta función provoca UB cuando SIZE_MAX>INT_MAX. Posible solución: Cambiarlo a (int) ((10+strlen(s)/2)&INT_MAX)

    – 12431234123412341234123

    13 de agosto de 2021 a las 20:36


  • Correcto. En la mayoría de los casos, basta con lanzar strlen directamente a int. Es poco probable que una cadena que estaba destinada a estar centrada exceda INT_MAX en longitud.

    – Giuseppe Guerrini

    23 de agosto de 2021 a las 7:24

avatar de usuario
luz clara

@ GiuseppeGuerrini fue útil al sugerir cómo usar los especificadores de formato de impresión y dividir el espacio en blanco. Desafortunadamente, puede truncar el texto.

Lo siguiente resuelve el problema del truncamiento (asumiendo que el campo especificado es lo suficientemente grande para contener el texto).

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

  • tenga en cuenta que el ancho total resultante será diferente dependiendo de si fieldWidth - strlen(text) es par o impar

    – rtpax

    21 de febrero de 2019 a las 16:38

  • agregó una versión modificada que siempre imprime fieldWidth caracteres (por ejemplo, para uso en tablas, etc.)

    – zeawoas

    4 de diciembre de 2019 a las 14:29

  • El comportamiento es implementaciones definidas cuando el resultado de (fieldWidth - strlen(text)) / 2; está fuera del alcance de un int. ¿Cuál puede ser el caso cuando SIZE_MAX>INT_MAX y strlen(text)>fieldWidthcausando (fieldWidth - strlen(text)) == SIZE_MAX-(strlen(text)-fieldWidth-1).

    – 12431234123412341234123

    13 de agosto de 2021 a las 20:43


No hay printf() especificador de formato para centrar el texto.

Deberá escribir su propia función o ubicar una biblioteca que proporcione la funcionalidad que está buscando.

  • Su oportunidad de recorrer las 9 yardas adicionales para proporcionar una solución, ¿eh?

    – luz clara

    25 de noviembre de 2018 a las 15:14

Puede intentar escribir su propia función para este problema.

/**
 * 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;
}

Resultados:

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!!!

Dejaré caer mis 2 centavos después de lidiar con un problema similar de tratar de centrar los encabezados de una tabla en una fila con printf.

Las siguientes macros deberán imprimirse antes o después del texto y se alinearán independientemente de la longitud del texto en sí. Tenga en cuenta que si tenemos cadenas de longitudes impares, no las alinearemos como deberíamos (porque la división normal dará como resultado que falte espacio). Por lo tanto, se necesita un redondeo, y creo que esta es la forma elegante de resolver ese problema:

#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))

Ejemplo de uso:

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

avatar de usuario
hlovdal

Sí, tendrá que escribir su propia función que devuelva “prueba”, etc., por ejemplo

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

O tiene una función center_print, algo como lo siguiente:

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);
        }
}

avatar de usuario
colinb

Una versión más compacta de la función de PADYMKO anterior (que aún pierde memoria):

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);
}

Esto establece todo el búfer recién asignado al carácter de relleno, nulo lo termina y luego suelta la cadena para que se centre en el medio del búfer, sin bucles ni seguimiento de dónde copiar.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad