4

I want to create output like this using printf:

Name          Available         Required
------------- ----------------- ---------------
Something     10.1 GiB          2.3 GiB

But using the built-in spacing mechanism doesn't allow to add a measurement text (ie Gb) after the variable, before the space. The issue is that this text needs to be included in the spacing. I have tried

"%-12.1f GiB"

Which puts "GiB" after the spacing.

Tobias S
  • 79
  • 7

4 Answers4

4

The printf function won’t do this in one shot. Use an intermediate string.

double gigabytes_avail;
char buf[64];
snprintf(buf, sizeof(buf), "%.1f GB", gigabytes_avail);

printf("%-12s", buf);

If you are using Visual Studio and its compiler you may consider using sprintf_s() instead of snprintf(). They are not the same function, but sprintf_s() is suitable here.

Alternatively, you can do the padding yourself, since printf returns the number of characters written…

int column_width = 15;
double gigabytes_avail;
// Does not handle errors, printf may return -1.
int r = printf("%.1f GB", gigabytes_avail);
for (; r < column_width; r++) {
    putchar(' ');
}

As a minor note, the symbol for gigabyte is GB, not Gb. Gigabit is Gbit.

Dietrich Epp
  • 205,541
  • 37
  • 345
  • 415
3

printf returns the number of characters printed.
Use that value from one printf to pad the subsequent printf.

#include <stdio.h>

int main ( void) {

    printf ( "Name          Available         Required\n");
    printf ( "------------- ----------------- ---------------\n");

    for ( int out = 0; out < 20; out += 3) {
        printf ( "%-15s", "Something");
        int span = printf ( "%5.1f Gb", out * 10.1);
        printf ( "%*.1f Gb\n", 22 - span, out * 2.3);
    }

    return 0;
}  

Output:

Name          Available         Required
------------- ----------------- ---------------
Something        0.0 Gb           0.0 Gb
Something       30.3 Gb           6.9 Gb
Something       60.6 Gb          13.8 Gb
Something       90.9 Gb          20.7 Gb
Something      121.2 Gb          27.6 Gb
Something      151.5 Gb          34.5 Gb
Something      181.8 Gb          41.4 Gb

A few edits will produce fully left justified results.

#include <stdio.h>

int main ( void) {

    printf ( "Name          Available         Required\n");
    printf ( "------------- ----------------- ---------------\n");

    for ( int out = 0; out < 20; out += 3) {
        printf ( "%-14s", "Something");
        int span = printf ( "%.1f Gb", out * 10.1);
        printf ( "%*c%.1f Gb\n", 18 - span, ' ', out * 2.3);
    }

    return 0;
}

Fully left justified results:

Name          Available         Required
------------- ----------------- ---------------
Something     0.0 Gb            0.0 Gb
Something     30.3 Gb           6.9 Gb
Something     60.6 Gb           13.8 Gb
Something     90.9 Gb           20.7 Gb
Something     121.2 Gb          27.6 Gb
Something     151.5 Gb          34.5 Gb
Something     181.8 Gb          41.4 Gb
user3121023
  • 8,181
  • 5
  • 18
  • 16
1

Sometimes it is fun to create a reusable, even in a printf() line, as macro/function to handle a wide range of values.

#include <assert.h>
#include <limits.h>
#include <stdio.h>
#include <math.h>

// https://stackoverflow.com/a/4589384/2410359
#define IMAX_BITS(m) ((m)/((m)%255+1) / 255%255*8 + 7-86/((m)%255+12))

// Decimal digits possible in an integer `m`    
#define IMAX_DIGITS(m) (IMAX_BITS(m)*28/93 + 1) //ceiling( bits * log10(2))

#define ULLONG_STRING_SIZE (IMAX_DIGITS(ULLONG_MAX) + 1)
#define KSCALE 1000u /* or 1024u */

char* PrintMetric(char *dest, size_t n, unsigned long long b, char *suffix) {
  char prefix[] = "\0kMGTPEZY";
  int i = 0;
  while (i < sizeof prefix - 1 && b / pow(KSCALE, i) >= 99.95) {
    i++;
  }
  assert(i < sizeof prefix - 1);
  snprintf(dest, n, "%-.1f %.1s%s", b / pow(KSCALE, i), prefix + i, suffix);
  return dest;
}

#define PRT_BYTES(ull) \
    (PrintMetric((char [ULLONG_STRING_SIZE]){0}, ULLONG_STRING_SIZE, (ull), "b"))
//               ^----------------------------^ Compound literal

Usage
Notice multiple usages in a printf().

#define GSCALE (1ull * KSCALE * KSCALE * KSCALE)
void fooo() {
  puts("Name          Available         Required");
  puts("------------- ----------------- ---------------");
  // Notice 2 calls to PRT_BYTES()
  printf("%-*s %-*s %-s\n", 13, "Something", 17, PRT_BYTES(10.1*GSCALE), PRT_BYTES(2.3*GSCALE));
  printf("%-*s %-*s %-s\n", 13, "Something", 17, PRT_BYTES(99949), PRT_BYTES(99950));
  printf("%-*s %-*s %-s\n", 13, "Something", 17, PRT_BYTES(0), PRT_BYTES(ULLONG_MAX));
}

int main() {
  fooo();
  return 0;
}

Output

Name          Available         Required
------------- ----------------- ---------------
Something     10.1 Gb           2.3 Gb
Something     99.9 kb           0.1 Mb
Something     0.0 b             18.4 Eb
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
0

An alternative way would be to use the "Gb(yte)" indicators not within the same format string and implement them in the headings:

#include <stdio.h>

int main (void)
{
    printf("Name            Available (Gbyte)   Required (Gbyte)\n");
    printf("---------       ----------          ---------\n");
    printf("Something       %-12.1f        %.1f ", 125.0, 1444.6);
}

Output:

Name            Available (Gbyte)   Required (Gbyte)
---------       ----------          ---------
Something       125.0               1444.6