Как правильно отформатировать строку чисел в c?

#c

Вопрос:

У меня есть структура, определенная как таковая

    typedef struct box
{
   float x;
   float y;
   float z;
   float volume;
   int order;
}box;
 

и файл, который мне дают, представляет данные таким образом:

 3   2.5 3
4.4 5   6
8.7 6.5 9.5
2.5 6.5 7.3
7.6 5.1 6.2
 

с каждым числом, представляющим размер параллелепипеда

 printf("%f  %f  %f      %fn", A[i]->x, A[i]->y, A[i]->z, A[i]->volume)
 

поскольку я являюсь циклом for и массив, в котором я храню структуры, дает мне этот вывод

 3  2.5  3      22.5
4.4  5  6      132
8.7  6.5  9.5      537.225
2.5  6.5  7.3      118.625
5.7  8.7  9.8      485.982
7.6  5.1  6.2      240.312
 

Как я могу распечатать его организованно, как входной файл?

одна из проблем заключается в том, что входные данные также могут выглядеть по-другому, например.

 5.244231 2.231432 7.232432
 

поэтому мне нужна моя распечатка для размещения чисел разной длины, хотя форматы не смешиваются, поэтому, например, это не может быть 5 4.52653 3.674

Комментарии:

1. Разделены ли числа во входном файле пробелами или символами табуляции?

2. Может быть, это может вам помочь: Спецификаторы формата в C

