mr.loop
mr.loop

Reputation: 1005

Output indentation in C

Basically I want to print something like this:

Process No.(Size)    Block No.(Size)
1(200)               3(300)
2(3)                 1(50)
3(1000)              4(1200)

Since the spacing is variable, how to do it by the width specifier in %d?

printf("%d(%d)%d(%d)",processNo,processSize,blockNo,blockSize)

Where to put the spacing value?

Upvotes: 3

Views: 2661

Answers (3)

See the 'Width field' subsection in printf format string for an example of how width is passed as an argument to printf.

I got the width algorithm from Finding the length of an integer in C.

I tested my code at Code Chef. If you intend to compile this code with gcc, you will need the -lm option to bring in the library for math.h. See How to compile a C program that uses math.h?.

If you need a different table header, you could adjust the constants 11 in printspaces(11-w) & 5 in printspaces(5-w) till data aligns with the header.

#include <stdio.h>
#include <math.h>
#include <stdlib.h>

int width(int v){
    return floor(log10(abs(v))) + 1;
}

int printspaces(int s){
    int x;
    for (x=0;x<s;x++)
        printf(" ");
}

int main(void){
    int i,j,w;
    int a[3][4] = {{1,200,3,300},{2,3,1,50},{3,1000,4,1200}};
    //int a[3][4] = {{10,200,37,3000},{278,3565,1131,50},{390,100,4567,1200}};
    char *separator[4]  = {"","","","\n"};
    printf("Process No.(Size)   Block No.(Size)\n");
    for(i=0;i<3;i++){
        for (j=0;j<4;j++){
            w = width(a[i][j]);
            if (j%2==0){
                printspaces(11-w);
                printf("%*d%s",w,a[i][j],separator[j]);
            }
            else{
                printf("(%*d)",w,a[i][j]);
                printspaces(5-w);
                printf("%s",separator[j]);
            }
        }
    }
    return 0;
}

Here are the results (for the two example arrays in the code):

int a[3][4] = {{1,200,3,300},{2,3,1,50},{3,1000,4,1200}};

Process No.(Size)   Block No.(Size)
          1(200)            3(300)  
          2(3)              1(50)   
          3(1000)           4(1200) 


int a[3][4] = {{10,200,37,3000},{278,3565,1131,50},{390,100,4567,1200}};

Process No.(Size)   Block No.(Size)
         10(200)           37(3000) 
        278(3565)        1131(50)   
        390(100)         4567(1200) 

Upvotes: 0

user3386109
user3386109

Reputation: 34829

printf has two seldom-used features that allow this to be done simply and directly.

The first feature is the return value from printf. Yes, printf actually returns something, and it's the number of characters that were printed. Consider the following two lines of code:

int width = printf("Process No.(Size)    ");
int used  = printf("%d(%d)", procNum[i], procSize[i]);

The first line prints the column header, and sets variable width to the number of characters in that header. The second line prints the process information, and sets variable used to the number of characters needed to print the two numbers, and the parentheses. So the number of space characters required between the end of the process information, and the beginning of the block information, is simply width - used.

The second feature is the variable field width specifier. From the man page: "A field width or precision, or both, may be indicated by an asterisk '*' [...] In this case, an int argument supplies the field width or precision." Consider the following line of code:

printf("%*s", width - used, "");

The code is using the "%s" conversion specifier to print a string, but the string "" is empty, so normally nothing would be printed. But we've used an asterisk as the field width. So the argument before the string (width - used) supplies the field width. The string will be padded with spaces to fill that width.

Note that if width - used is less than 1, the code should set the field width to 1, so that at least one space is inserted between the process information, and the block information.

Putting it all together, the code looks like this:

#include <stdio.h>
#define max(a,b) ((a) > (b) ? (a) : (b))

int main(void)
{
    int procNum[]   = { 1, 2, 3 };
    int procSize[]  = { 200, 3, 1000 };
    int blockNum[]  = { 3, 1, 4 };
    int blockSize[] = { 300, 50, 1200};

    int width = printf("Process No.(Size)    ");    // print first column header, and save the width
    printf("Block No.(Size)\n");

    for (int i = 0; i < 3; i++)
    {
        int used = printf("%d(%d)", procNum[i], procSize[i]);      // print process information, and save the number of characters used
        printf("%*s%d(%d)\n", max(width - used, 1), "", blockNum[i], blockSize[i]);   // print spaces followed by the block information
    }
}

Upvotes: 2

Adrian Mole
Adrian Mole

Reputation: 51835

You can first print each 'field' of your table into a character string, then print each such field string using a left-justified, fixed-width format specifier. The latter is accomplished using the - flag and a width value in the %s format specifier (i.e., -n%s).

The following code displays what you require (I have used the fields to hold both the column titles and each row of data, but this is an 'optional extra').

#include <stdio.h>

int main(void)
{
    // Test data ...
    int procNo[3] = { 1, 2, 3 };
    int procSize[3] = { 200, 3, 1000};
    int blockNo[3] = { 3, 1, 4 };
    int blockSize[3] = { 300, 50, 1200 };

    char procField[32] = "Process No.(Size)";
    char blockField[32] = "Block No.(Size)";

    // Print rubric ...
    printf("%-22s%-22s\n", procField, blockField);

    // Print data rows ...
    for (int i = 0; i < 3; ++i) {
        // Write each field to string ...
        snprintf(procField, sizeof(procField), "%d(%d)", procNo[i], procSize[i]);
        snprintf(blockField, sizeof(blockField), "%d(%d)", blockNo[i], blockSize[i]);
        // ... then print fields as left-justified, fixed-length fields
        printf("%-22s%-22s\n", procField, blockField);
    }
    return 0;
}

Upvotes: 3

Related Questions