21

Re: Вивід рядків із масиву колонками

За основу я взяв завдання із книги Кернігана і Рітчі.

Exercise 5-17. Add a field-searching capability, so sorting may bee done on fields within lines, each field sorted according to an independent set of options. (The index for
this book was sorted with -df for the index category and -n for the page numbers.)

Там сказано про зміст книги, він виглядає так:

Назва1   \t   сторінка1      \t      Назва5   \t   сторінка5
Назва2   \t   сторінка2      \t      Назва6   \t   сторінка6
Назва3   \t   сторінка3      \t      Назва7   \t   сторінка7
Назва4   \t   сторінка4      \t      Назва8   \t   сторінка8

Тобто дві колонки, перша колонка складається із двох колонок - колонка назв і колонка сторінок, і друга так само. Колонки назв і сторінок мають різне форматування.

Я собі повністю спростив все це завдання до того щоб для початку вивести набір рядкових констант у вигляді колонок. Але користувач може вибрати кількість колонок і від того розмір самих колонок і вид виводу зміниться.

Приклад
Маємо файл і в ньому рядки:

Привіт, друже!
Це мій лист із Америки. Я тут уже влаштувався і ознайомився із околицями. Люди хоч і мають інший менталітет, але все ж привітні.
Думаю я не буду тут довго затримуватись. Скучив за домівкою.
Тож чекай! Іще не довго залишилось.
Віталік

Приклад для виводу в один і в два стовпчики:

-------------------------60--------------------------------|
Привіт, друже!---------------------------------------------
//
Це мій лист із Америки. Я тут уже влаштувався і ознайомився
із околицями. Люди хоч і мають інший менталітет, але все ж 
привітні.--------------------------------------------------
//
Думаю я не буду тут довго затримуватись. Скучив за домівкою
//
.----------------------------------------------------------
Тож чекай! Іще не довго залишилось.------------------------
//
Віталік----------------------------------------------------
//


--------------30---------------|--------------30---------------|
Привіт, друже!-----------------|Це мій лист із Америки. Я тут у|
-------------------------------|же влаштувався і ознайомився із|
-------------------------------| околицями. Люди хоч і мають ін|
-------------------------------|ший менталітет, але все ж приві|
-------------------------------|тні.---------------------------|
//
Думаю я не буду тут довго затри|Тож чекай! Іще не довго залишил|
муватись. Скучив за домівкою.--|ось.---------------------------|
//
Віталік.-----------------------|-------------------------------|

22 Востаннє редагувалося Ярослав (02.06.2014 15:50:53)

Re: Вивід рядків із масиву колонками

Тобто кожен наступний рядок виводиться в наступну колонку, але вивід цілого ряду не завершиться, доки не буде виведено повністю кожен із рядків групи.

23

Re: Вивід рядків із масиву колонками

Привіт!
Проблема полягає не в тому що незрозумілий принцип дії програми, а в тому, що щось неправильно записується в масиви або покажчики вказують не на те.
Ось іще одна версія програми, тільки тут я уже намагаюсь просто посимвольно вивести зчитані рядки:

У нас є певний буфер buffer[][], це по суті двовимірний масив типу char. Для того, щоб отримати доступ до нього із іншої функції необхідно встановити покажчик на першу комірку цього масиву - це буде масив покажчиків lineptr. Всі елементи buffer занулюються, на кожну першу комірку кожного рядка buffer встановлюється відповідний покажчик lineptr.
Потім ми виконуємо функцію input, думаю проблема десь тут. В змінну c записується кожен наступний символ, а потім записується в buffer через покажчик ptr, для ptr ми визначали тільки один вимір, інший же має не фіксований розмір, може проблема в цьому? Може взагалі жодного виміру не можна визначати, а треба писати **lineptr і все?
Якщо виконувати в Debug-ері програму, то видно що все одно записується в buffer всі рядки. Коли виконувати функцію output, то c приймає усі необхідні символи, я спостерігав в Debug-ері, як в с були символи всіх трьох рядків по порядку: 1, :,  , H, e, ... і нуль-символ також в кінці кожного рядка. Але функція putchar() працює так, що не одразу виводить те що ми їй передаємо, а передає це на потік виводу (скоріш за все, я не впевнений, я так собі це уявляю).
Я виконував зневадження покроково і в кінці функції почали виконуватись рядки із різних асемблер-файлів. Я затиснув кнопку покрокового виконання і на певному файлі write відбувся вивід:
: How are you?d!
Ніяк не збагну в чому помилка.
Може я неправильно передаю покажчик в функцію або неправильно його визначаю?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_STRING_NUMBER 100
#define MAX_STRING_LENGTH 128
#define MAX_COLUMNS 5
#define OUTPUT_AREA 60

/*
 * 
 */
int input(char **ptr);
void output(char **ptr, int total_lines, int columns);