3. Ты в курсе, что можешь делать подобное printf("?" ?

4. Проще всего: используйте вкладки (или просто одиночные пробелы).

5. @AndreasWenzel они разделены вкладками

Ответ №1:

учитывайте различную длину чисел

float значения обычно варьируются от /-1,4…e-45F до /-3,4…e 38F, /-0, бесконечность и различные не-числа.

"%f" печатает с 6 цифрами после . as, поэтому печатает 0.000000 для многих маленьких float и со многими неинформативными цифрами, когда очень большие, как, таким образом, неудовлетворительно.

Для общей печати начните с использования "%g" , которое адаптирует свой вывод (используя экспоненциальную нотацию для больших и малых значений) и усекает конечные нули.

Используйте ширину, которая определяет минимальное количество символов для печати.

float обычно требуется до 9 значащих цифр, чтобы отличить их от других float . Рассмотрим точность.

Используйте объединение исходных текстов, чтобы разрешить только 1 определение желаемого формата.

 // printf("%f  %f  %f      %fn", A[i]->x, A[i]->y, A[i]->z, A[i]->volume)

#define FMT_F "%-15.9g" 
//                  ^ precision
//               ^^   width
//              ^     left justify   

printf(FMT_F " " FMT_F " " FMT_F " " FMT_F "n", 
    A[i]->x, A[i]->y, A[i]->z, A[i]->volume)

// Example output
4.4             5               6               132            
8.7             6.5             9.5             537.225        
1.40129846e-45  -3.40282347e 38 123.4           -5.88417018e-05
 

Более продвинутый код будет использовать ширину и точность, зависящие от компилятора.

Ответ №2:

Мне нужна распечатка для размещения чисел разной длины, хотя форматы не смешиваются, поэтому, например, она не может быть 5 4,52653 3,674

Это просто (хорошо, это не так просто)! Просто:

  • Выделите 2d массив выходных строк.
  • Выведите все элементы этого массива.
  • Для каждого столбца в выходном массиве:
    • получите самый длинный элемент этого столбца.
  • Выведите каждый столбец, заполненный пробелами, до самого длинного элемента.
  • Печатайте пробелы между столбцами и новые строки между строками.

Для вдохновения см. утилиту util-linux/column.c.

И, ну, пример кода: с массивом из двух int с разными значениями, давайте распечатаем их:

 #define _GNU_SOURCE 1 // for asprintf
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

typedef struct {
    int a;
    int b;
} data_t;
#define DATA_ELEMS  2  // the count of output columns
#define COUNT       3  // the count of output rows

int main() {
    // some data we are going to print
    const data_t data[COUNT] = {
        {rand(),rand()},
        {rand(),rand()},
        {1,2},
    };
    // Print all elements to this array.
    char *strs[COUNT][DATA_ELEMS] = {0};
    for (size_t i = 0; i < COUNT;   i) {
        const data_t *it = amp;data[i];
        asprintf(amp;strs[i][0], "%d", it->a);
        asprintf(amp;strs[i][1], "%d", it->b);
        // TODO: error checking
    }
    // For each column in the output array:
    // get the longest element of this column.
    size_t collens[DATA_ELEMS] = {0};
    for (size_t i = 0; i < COUNT;   i) {
        for (size_t j = 0; j < DATA_ELEMS;   j) {
            if (collens[j] < strlen(strs[i][j])) {
                collens[j] = strlen(strs[i][j]);
            }
        }
    }
    
    for (size_t i = 0; i < COUNT;   i) {
        // Print each column padded with spaces to the longest element.
        for (size_t j = 0; j < DATA_ELEMS;   j) {
            printf("%*s", (int)collens[j], strs[i][j]);
            // Print spaces between columns.
            putchar(j   1 == DATA_ELEMS ? 'n' : ' ');
        }
    }

    for (size_t i = 0; i < COUNT;   i) {
        for (size_t j = 0; j < DATA_ELEMS;   j) {
            free(strs[i][j]);
        }
    }
}
 

будет например выводить на godbolt:

 1804289383  846930886
1681692777 1714636915
         1          2
 

Ответ №3:

Вы можете указать ширину столбца, поместив число между % и f так, например:

 printf("||f|f|f|f||n", box.x, box.y, box.z, box.volume);
 

бы напечатать:

 ||  3.100000|  2.000000|  9.500000| 58.899998||
 

Обратите внимание, что они выровнены по правому краю. Если вы поставите знак минус перед шириной столбца, вы можете выровнять его по левому краю.

 printf("||f|f|f|%-10f||n", box.x, box.y, box.z, box.volume);
 

напечатает:

 ||  3.100000|  2.000000|  9.500000|58.899998 ||
 

Также полезно знать, что вы можете контролировать количество цифр, отображаемых после десятичной точки, указав .<count> ширину столбца после:

 printf("||.2f|.2f|.2f|.4f||n", box.x, box.y, box.z, box.volume);
 

напечатает

 ||      3.10|      2.00|      9.50|   58.9000||
 

Обновление:
Похоже, вы добавили больше деталей к своему вопросу. Вот дополнительная информация, которую вы могли бы использовать.

Числа ширины и точности не обязательно должны быть константами в строке формата. Вы можете заменить любой или все из них звездочкой » * » и указать переменную, подобную этой:

     for (int precision = 1; precision < 5;   precision) {
        int width = precision * 4;
        printf("||%*.*f|%*.*f|%*.*f|%*.*f||n",
               width, precision, box.x,
               width, precision, box.y,
               width, precision, box.z,
               width, precision, box.volume);
    }
 

Это приведет к:

 || 3.1| 2.0| 9.5|58.9||
||    3.10|    2.00|    9.50|   58.90||
||       3.100|       2.000|       9.500|      58.900||
||          3.1000|          2.0000|          9.5000|         58.9000||
 

Быстрый способ определения количества цифр перед десятичной запятой — это:

 int digits = (int)log(values[i])/log(10)   1;
 

поэтому, если вы предварительно просмотрите свои данные, вы сможете выбрать подходящую ширину столбца.

Что касается цифр после десятичной точки, то это решение зависит от вас; причуды представлений с плавающей запятой могут привести к тому, что конечный пользователь может счесть их запутанными. Например, в данных, которые я использовал выше 3.1 * 2.0 * 9.5 печатается как 58.899998, если разрешено печатать все цифры, тогда как 5.9-это, вероятно, то, что ожидали бы конечные пользователи.