C printf: Есть ли способ добавить текст после переменной и перед интервалом? - PullRequest
4 голосов
/ 19 июня 2020

Я хочу создать такой вывод с помощью printf:

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

Но использование встроенного механизма интервалов не позволяет добавлять текст измерения (ie Gb) после переменной, перед пробела. Проблема в том, что этот текст нужно включить в интервал. Я пробовал

"%-12.1f GiB"

Что ставит "ГиБ" после пробела.

Ответы [ 4 ]

4 голосов
/ 19 июня 2020

Функция printf не сделает этого за один раз. Используйте промежуточную строку.

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

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

Если вы используете Visual Studio и ее компилятор, вы можете использовать sprintf_s() вместо snprintf(). Это не одна и та же функция, но здесь подходит sprintf_s().

В качестве альтернативы вы можете выполнить заполнение самостоятельно, поскольку printf возвращает количество записанных символов ...

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

В качестве незначительного примечания, символ гигабайта - это ГБ, а не ГБ. Гигабит - это Гбит.

3 голосов
/ 19 июня 2020

printf возвращает количество напечатанных символов. Используйте это значение из одного printf, чтобы дополнить последующие 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;
}  

Результат:

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

Несколько правок приведут к результатам, полностью выровненным по левому краю.

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

Результаты полностью выровнены по левому краю:

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
1 голос
/ 19 июня 2020

Иногда забавно создать повторно используемый, даже в строке printf(), макрос / функцию для обработки широкого диапазона значений.

#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

Использование
Обратите внимание на многократное использование в 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;
}

Вывод

Name          Available         Required
------------- ----------------- ---------------
Something     10.1 Gb           2.3 Gb
Something     99.9 kb           0.1 Mb
Something     0.0 b             18.4 Eb
0 голосов
/ 19 июня 2020

Альтернативным способом было бы использовать индикаторы "Gb(yte)" не в одной строке формата и реализовать их в заголовках:

#include <stdio.h>

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

Вывод:

Name            Available (Gbyte)   Required (Gbyte)
---------       ----------          ---------
Something       125.0               1444.6 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...