int main(int argc, char** argv) {
    int i, j, cols, nlines;
    char *lineptr[MAX_STRING_NUMBER];
    char buffer[MAX_STRING_NUMBER+1][MAX_STRING_LENGTH+1];
    
    for(i = 0; i < MAX_STRING_NUMBER; i++){
        for(j = 0; j < MAX_STRING_LENGTH; j++){
            buffer[i][j] = 0;
        }
    }
    for(i = 0; i < MAX_STRING_NUMBER; i++){
        lineptr[i] = buffer[i];
    }
    
    /* визначення кількості колонок */
    /*do{
        printf("Input number of columns (0 - default): ");
        scanf("%d", &cols);
    } while (cols < 0 || cols > MAX_COLUMNS);
    if (cols == 0){
        cols = 1;
    }*/
    nlines = input(lineptr);
    output(lineptr, nlines, cols);
    return (EXIT_SUCCESS);
}

int input(char **ptr){
    char c; // Приймає символ
    int curr_ln = 0, // Поточний рядок
        curr_ch = 0; // Поточний символ
    FILE *fp; // Покажчик для лексем файлу

    fp = fopen("user_strings.txt", "r"); // Відкриваємо файл на читання
    if(!fp) // Файлу немає
        return (-1);

    while( ((c = fgetc(fp)) != EOF)             // Йдемо до кінця файлу
            && curr_ln < MAX_STRING_NUMBER-1    // Запобігання виходу за межі масиву
            && curr_ch < MAX_STRING_LENGTH-1 ){ 
        if(c != '\n' && c > 0){ // Рядок не закінчився
            ptr[ curr_ln ][ curr_ch ] = c; // Запис символу в масив
            curr_ch++; // Приготуємо лічильник для наступного символу
        } else { // Рядок закінчився
            ptr[ curr_ln ][ curr_ch ] = '\0'; // Додаємо нуль-символ в кінець радка
            curr_ln++; // Приготуємо лічильник для наступного рядка
            curr_ch = 0; // Обнулимо лічильник символів
        }
    }
    fclose(fp); // Закриваємо файл
    return curr_ln;
}

void output(char **ptr, int total_lines, int cols){
    char c;
    int  cur_line = 0,
         cur_char = 0;
    while(cur_line < total_lines){
        c = ptr[ cur_line ][ cur_char ];    
        if(c != '\0'){
            putchar(c);
        } else {
            cur_char = 0;
            cur_line++;
        }
        cur_char++;
    }
}

24 Востаннє редагувалося Ярослав (04.06.2014 12:44:48)

Re: Вивід рядків із масиву колонками

Мені уже здається, що було б простіше замість putchar(c) записувати ці символи в іще який небудь масив buffer2[] а потім уже в функції main просто цей масив вивести. Проте все іще є надія на те , що вдастся розібратись із цією програмою. Тому форумчани, прошу вашої допомоги!

25

Re: Вивід рядків із масиву колонками

Я поки що копирсаюся в вашому старому коді. Є принципова помилка в функції input, а саме тип змінної c. Вона має бути int. Чому? Подивіться визначення функції fgetc і константи EOF, якщо щось незрозумієте - перепитуйте.

Що ж стосується output, то:
- ви не виводите символ нового рядка наприкінці рядка, відповідно, все зсувається і збивається; додайте

putchar('\n');

наприкінці циклу по toprint.
- ви перебираєте рядки по одному, а треба пачками по cols:

for(curr_ln = 0; curr_ln < nlines; curr_ln += cols ){

- ви не враховуєте розмітку (символи '|') в таблиці, і колонки зсуваються.

26

Re: Вивід рядків із масиву колонками

А з масивами ви стерпно працюєте, як для початківця. В принципі, можна спростити:
typedef char buffer[MAX_STRING_NUMBER][MAX_STRING_LENGTH];
...
int input(buffer ptr);
void output(buffer ptr, int nlines, int columns);
і т.д.

27

Re: Вивід рядків із масиву колонками

Про масиви, звідки ця плутанина: в C ім'я масиву зазвичай означає його адресу (ar == &ar[ 0 ]), а номер елемента обчислюється як ar[ i ] == *( ar + i ). Але щоб правильно обчислити суму вказівника і числа, треба знати розмір елемента: *( ar + i ) == (type *)( int( ar ) + int( i )*sizeof( ar[ 0 ] ) ). Пояснювати цей жах треба? Якщо ні, то одразу стає ясно, чому не можна просто взяти і передати масив масивів у функцію: функція має знати розмір елемента, а елемент - це масив певного розміру. Тому і не виходить зробити параметр ar[][].

28 Востаннє редагувалося Ярослав (05.06.2014 09:53:50)

Re: Вивід рядків із масиву колонками

Зрозумів, що fgetc повертає int, а EOF має значення -1, тому треба використовувати int тип для c.
Виправив іще трохи код, ніби то по логіці все правильно уже має бути, але він все одно не працює. Мав би вивести всі рядки, може криво але всі мав би вивести. А виводить незрозуміло як.
Ось.
В функції output є змінна c, можна поствити точку розбиття на місці putchar( ptr[ curr_ln+curr_col ][ last_ch+curr_ch ] ); (109) і дивитись які значення приймає c, але чомусь не друкується.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_STRING_NUMBER 100
#define MAX_STRING_LENGTH 128
#define MAX_COLUMNS 5
#define OUTPUT_AREA 60

/*
*
*/
int input(char **ptr);
void output(char **ptr, int nlines, int columns);

int main(int argc, char** argv) {
    int i, j, cols, nlines;
    char str_buf[MAX_STRING_NUMBER][MAX_STRING_LENGTH];
    char *lineptr[MAX_STRING_NUMBER];
    
    for(i = 0; i < MAX_STRING_NUMBER; ++i){
        for(j = 0; j < MAX_STRING_LENGTH; ++j){
            str_buf[i][j] = 0;
        }
    }
    /* визначення кількості колонок */
    do{
        printf("Input number of columns (0 - default): ");
        scanf("%d", &cols);
    } while (cols < 0 || cols > MAX_COLUMNS);
    
    if (cols == 0){
        cols = 1;
    }
    
    for(i = 0; i < MAX_STRING_NUMBER; ++i){
        lineptr[i] = str_buf[i];
    }
    
    nlines = input(lineptr);
    output(lineptr, nlines, cols);
    
    return (EXIT_SUCCESS);
}

int input(char **lineptr){
    int c; // Приймає символ
    int curr_ln = 0, // Поточний рядок
        curr_ch = 0; // Поточний символ
    FILE *fp; // Покажчик для лексем файлу
    
    fp = fopen("user_strings.txt", "r"); // Відкриваємо файл на читання
    
    if(!fp) // Файлу немає
        return (-1);
    while( ((c = fgetc(fp)) != EOF)        // Йдемо до кінця файлу
            && curr_ln < MAX_STRING_NUMBER // Запобігання виходу за межі масиву
            && curr_ch < MAX_STRING_LENGTH ){
        if(c != '\n' && c > 0){            // Рядок не закінчився
            lineptr[ curr_ln ][ curr_ch ] = (char) c; // Запис символу в масив
            curr_ch++;                     // Приготуємо лічильник для наступного символу
        } else {                           // Рядок закінчився
            lineptr[ curr_ln ][ curr_ch ] = '\0';  // Додаємо нуль-символ в кінець радка
            curr_ln++;                             // Приготуємо лічильник для наступного рядка
            curr_ch = 0;                           // Обнулимо лічильник символів
        }
    }
    
    fclose(fp); // Закриваємо файл
    return curr_ln;
}

void output(char **ptr, int nlines, int cols){
    char c;
    int toprint, posv[MAX_COLUMNS];
    int curr_col = 0, // Поточна колонка
        curr_ch = 0,  // Поточний символ
        last_ch = 0,  // Останній символ, який виводився із поточного рядка
        curr_ln = 0,  // Поточний рядок
        col_width = ( OUTPUT_AREA - cols - 1 ) / cols; // визначення ширини колонок

    /* друк шапки */
    for(curr_col = 0; curr_col < cols; curr_col++){
        putchar('|');
        for(curr_ch = 0; curr_ch < col_width; curr_ch++){
            putchar('=');
        }
    }
    putchar('|');
    putchar('\n');
    
    /* друк колонок */
    for(curr_col = 0; curr_col < cols; curr_col++){
        // Сюди записуються позиції,
        // на яких вивід поточного рядка був призупинений
        posv[curr_col] = 0;
    }
    for(curr_ln = 0; curr_ln < nlines; curr_ln += cols){
        toprint = 1; // Обов’язково на друк є хоча б один рядок
        while(toprint > 0){
            toprint = 0; // Поки що невідомо скільки рядків треба друкувати
            for(curr_col = 0; curr_col < cols; curr_col++){
                curr_ch = 0; // Перший символ поточного рядка
                last_ch = posv[curr_col]; // Останній символ поточного рядка
                while( curr_ch < col_width // Іще є куди записувати
                                           // і поточний рядок іще не закінчився (є іще символи)
                        && ptr[ curr_ln+curr_col ][ last_ch+curr_ch ] != '\0' ){
                    // просто друкувати по порядку
                    c = ptr[ curr_ln+curr_col ][ last_ch+curr_ch ];
                    putchar( ptr[ curr_ln+curr_col ][ last_ch+curr_ch ] );
                    curr_ch++; // наступний символ
                }
                // Якщо цикл закінчився, але рядок іще не повністю виведений
                if(ptr[ curr_ln+curr_col ][ last_ch+curr_ch ] != '\0'){
                    toprint++; // Треба закінчити друк іще одного рядка
                    posv[curr_col] += curr_ch; // Запам’ятовуємо місце, де ми зупинились
                } else { // Інакше просто почнемо друк із початку
                    posv[curr_col] = 0;
                }
                // Якщо ми закінчили виводити рядок, а місце іще залишилось -
                // заповнюємо його пробілами
                while(curr_ch < col_width){
                    putchar(' ');
                    curr_ch++;
                }
            }
        }
    }
}

29

Re: Вивід рядків із масиву колонками

Ще раз: в рядок 126 додайте putchar('\n');

30

Re: Вивід рядків із масиву колонками

Ось код:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_STRING_NUMBER 100
#define MAX_STRING_LENGTH 128
#define MAX_COLUMNS 5
#define OUTPUT_AREA 60

/*
*
*/
int input(char **ptr);
void output(char **ptr, int nlines, int columns);

int main(int argc, char** argv) {
    int i, j, cols, nlines;
    char str_buf[MAX_STRING_NUMBER][MAX_STRING_LENGTH];
    char *lineptr[MAX_STRING_NUMBER];
    
    for(i = 0; i < MAX_STRING_NUMBER; ++i){
        for(j = 0; j < MAX_STRING_LENGTH; ++j){
            str_buf[i][j] = 0;
        }
    }
    /* визначення кількості колонок */
    do{
        printf("Input number of columns (0 - default): ");
        scanf("%d", &cols);
    } while (cols < 0 || cols > MAX_COLUMNS);
    
    if (cols == 0){
        cols = 1;
    }
    
    for(i = 0; i < MAX_STRING_NUMBER; ++i){
        lineptr[i] = str_buf[i];
    }
    
    nlines = input(lineptr);
    output(lineptr, nlines, cols);
    
    return (EXIT_SUCCESS);
}

int input(char **lineptr){
    int c; // Приймає символ
    int curr_ln = 0, // Поточний рядок
        curr_ch = 0; // Поточний символ
    FILE *fp; // Покажчик для лексем файлу
    
    fp = fopen("user_strings.txt", "r"); // Відкриваємо файл на читання
    
    if(!fp) // Файлу немає
        return (-1);
    while( ((c = fgetc(fp)) != EOF)        // Йдемо до кінця файлу
            && curr_ln < MAX_STRING_NUMBER // Запобігання виходу за межі масиву
            && curr_ch < MAX_STRING_LENGTH ){
        if(c != '\n' && c > 0){            // Рядок не закінчився
            lineptr[ curr_ln ][ curr_ch ] = (char) c; // Запис символу в масив
            curr_ch++;                     // Приготуємо лічильник для наступного символу
        } else {                           // Рядок закінчився
            lineptr[ curr_ln ][ curr_ch ] = '\0';  // Додаємо нуль-символ в кінець радка
            curr_ln++;                             // Приготуємо лічильник для наступного рядка
            curr_ch = 0;                           // Обнулимо лічильник символів
        }
    }
    
    fclose(fp); // Закриваємо файл
    return curr_ln;
}

void output(char **ptr, int nlines, int cols){
    char c;
    int toprint, posv[MAX_COLUMNS];
    int curr_col = 0, // Поточна колонка
        curr_ch = 0,  // Поточний символ
        last_ch = 0,  // Останній символ, який виводився із поточного рядка
        curr_ln = 0,  // Поточний рядок
        col_width = ( OUTPUT_AREA - cols - 1 ) / cols; // визначення ширини колонок

    /* друк шапки */
    for(curr_col = 0; curr_col < cols; curr_col++){
        putchar('|');
        for(curr_ch = 0; curr_ch < col_width; curr_ch++){
            putchar('=');
        }
    }
    putchar('|');
    putchar('\n');
    
    /* друк колонок */
    for(curr_col = 0; curr_col < cols; curr_col++){
        // Сюди записуються позиції,
        // на яких вивід поточного рядка був призупинений
        posv[curr_col] = 0;
    }
    for(curr_ln = 0; curr_ln < nlines; curr_ln += cols){
        toprint = 1; // Обов’язково на друк є хоча б один рядок
        while(toprint > 0){
            putchar('|');
            toprint = 0; // Поки що невідомо скільки рядків треба друкувати
            for(curr_col = 0; curr_col < cols; curr_col++){
                curr_ch = 0; // Перший символ поточного рядка
                last_ch = posv[curr_col]; // Останній символ поточного рядка
                while( curr_ch < col_width // Іще є куди записувати
                                           // і поточний рядок іще не закінчився (є іще символи)
                        && ptr[ curr_ln+curr_col ][ last_ch+curr_ch ] != '\0' ){
                    // просто друкувати по порядку
                    c = ptr[ curr_ln+curr_col ][ last_ch+curr_ch ];
                    putchar( ptr[ curr_ln+curr_col ][ last_ch+curr_ch ] );
                    curr_ch++; // наступний символ
                }
                // Якщо цикл закінчився, але рядок іще не повністю виведений
                if(ptr[ curr_ln+curr_col ][ last_ch+curr_ch ] != '\0'){
                    toprint++; // Треба закінчити друк іще одного рядка
                    posv[curr_col] += curr_ch; // Запам’ятовуємо місце, де ми зупинились
                } else { // Інакше просто почнемо друк із початку
                    posv[curr_col] = 0;
                }
                // Якщо ми закінчили виводити рядок, а місце іще залишилось -
                // заповнюємо його пробілами
                while(curr_ch < col_width){
                    putchar(' ');
                    curr_ch++;
                }
                putchar('|');
            }
            putchar('\n');
        }
    }
}

Вивід для 1 колонки:

Input number of columns (0 - default): 0
|==========================================================|
                                         |
                                          |

Вивід для 2 колонок:

|============================|============================|
            |: How are you?

Вивід для 3 колонок:

Input number of columns (0 - default): 3
|==================|==================|==================|
  |3: I'm fine, thx! |

Вивід для 4 колонок:

Input number of columns (0 - default): 4
|=============|=============|=============|=============|
|1: Hello, wor|2: How are yo|3: I'm fine, |             |
          |thx!         |             |

Вивід для 5 колонок:

|==========|==========|==========|==========|==========|
|1: Hello, |2: How are|3: I'm fin|          |          |
    |e, thx!   |          |          |

31 Востаннє редагувалося koala (05.06.2014 11:54:33)

Re: Вивід рядків із масиву колонками

Ваш код у мене працює так:

Input number of columns (0 - default): 5
|==========|==========|==========|==========|==========|
|1: Hello, |2: How are|3: I'm fin|          |          |
|world!    | you?     |e, thx!   |          |          |

Яким компілятором користуєтеся? Може, якісь особливі налаштування?

32

Re: Вивід рядків із масиву колонками

Система Debian Linux, 64bit, компілятор gcc, IDE Netbeans.
Запускав як в NB, так і в терміналі, результат той самий.

33

Re: Вивід рядків із масиву колонками

Спробуйте \r замість \n, Linux все ж таки.

34

Re: Вивід рядків із масиву колонками

Підкажу, бери у терміналу ширину через getenv("COLUMNS"), він передає кількість символів у строчці, розрахувати ширину в символах всіх колонок та відмовитися від \t . Скажу з досвіду, лише так зможете зробити колонки які будуть завжди колонками у терміналі якого розміру він не був би.

35

Re: Вивід рядків із масиву колонками

HetmanNet написав:

Підкажу, бери у терміналу ширину через getenv("COLUMNS"), він передає кількість символів у строчці, розрахувати ширину в символах всіх колонок та відмовитися від \t . Скажу з досвіду, лише так зможете зробити колонки які будуть завжди колонками у терміналі якого розміру він не був би.

В програмі немає жодних табуляцій.

36

Re: Вивід рядків із масиву колонками

Так!
Саме воно.
Дякую Вам, шановний пане Koala!
Ви абсолютно точно підмітили - "де собака зарита".

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_STRING_NUMBER 100
#define MAX_STRING_LENGTH 128
#define MAX_COLUMNS 5
#define OUTPUT_AREA 60

/*
*
*/
int input(char **ptr);
void output(char **ptr, int nlines, int columns);

int main(int argc, char** argv) {
    int i, j, cols, nlines;
    char str_buf[MAX_STRING_NUMBER][MAX_STRING_LENGTH];
    char *lineptr[MAX_STRING_NUMBER];
    
    for(i = 0; i < MAX_STRING_NUMBER; ++i){
        for(j = 0; j < MAX_STRING_LENGTH; ++j){
            str_buf[i][j] = 0;
        }
    }
    /* визначення кількості колонок */
    do{
        printf("Input number of columns (0 - default): ");
        scanf("%d", &cols);
    } while (cols < 0 || cols > MAX_COLUMNS);
    
    if (cols == 0){
        cols = 1;
    }
    
    for(i = 0; i < MAX_STRING_NUMBER; ++i){
        lineptr[i] = str_buf[i];
    }
    
    nlines = input(lineptr);
    output(lineptr, nlines, cols);
    
    return (EXIT_SUCCESS);
}

int input(char **lineptr){
    int c; // Приймає символ
    int curr_ln = 0, // Поточний рядок
        curr_ch = 0; // Поточний символ
    FILE *fp; // Покажчик для лексем файлу
    
    fp = fopen("user_strings.txt", "r"); // Відкриваємо файл на читання
    
    if(!fp) // Файлу немає
        return (-1);
    while( ((c = fgetc(fp)) != EOF)        // Йдемо до кінця файлу
            && curr_ln < MAX_STRING_NUMBER // Запобігання виходу за межі масиву
            && curr_ch < MAX_STRING_LENGTH ){
        if(c != '\r' && c != '\n' && c > 0){            // Рядок не закінчився
            lineptr[ curr_ln ][ curr_ch ] = (char) c; // Запис символу в масив
            curr_ch++;                     // Приготуємо лічильник для наступного символу
        } else if(c == '\n'){                           // Рядок закінчився
            lineptr[ curr_ln ][ curr_ch ] = '\0';  // Додаємо нуль-символ в кінець радка
            curr_ln++;                             // Приготуємо лічильник для наступного рядка
            curr_ch = 0;                           // Обнулимо лічильник символів
        }
    }
    
    fclose(fp); // Закриваємо файл
    return curr_ln;
}

void output(char **ptr, int nlines, int cols){
    char c;
    int toprint, posv[MAX_COLUMNS];
    int curr_col = 0, // Поточна колонка
        curr_ch = 0,  // Поточний символ
        last_ch = 0,  // Останній символ, який виводився із поточного рядка
        curr_ln = 0,  // Поточний рядок
        col_width = ( OUTPUT_AREA - cols - 1 ) / cols; // визначення ширини колонок

    /* друк шапки */
    for(curr_col = 0; curr_col < cols; curr_col++){
        putchar('|');
        for(curr_ch = 0; curr_ch < col_width; curr_ch++){
            putchar('=');
        }
    }
    putchar('|');
    putchar('\n');
    
    /* друк колонок */
    for(curr_col = 0; curr_col < cols; curr_col++){
        // Сюди записуються позиції,
        // на яких вивід поточного рядка був призупинений
        posv[curr_col] = 0;
    }
    for(curr_ln = 0; curr_ln < nlines; curr_ln += cols){
        toprint = 1; // Обов’язково на друк є хоча б один рядок
        while(toprint > 0){
            putchar('|');
            toprint = 0; // Поки що невідомо скільки рядків треба друкувати
            for(curr_col = 0; curr_col < cols; curr_col++){
                curr_ch = 0; // Перший символ поточного рядка
                last_ch = posv[curr_col]; // Останній символ поточного рядка
                while( curr_ch < col_width // Іще є куди записувати
                                           // і поточний рядок іще не закінчився (є іще символи)
                        && ptr[ curr_ln+curr_col ][ last_ch+curr_ch ] != '\0' ){
                    // просто друкувати по порядку
                    c = ptr[ curr_ln+curr_col ][ last_ch+curr_ch ];
                    putchar( ptr[ curr_ln+curr_col ][ last_ch+curr_ch ] );
                    curr_ch++; // наступний символ
                }
                // Якщо цикл закінчився, але рядок іще не повністю виведений
                if(ptr[ curr_ln+curr_col ][ last_ch+curr_ch ] != '\0'){
                    toprint++; // Треба закінчити друк іще одного рядка
                    posv[curr_col] += curr_ch; // Запам’ятовуємо місце, де ми зупинились
                } else { // Інакше просто почнемо друк із початку
                    posv[curr_col] = 0;
                }
                // Якщо ми закінчили виводити рядок, а місце іще залишилось -
                // заповнюємо його пробілами
                while(curr_ch < col_width){
                    putchar(' ');
                    curr_ch++;
                }
                putchar('|');
            }
            putchar('\n');
        }
    }
}

Підказка користувачам, хто стикається із подібними проблемами:
Дивіться у відладчику на покажчик fp, там можна побачити як закінчується кожен рядок із файлу, це виглядає десь так:

abcdefg\\r\\n
hijklmnop\\r\\n
авбгґдеє\\r\\n

Тому Koala вірно сказав. Я зчитував і записував в масив \r символ всюди, тому вивід і спотворювався. Також якщо в кінці файлу немає символу нового рядка вивід не буде таким як треба.

37 Востаннє редагувалося Ярослав (05.06.2014 13:20:46)

Re: Вивід рядків із масиву колонками

Також дякую і Вам, HetmanNet! Я якраз коли писав цей вивід думав про те як робити програму, щоб вона адаптувалась під розмір терміналу, ваша порада теж дуже слушна для мене!

38 Востаннє редагувалося Ярослав (06.06.2014 09:57:31)

Re: Вивід рядків із масиву колонками

Іще одна версія коду:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_STRING_NUMBER 100
#define MAX_STRING_LENGTH 128
#define MAX_COLUMNS 5
#define OUTPUT_AREA 60

/*
*
*/
int input(char **ptr);
void output(char **ptr, int nlines, int columns);

int main(int argc, char** argv) {
    int i, j, cols, nlines;
    char str_buf[MAX_STRING_NUMBER][MAX_STRING_LENGTH];
    char *lineptr[MAX_STRING_NUMBER];
    
    for(i = 0; i < MAX_STRING_NUMBER; ++i){
        for(j = 0; j < MAX_STRING_LENGTH; ++j){
            str_buf[i][j] = 0;
        }
    }
    /* визначення кількості колонок */
    do{
        printf("Input number of columns (0 - default): ");
        scanf("%d", &cols);
    } while (cols < 0 || cols > MAX_COLUMNS);
    
    if (cols == 0){
        cols = 1;
    }
    
    for(i = 0; i < MAX_STRING_NUMBER; ++i){
        lineptr[i] = str_buf[i];
    }
    
    nlines = input(lineptr);
    output(lineptr, nlines, cols);
    
    return (EXIT_SUCCESS);
}

int input(char **lineptr){
    int c; // Приймає символ
    int curr_ln = 0, // Поточний рядок
        curr_ch = 0; // Поточний символ
    FILE *fp; // Покажчик для лексем файлу
    
    fp = fopen("user_strings.txt", "r"); // Відкриваємо файл на читання
    
    if(!fp) // Файлу немає
        return (-1);
    while( ((c = fgetc(fp)) != EOF)        // Йдемо до кінця файлу
            && curr_ln < MAX_STRING_NUMBER // Запобігання виходу за межі масиву
            && curr_ch < MAX_STRING_LENGTH ){
        if(c != '\r' && c != '\n' && c > 0){            // Рядок не закінчився
            lineptr[ curr_ln ][ curr_ch ] = (char) c; // Запис символу в масив
            curr_ch++;                     // Приготуємо лічильник для наступного символу
        } else if(c == '\n'){          // Рядок закінчився
            lineptr[ curr_ln ][ curr_ch ] = '\0';  // Додаємо нуль-символ в кінець радка
            curr_ln++;                             // Приготуємо лічильник для наступного рядка
            curr_ch = 0;                           // Обнулимо лічильник символів
        }
    }
    if(c == EOF && curr_ch != 0){                  // Якщо після останнього рядку не був
                                                   // поставлений символ нового рядка
        lineptr[ curr_ln ][ ++curr_ch ] = '\0';    // Додаємо нуль-символ в кінець останнього рядка
    }
    
    fclose(fp); // Закриваємо файл
    return curr_ln;
}

void output(char **ptr, int nlines, int cols){
    char c;
    int toprint, posv[MAX_COLUMNS];
    int curr_col = 0, // Поточна колонка
        curr_ch = 0,  // Поточний символ
        last_ch = 0,  // Останній символ, який виводився із поточного рядка
        curr_gr = 0,  // Перший рядок поточної групи рядків
        col_width = ( OUTPUT_AREA - cols - 1 ) / cols; // визначення ширини колонок

    /* друк шапки */
    for(curr_col = 0; curr_col < cols; curr_col++){
        putchar('|');
        for(curr_ch = 0; curr_ch < col_width; curr_ch++){
            putchar('=');
        }
    }
    putchar('|');
    putchar('\n');
    
    /* друк колонок */
    for(curr_gr = 0; curr_gr < nlines; curr_gr += cols){
        for(curr_col = 0; curr_col < cols; curr_col++){
            // Сюди записуються позиції,
            // на яких вивід поточного рядка був призупинений
            posv[curr_col] = 0;
        }
        toprint = 1; // Обов’язково на друк є хоча б один рядок
        while(toprint > 0){
            putchar('|');
            toprint = 0; // Поки що невідомо скільки рядків треба друкувати
            for(curr_col = 0; curr_col < cols; curr_col++){
                curr_ch = 0; // Перший символ поточного рядка
                last_ch = posv[curr_col]; // Останній символ поточного рядка
                while( curr_ch < col_width // Іще є куди записувати
                                           // і поточний рядок іще не закінчився (є іще символи)
                        && ptr[ curr_gr+curr_col ][ last_ch+curr_ch ] != '\0' ){
                    // просто друкувати по порядку
                    c = ptr[ curr_gr+curr_col ][ last_ch+curr_ch ];
                    putchar( ptr[ curr_gr+curr_col ][ last_ch+curr_ch ] );
                    curr_ch++; // наступний символ
                }
                // Якщо цикл закінчився, але рядок іще не повністю виведений
                if(ptr[ curr_gr+curr_col ][ last_ch+curr_ch ] != '\0'){
                    toprint++; // Треба закінчити друк іще одного рядка
                    posv[curr_col] += curr_ch; // Запам’ятовуємо місце, де ми зупинились
                } else { // В іншому разі треба запам’ятати місце нуль символу
                         // виведеного рядка, якщо якийсь із рядків групи не 
                         // повністю виведений
                    posv[curr_col] = last_ch + curr_ch;
                }
                // Якщо ми закінчили виводити рядок, а місце іще залишилось -
                // заповнюємо його пробілами
                while(curr_ch < col_width){
                    putchar(' ');
                    curr_ch++;
                }
                putchar('|');
            }
            putchar('\n');
        }
    }
}

Впоралась із таким файлом:

1: Hello, world!
2: How are you?
3: I'm fine, thx!
4: What about special characters?
5: !@#$%^&*()<>
6: What about long-long lines?
7: looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong line
8: Nice one!

Без символу нового рядка в кінці файлу або з ним.

39 Востаннє редагувалося Ярослав (06.06.2014 12:22:09)

Re: Вивід рядків із масиву колонками

І щоб вже закрити цю тему, давно хотів спробувати попрацювати із malloc(), calloc(), free()

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_STRING_NUMBER 100
#define MAX_STRING_LENGTH 128
#define MAX_COLUMNS 5
#define OUTPUT_AREA 60

/*
*
*/
int input(char **ptr);
void output(char **ptr, int nlines, int columns);

int main(int argc, char** argv) {
    int i, j, cols, nlines;
    char *lineptr[MAX_STRING_NUMBER];
    
    /* визначення кількості колонок */
    do{
        printf("Input number of columns (0 - default): ");
        scanf("%d", &cols);
    } while (cols < 0 || cols > MAX_COLUMNS);
    
    if (cols == 0){
        cols = 1;
    }
    
    for(i = 0; i < MAX_STRING_NUMBER; ++i){
        lineptr[i] = calloc(MAX_STRING_NUMBER, sizeof(char));
    }
    
    nlines = input(lineptr);
    output(lineptr, nlines, cols);
    
    for(i = 0; i < MAX_STRING_NUMBER; ++i){
        free(lineptr[i]);
    }
    
    return (EXIT_SUCCESS);
}

int input(char **lineptr){
    int c; // Приймає символ
    int curr_ln = 0, // Поточний рядок
        curr_ch = 0; // Поточний символ
    FILE *fp; // Покажчик для лексем файлу
    
    fp = fopen("user_strings.txt", "r"); // Відкриваємо файл на читання
    
    if(!fp) // Файлу немає
        return (-1);
    while( ((c = fgetc(fp)) != EOF)        // Йдемо до кінця файлу
            && curr_ln < MAX_STRING_NUMBER // Запобігання виходу за межі масиву
            && curr_ch < MAX_STRING_LENGTH ){
        if(c != '\r' && c != '\n' && c > 0){            // Рядок не закінчився
            lineptr[ curr_ln ][ curr_ch ] = (char) c; // Запис символу в масив
            curr_ch++;                     // Приготуємо лічильник для наступного символу
        } else if(c == '\n'){          // Рядок закінчився
            lineptr[ curr_ln ][ curr_ch ] = '\0';  // Додаємо нуль-символ в кінець радка
            curr_ln++;                             // Приготуємо лічильник для наступного рядка
            curr_ch = 0;                           // Обнулимо лічильник символів
        }
    }
    if(c == EOF && curr_ch != 0){                  // Якщо після останнього рядку не був
                                                   // поставлений символ нового рядка
        lineptr[ curr_ln ][ ++curr_ch ] = '\0';    // Додаємо нуль-символ в кінець останнього рядка
    }
    
    fclose(fp); // Закриваємо файл
    return curr_ln;
}

void output(char **ptr, int nlines, int cols){
    char c;
    int toprint, posv[MAX_COLUMNS];
    int curr_col = 0, // Поточна колонка
        curr_ch = 0,  // Поточний символ
        last_ch = 0,  // Останній символ, який виводився із поточного рядка
        curr_gr = 0,  // Перший рядок поточної групи рядків
        col_width = ( OUTPUT_AREA - cols - 1 ) / cols; // визначення ширини колонок

    /* друк шапки */
    for(curr_col = 0; curr_col < cols; curr_col++){
        putchar('|');
        for(curr_ch = 0; curr_ch < col_width; curr_ch++){
            putchar('=');
        }
    }
    putchar('|');
    putchar('\n');
    
    /* друк колонок */
    for(curr_gr = 0; curr_gr < nlines; curr_gr += cols){
        for(curr_col = 0; curr_col < cols; curr_col++){
            // Сюди записуються позиції,
            // на яких вивід поточного рядка був призупинений
            posv[curr_col] = 0;
        }
        toprint = 1; // Обов’язково на друк є хоча б один рядок
        while(toprint > 0){
            putchar('|');
            toprint = 0; // Поки що невідомо скільки рядків треба друкувати
            for(curr_col = 0; curr_col < cols; curr_col++){
                curr_ch = 0; // Перший символ поточного рядка
                last_ch = posv[curr_col]; // Останній символ поточного рядка
                while( curr_ch < col_width // Іще є куди записувати
                                           // і поточний рядок іще не закінчився (є іще символи)
                        && ptr[ curr_gr+curr_col ][ last_ch+curr_ch ] != '\0' ){
                    // просто друкувати по порядку
                    c = ptr[ curr_gr+curr_col ][ last_ch+curr_ch ];
                    putchar( ptr[ curr_gr+curr_col ][ last_ch+curr_ch ] );
                    curr_ch++; // наступний символ
                }
                // Якщо цикл закінчився, але рядок іще не повністю виведений
                if(ptr[ curr_gr+curr_col ][ last_ch+curr_ch ] != '\0'){
                    toprint++; // Треба закінчити друк іще одного рядка
                    posv[curr_col] += curr_ch; // Запам’ятовуємо місце, де ми зупинились
                } else { // В іншому разі треба запам’ятати місце нуль символу
                         // виведеного рядка, якщо якийсь із рядків групи не 
                         // повністю виведений
                    posv[curr_col] = last_ch + curr_ch;
                }
                // Якщо ми закінчили виводити рядок, а місце іще залишилось -
                // заповнюємо його пробілами
                while(curr_ch < col_width){
                    putchar(' ');
                    curr_ch++;
                }
                putchar('|');
            }
            putchar('\n');
        }
    }
}

Файл user_strings.txt:

1: Hello, world!
2: How are you?
3: I'm fine, thx!
4: What about special characters?
5: !@#$%^&*()<>
6: What about long-long lines?
7: looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong line
8: Nice one!

40

Re: Вивід рядків із масиву колонками

*WALL*
Динамічне виділення пам'яті використовують для того, щоб не виділяти MAX_ЧОГОСЬ_ТАМ пам'яті, а строго за потребами.