1 Востаннє редагувалося Ярослав (30.01.2014 18:03:18)

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

Привіт Вам, форумчани!
Допоможіть, будь ласка, розібратись!

Я вирішив частково виконати завдання 5.17 із книги Кернігана і Рітчі. Воно полягає в тому, щоб реалізувати вивід рядків в колонки, і кожна колонка була відсортована певним способом (в алфавітному порядку, з урахуванням верхнього або нижнього регістрів і т. д.), ці способи задаються аргументами командного рядку.
Я хочу тільки вивести рядки в колонки. Ось як я намагаюсь це зробити:

#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
/*
 * 
 */
// string_buffer: місце для зберігання рядків
char string_buffer[MAX_STRING_NUMBER][MAX_STRING_LENGTH];
// *string_ptr: масив покажчиків на рядкові константи
char *string_ptr[MAX_STRING_NUMBER];
// input: зчитує рядки із файлу і зберігає їх в місці, на яке вказує ptr_to_string
//        повертає кількість колонок або -1 в разі невдачі
int input(char **ptr_to_string, int size_of_string);
// output: виводить рядки із місця, куди вказує ptr_to_string у вигляді колонок
//         повертає -1 в разі невдачі
int output(char **ptr_to_string, int lines_number, int size_of_string);

int main(int argc, char** argv) {
    int nlines = 0; // кількість рядків
    
    if((nlines = input(string_ptr, MAX_STRING_LENGTH)) != -1){
        printf("There was %d string(s) read successfully\n", nlines);
    } else {
        return (EXIT_FAILURE);
    }
    if(output(string_ptr, nlines, MAX_STRING_LENGTH) != -1){
        printf("End of output\n");
    } else {
        return (EXIT_FAILURE);
    }
    
    return (EXIT_SUCCESS);
}

int input(char **pointer_to_string, int size_of_string){
    FILE *fp; // файловий покажчик
    int nlines; // кількість зчитаних рядків

    /* відкривання файлу для прочитування */
    fp = fopen("user_strings.txt", "r");
    if(fp == NULL) {
        perror("Error opening file");
        return (-1);
    }
    
    /* запис рядків в буфер і встановлення покажчиків на кожен із них */
    for( nlines = 0;
         fgets(string_buffer[nlines], size_of_string, fp) != NULL;
         nlines++ ){
        string_ptr[nlines] = string_buffer[nlines];
        printf("Operating line: %s", string_buffer[nlines]);
    }
    
    /* закривання файлу */
    fclose(fp);
    
    return nlines;
}

int output(char **ptr_to_string, int nlines, int str_length){
    char c;
    int i, j;
    int cols = 0; // кількість колонок для виводу
    int col_width = 0; // ширина колонки
    
    /* визначення кількості колонок */
    do{
        printf("Input number of columns (0 - default): ");
        scanf("%d", &cols);
    } while (cols < 0 || cols > MAX_COLUMNS);
    if (cols == 0){
        cols = 1;
    }
    
    /* визначення ширини колонок */
    /* кількість роздільників завжди на 1 більша ніж колонок */
    /* від загальної площі віднімаємо кількість колонок і 1 */
    /* результат ділимо на кількість колонок */
    col_width = ( OUTPUT_AREA - cols - 1 ) / cols;
    for(i = 0; i < cols; i++){
        putchar('|');
        for(j = 0; j < col_width; j++){
                putchar('=');
            }
    }
    putchar('|');
    putchar('\n');
    
    /* вивід тексту */
    //while(nlines-- > 0){
        for(i = 0; i < cols; i++){
                putchar('|');
                for(j = 0; j < col_width; j++){
                    c = string_buffer[i][j];
                    c != '\n' ? putchar(c) : putchar('n');
                }
        }
        putchar('|');
        putchar('\n');
    //}


    return 0;
}

Вміст файлу user_strings.txt

1: Hello there
2: How are you doing
3: $0me special characters !@#$%^&*()-=_+їі
4: White spaces in next string
5: \t       \t\t        ;
6: Some new lines


9: Empty next string

10: LongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLong String
11: End :)

Проблема виникає із цим шматком коду:

        for(i = 0; i < cols; i++){
                putchar('|');
                for(j = 0; j < col_width; j++){
                    c = string_buffer[i][j];
                    c != '\n' ? putchar(c) : putchar('n');
                }
        }
        putchar('|');
        putchar('\n');

Я працюю в середовищі NetBeans і в режимі зневадження відслідковував які значення приймає c. Так от, c коректно приймає всі значення, кожен із символів із масиву, але, якшо обрати вивід в 3 колонки, наприклад, то він буде таким:

Input number of columns (0 - default): 3
|==================|==================|==================|
n|2: How are you doi|3: $0me special ch|

Мав би починатись так:

Input number of columns (0 - default): 3
|==================|==================|==================|
|1: Hello there...

Головне, що я заміняв оператор ? : на if, і його тіло виконувалось (я поставив точки розбиття там), але після виконання функцій у вікно виводу нічого не виводилось, не можу зрозуміти в чому проблема, допоможіть, будь ласка.

Білий Лунь

2 Востаннє редагувалося koala (30.01.2014 22:49:25)

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

putchar('n');

Ви певні, що хочете саме літеру 'n' вивести?
Тернарний оператор треба використовувати, коли ви користуєтеся значенням, наприклад, так:

 putchar( c != '\n' ? c : 'n');

Інакше сенсу нема, if буде зрозумілішим.

Подякували: Ярослав1

3 Востаннє редагувалося Ярослав (30.01.2014 18:59:18)

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

Я поставив там літеру n, щоб побачити, де саме програма бачить символ нового рядка.

        for(i = 0; i < cols; i++){
                putchar('|');
                for(j = 0; j < col_width; j++){
                    c = string_buffer[i][j];
                    if(c != '\n' && c != '\0')
                        putchar(c);
                    else
                        putchar(' ');
                }
        }
        putchar('|');
        putchar('\n');

Із таким шматком коректно виводить для 4 і 5 колонок, а для 1, 2, 3 - некоректно. Дякую за зауваження.

Я помітив із-за чого це виходить, коли в рядку менше символів, ніж в колонці, то воно спотворює вивід. Якщо для перших двох рядків залишити по 3-4 символи, то і для 4 та 5 колонок вивід буде неправильний.

Той самий результат:

        for(i = 0; i < cols; i++){
                putchar('|');
                for(j = 0; j < col_width; j++){
                    if( *(*(ptr_to_string+i)+j) != '\n' && 
                        *(*(ptr_to_string+i)+j) != '\0')
                        printf("%c", *(*(ptr_to_string+i)+j));
                    else
                        putchar(' ');
                }
        }
        putchar('|');
        putchar('\n');
Білий Лунь

4 Востаннє редагувалося koala (30.01.2014 18:54:07)

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

О, тепер зрозумів. Дивіться, у нас є рядок string_buffer[0]: "ABC". Це означає, що реально в string_buffer[0] (а це, нагадаю, MAX_STRING_LENGTH char-ів) знаходиться щось на кшталт "ABC\0#@$%^&*(", довжиною в MAX_STRING_LENGTH: наш рядок, символ \0 і якесь сміття - просто неініціалізована пам'ять. А тепер що ви робите:

for(j = 0; j < col_width; j++){
c = string_buffer[i][j];//вибираємо ВСІ символи - не до \0, а абсолютно всі

Тепер ясно, в чому проблема?

Подякували: Ярослав1

5

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

Так, ясно.

Білий Лунь

6

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
/*
 * 
 */
// string_buffer: місце для зберігання рядків
char string_buffer[MAX_STRING_NUMBER][MAX_STRING_LENGTH];
// *string_ptr: масив покажчиків на рядкові константи
char *string_ptr[MAX_STRING_NUMBER];
// input: зчитує рядки із файлу і зберігає їх в місці, на яке вказує ptr_to_string
//        повертає кількість колонок або -1 в разі невдачі
int input(char **ptr_to_string, int size_of_string);
// output: виводить рядки із місця, куди вказує ptr_to_string у вигляді колонок
//         повертає -1 в разі невдачі
int output(char **ptr_to_string, int lines_number, int size_of_string);

int main(int argc, char** argv) {
    int nlines = 0; // кількість рядків
    
    if((nlines = input(string_ptr, MAX_STRING_LENGTH)) != -1){
        printf("There was %d string(s) read successfully\n", nlines);
    } else {
        return (EXIT_FAILURE);
    }
    if(output(string_ptr, nlines, MAX_STRING_LENGTH) != -1){
        printf("End of output\n");
    } else {
        return (EXIT_FAILURE);
    }
    
    return (EXIT_SUCCESS);
}

int input(char **pointer_to_string, int size_of_string){
    FILE *fp; // файловий покажчик
    int i, j, nlines; // кількість зчитаних рядків

    /* відкривання файлу для прочитування */
    fp = fopen("user_strings.txt", "r");
    if(fp == NULL) {
        perror("Error opening file");
        return (-1);
    }
    
    /* запис рядків в буфер і встановлення покажчиків на кожен із них */
    for( nlines = 0;
         fgets(string_buffer[nlines], size_of_string, fp) != NULL;
         nlines++ ){
        printf("Operating line: %s", string_buffer[nlines]);
    }
    
    /* закривання файлу */
    fclose(fp);
    
    /* Додавання символу \0 в кінець рядків */
    for(i = 0, j = 0; i < nlines; i++){
        while(string_buffer[i][j] != '\n' && j < MAX_STRING_LENGTH){
            j++;
        }
        if(j != MAX_STRING_LENGTH){
            string_buffer[i][j+1] = '\0';
        } else {
            string_buffer[i][j] = '\0';
        }
        pointer_to_string[i] = string_buffer[i];
        j = 0;
    }
    return nlines;
}

int output(char **ptr_to_string, int nlines, int str_length){
    char c;
    int i, j, k;
    int cols = 0; // кількість колонок для виводу
    int col_width = 0; // ширина колонки
    /* прапори */
    int nl_sign = 0; // символ нового рядка
    int no_more_char = 0; // всі си
    
    /* визначення кількості колонок */
    do{
        printf("Input number of columns (0 - default): ");
        scanf("%d", &cols);
    } while (cols < 0 || cols > MAX_COLUMNS);
    if (cols == 0){
        cols = 1;
    }
    
    /* визначення ширини колонок */
    /* кількість роздільників завжди на 1 більша ніж колонок */
    /* від загальної площі віднімаємо кількість колонок і 1 */
    /* результат ділимо на кількість колонок */
    col_width = ( OUTPUT_AREA - cols - 1 ) / cols;
    for(i = 0; i < cols; i++){
        putchar('|');
        for(j = 0; j < col_width; j++){
                putchar('=');
        }
    }
    putchar('|');
    putchar('\n');
    
    /* вивід тексту 
     * Текст виводиться доки іще рядки на вивід.
     * Рядок вважається повністю виведеним, коли його частина 
     * один раз виведена в колонку.
     * Стрічки виводиться посимвольно.
     */
    i = 0;
    while(i < nlines){ // є іще рядки
        for(j = 0; j < cols; j++){ // формуємо колонки
            putchar('|'); // ставимо перший розділювач в рядку
            for(k = 0; k < col_width; k++){ // виводимо символи
                if(nl_sign == 0){
                    if((c = *(*(ptr_to_string+i)+k)) != '\n' && c != '\0'){
                        putchar(c); // символ із рядка
                    } else if(c == '\n' || c == '\0'){
                        nl_sign = 1; // рядок закінчився
                        putchar(' ');
                    }
                } else {
                    putchar(' '); // рядок закінчився
                }
            }
            nl_sign = 0;
            i++;
        }
        putchar('|');
        putchar('\n');
    }


    return 0;
}

Але програма все одно падає на цьому рядку

if((c = *(*(ptr_to_string+i)+k)) != '\n' && c != '\0')

Текст user_strings

1: Hello, world!
2: How you are doing?
3: Numbers:1234567890
4: White spaces:1 2  3   4    5     end
5: Tabs:123
6: New lines 6 - 9



10: Characters:!@#$%^&*()_+/*-+.[];'./,
11: Ukrainian characters:абвгґдеєжзиіїйклмнопрстуфхцчшщьюя
12: Next line is empty

14: Next line is long and chars are numerated
0123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_
16: End :)

Не знаю в чому проблема. У відладчику c приймає всі символи по черзі, а коли справа доходить до putchar - то виводу просто нема. Не знаю в чому проблема.

Білий Лунь

7

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

Може хтось щось порадити або підказати куди копати?

Білий Лунь

8

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

Привіт. Я спробував переписати цю програмку з нуля і ось що маю на разі:
В принципі все залишилось як є окрім частини виводу.
Вивід працює наступним чином:
1. Виводимо рядки, доки всі рядки іще не повністю виведені (i < nlines)
2.

#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 main(int argc, char** argv) {
    int nlines;
    char str_buf[MAX_STRING_NUMBER][MAX_STRING_LENGTH];
    char *lineptr[MAX_STRING_NUMBER];
    lineptr[0] = str_buf[0];
    nlines = input(lineptr);
    output(lineptr, nlines);
    return (EXIT_SUCCESS);
}

int input(char **lineptr){
    char c;
    int i, j;
    FILE *fp;
    fp = fopen("user_strings.txt", "r");
    i = j = 0;
    while((c = fgetc(fp)) != EOF){
        if(c != '\n'){
            *(*(lineptr+i)+j) = c;
            j++;
        } else {
            *(*(lineptr+i)+j) = '\0';
            i++;
            j = 0;
        }
    }
    fclose(fp);
    return i;
}

void output(char **ptr, int nlines){
    char c;
    int toprint, posv[MAX_COLUMNS];
    int i, j, k, l, nfirst, cols, col_width;
    i = j = k = l = nfirst = cols = col_width = toprint = 0;
    /* визначення кількості колонок */
    do{
        printf("Input number of columns (0 - default): ");
        scanf("%d", &cols);
    } while (cols < 0 || cols > MAX_COLUMNS);
    if (cols == 0){
        cols = 1;
    }
    /* визначення ширини колонок */
    col_width = ( OUTPUT_AREA - cols - 1 ) / cols;
    for(i = 0; i < cols; i++){
        putchar('|');
        for(j = 0; j < col_width; j++){
                putchar('=');
        }
    }
    putchar('|');
    putchar('\n');
    /* друк колонок */
    for(i = 0; i < cols; i++){
        posv[i] = 0;
    }
    for(i = 0; i < nlines; i++){
        toprint = 1;
        while(toprint > 0){
            toprint = 0;
            for(j = 0; j < cols; j++){
                k = 0;
                l = posv[j];
                while(l < col_width && *(*(ptr+nfirst+j)+l+k) != '\0'){
                    printf("%c", *(*(ptr+nfirst+j)+l+k));
                    l++;
                }
                if((*(*(ptr+nfirst+j)+l+k) != '\0')){
                    toprint++;
                    posv[j] = l+k;
                } else {
                    i++;
                }
                while(k < col_width){
                    putchar(' ');
                    k++;
                }
            }
        }           
        
    }
    

}

Поясню останню частину із виводом рядків:
1. Виводимо рядки, доки всі рядки іще не виведені (i буде інкрементуватись тільки після повного виводу рядка) ідея полягає в цьому, реалізації поки нема, просто скинув сюди той етап над яким я зараз думаю)
2. toprint сигналізує що якийсь із групи рядків іще не повністю виведений значенням більше 0, якщо вся група виведена повністю (наприклад, для 3 колонок група буде складати три рядки), то значення toprint залишиться 0
3.

for(j = 0; j < cols; j++){
                k = 0;
                l = posv[j];

Цикл for відповідає за виведення одного рядку, який буде складатись із необхідної кількості колонок, k - це лічильник символів, завдяки ньому ми контролюємо, щоб не вийти за межі однієї колонки.
Якщо рядок не був повністю виведений - то значення k для нього додалось до відповідної комірки posv. Тому, щоб продовжити вивід із місця, де ми закінчили - то ми будемо використовувати суму k і l.
4.

                while(l < col_width && *(*(ptr+nfirst+j)+l+k) != '\0'){
                    printf("%c", *(*(ptr+nfirst+j)+l+k));
                    l++;
                }

Пробуємо дійти до знаку кінця рядка і паралельно друкуємо символи. Якщо ширина колонки закінчилась раніше за цей момент:
5.

                if((*(*(ptr+nfirst+j)+l+k) != '\0')){
                    toprint++;
                    posv[j] = l+k;
                } else {
                    i++;
                }

збільшуємо значення toprint, і зберігаємо номер символу із яким працюємо в цьому рядку, а інакше вивід рядку повністю закінчено і можна збільшити i.
6.

                while(k < col_width){
                    putchar(' ');
                    k++;
                }

Якщо рядок закінчився, то решту місця заповнюємо пробілами.

Якщо хтось дочитав цю писанину - дякую дуже. Це дуже сирий і не відпрацьований варіант.
А питання моє наступне. Коли я пробую зневаджувати цю програму я навіть не можу дійти до циклу щоб побачити свої помилки. А зупиняюсь на моменті із читанням файлу. Будь ласка, допоможіть мені вирішити цю проблему:
http://i.imgur.com/5VJgyd8.png
Перше зневаджування дійшов до циклу, але функція printf() хоч і виконувалась, але нічого не виводила, після першого проходження циклу програма впала із помилкою на скріні. Також дивно що незважаючи на те, що в файлі був лише рядок 1: Hello, world!\n, який мав би інтерпритуватись як 1: Hello, world!\0 в функції input(). В буфері (str_buf) містилось щось на кшталт цього: /315/287/2321: Hello, world!\\r
Друга спроба зневадити впала на цьому місці:
http://i.imgur.com/qbJgHw8.png
Я дійсно не знаю в чому причина. Я не маю досвіду і навіть не знаю в яку сторону копати.
В книзі Кернігана і Рітчі було сказано про те, що файл - це сукупність лексем, отаких 1: Hello, world!\n1: Hello, world!\n1: Hello, world!\n. Я зрозумів для себе що файл опрацьовується як одновимірний масив типу char і дуже довгим рядком, в кінці якого стоїть символ EOF.
Я нібито і відкриваю файл для читання, копіюю з нього по одному символу, коли зустрічаю \n заміняю його на \0, щоб потім добре працювати із масивом. Але в чому проблема - не розумію.

Білий Лунь

9

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

1.

fp = fopen("user_strings.txt", "r");
//отут слід ще додати таке: хтозна, чи існує такий файл?..
if( !fp) return (-1);

2. У вас у циклі посимвольного зчитування в input() відсутні будь-які перевірки на можливий вихід за рамки наявного буфера (у тому числі - нульові покажчики). Завдання завданням, а у коду має бути приємний запах. :)
3. Оті масиви у main(), які ви потім заповнюєте, можна перше й обнулити: не зашкодить.
4.

do{
        printf("Input number of columns (0 - default): ");
        scanf("%d", &cols);
    } while (cols < 0 || cols > MAX_COLUMNS);

Ліпше, імго, передавати даний параметр ззовні, бо функція трохи перевантаженою завданнями виходить.
5. Назви лічильників, імовірно, теж би якось змістовніше назвати, бо їх тут - сила...

I belong to the Dead Generation.
Подякували: Ярослав1

10

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

І ще - не робіть так:

(*(*(ptr+nfirst+j)+l+k)

В C є різні парадигми роботи з пам'яттю: як з масивом

for( int i = 0; i < 10; ++i )
  a[ i ] = 10;

і за допомогою вказівників

for( char *ptr = a; ptr < a + 10; ++ptr )
  *ptr = 10;

Обидва фрагменти роблять одне й те саме різними способами; на момент розробки C на комп'ютерах PDP-7/11 оптимальнішим був другий спосіб, архітектура Intel робить зручнішим для оптимізаторів перший. Але не змішуйте їх, код стає неможливо читати.

Подякували: Bartash, Ярослав, 0x9111A3

11 Востаннє редагувалося Ярослав (21.03.2014 19:39:18)

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

Bartash, спробував виправити те, на що ви вказали.
koala, уважно вчитався в ваші слова, але хочу уточнити, ви маєте на увазі, що треба працювати із покажчиком за допомогою інкрементування і декрементування і без індексів, а з індексами працюємо, коли взауємо порядковий номер у вимірі a[index]?

*(*(ptr+curr_ln+curr_col)+last_ch+curr_ch))

на щось інше, хіба що спочатку призначити цей символ змінній, а потім працювати із цією змінною.
Нова версія коду:

#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;
    }
    
    lineptr[0] = str_buf[0];
    nlines = input(lineptr);
    output(lineptr, nlines, cols);
    return (EXIT_SUCCESS);
}

int input(char **lineptr){
    printf("input :)\n");
    char c; // Приймає символ
    int curr_ln, curr_ch; // Поточний рядок, поточний символ
    FILE *fp; // Покажчик для лексем файлу
    fp = fopen("user_strings.txt", "r"); // Відкриваємо файл на читання
    if( !fp) // Файлу немає
        return (-1);
    curr_ln = curr_ch = 0; // Нульовий рядок і символ
    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) = 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, curr_ch, last_ch, curr_ln, col_width;
    toprint = curr_col = curr_ch = curr_ln = last_ch = col_width = 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++){
        printf("posv :)\n");
        posv[curr_col] = 0;
    }
    printf("prepare :)\n");
    for(curr_ln = 0; curr_ln < nlines; curr_ln++){
        printf("1 :)\n");
        toprint = 1;
        while(toprint > 0){
            printf("2 :)\n");
            toprint = 0;
            for(curr_col = 0; curr_col < cols; curr_col++){
                printf("3 :)\n");
                curr_ch = 0;
                last_ch = posv[curr_col];
                while( curr_ch < col_width 
                      && *(*(ptr+curr_ln+curr_col)+last_ch+curr_ch) != '\0' ){
                    printf("4.1 :)\n");
                    printf("%c", *(*(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){
                    printf("4.2 :)\n");
                    putchar(' ');
                    curr_ch++;
                }
            }
        }
    }
    printf("end of function :)\n");
}

Ось що виходить:
http://i.imgur.com/n3SNEgO.png

Якісь проблеми із читанням файлу, перші символи в рядках на кшталт таких: -69 '\\273'. Думаю проблеми в кодуванні - змінив із UTF-8 на ANSI, в масив записався перший рядок повністю, а при записі другого - така помилка:
http://i.imgur.com/StWV9nh.png

Знову таки не знаю в чому може бути причина, може щось не так з пам'яттю?

Я декілька разів обрав Forward and Continue і отримав повідомлення: No registers,
Після того у вікно виводу вивелось:

1 [main] strings_to_cols_v3 4172 cygwin_exception::open_stackdumpfile: Dumping stack trace to strings_to_cols_v3.exe.stackdump

Ось вміст створеного фалу:

Exception: STATUS_ACCESS_VIOLATION at eip=0040130F
eax=00000032 ebx=0022ABEC ecx=20018020 edx=00000000 esi=0000002E edi=00000073
ebp=00227808 esp=002277E0 program=C:\Users\YaR\Desktop\Creative\C\Strings_to_cols_v3\dist\Debug\Cygwin_4.x-Windows\strings_to_cols_v3.exe, pid 4172, thread main
cs=001B ds=0023 es=0023 fs=003B gs=0000 ss=0023
Stack trace:
Frame     Function  Args
00227808  0040130F (00227820, 0022ABB0, 00000000, 00000000)
0022ABC8  00401271 (00000001, 0022ABEC, 200280E8, 61007FCA)
0022ACF8  61008029 (00000000, 0022CD84, 610071C0, 00000000)
0022CD58  61005E84 (0022CD84, 00000000, 00000000, 00000000)
0022FF28  61005FF6 (610071C0, 00000000, 00000000, 00000000)
0022FF48  61006F44 (00401190, 00000000, 00000000, 00000000)
0022FF68  00401642 (00401190, 00000000, 00000000, 00000000)
0022FF88  00401015 (7FFDF000, 0022FFD4, 775F377B, 7FFDF000)
0022FF94  7745ED6C (7FFDF000, 75DDED3F, 00000000, 00000000)
0022FFD4  775F377B (00401000, 7FFDF000, 00000000, 00000000)
0022FFEC  775F374E (00401000, 7FFDF000, 00000000, 78746341)
End of stack trace

Білий Лунь

12

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

Десь так. Взагалі це серйозно залежить від контексту, але

ptr[ curr_ln + currcol ][ last_ch + curr_ch ]

коротше і зрозуміліше за

*(*(ptr+curr_ln+curr_col)+last_ch+curr_ch)
Подякували: Ярослав1

13 Востаннє редагувалося Ярослав (21.03.2014 19:47:01)

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

Дякую дуже, перепишу відповідно до вашого зауваження, а от що із файлом не так...
От стан fp:
http://i.imgur.com/zLDz0VZ.png

Білий Лунь

14 Востаннє редагувалося koala (21.03.2014 20:10:36)

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

Спробував розібратися, що ви все ж робите - і мені потрібні ваші коментарі: що таке lineptr і нащо він взагалі потрібен?
Одразу скажу - проблема в тому, як ви масив у функцію передаєте.

Подякували: Ярослав1

15

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

lineptr використовую для того, щоб встановити покажчики на відповідні рядки двовимірного масиву і передавати цей покажчик у функцію, бо думав що інакше цей масив у функцію не передаси.

Білий Лунь

16

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

Дякую дуже, так, правильно!!
Ось де була помилка:
в функції main()

    lineptr[0] = str_buf[0];

А інші покажчики залишились неініціалізованими.
Вирішив так

    for(i = 0; i < MAX_STRING_NUMBER; ++i){
        lineptr[i] = str_buf[i];
    }

Після того як закінчу зневаджувати викладу робочий код із коментарями.
Дякую дуже, Koala і Bartash!!

Білий Лунь
Подякували: koala, Bartash2

17

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){
    printf("input :)\n");
    char c; // Приймає символ
    int curr_ln, curr_ch; // Поточний рядок, поточний символ
    FILE *fp; // Покажчик для лексем файлу
    fp = fopen("user_strings.txt", "r"); // Відкриваємо файл на читання
    if( !fp) // Файлу немає
        return (-1);
    curr_ln = curr_ch = 0; // Нульовий рядок і символ
    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) = 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, curr_ch, last_ch, curr_ln, col_width;
    toprint = curr_col = curr_ch = curr_ln = last_ch = col_width = 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++){
        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' ){
                    // просто друкувати по порядку
                    printf("%c", *(*(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++;
                }
            }
        }
    }
    printf("end of function :)\n");
}

http://i.imgur.com/FVsDIxY.png

Білий Лунь

18

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

Пара зауважень для початку:

*(*(lineptr+curr_ln)+curr_ch)

значно красивіше записується як

lineptr[ curr_ln ][ curr_ch ]

Ініціалізуйте замість присвоювати:

    int toprint, posv[MAX_COLUMNS];
    int curr_col, curr_ch, last_ch, curr_ln, col_width;
    toprint = curr_col = curr_ch = curr_ln = last_ch = col_width = 0;
    /* визначення ширини колонок */
    col_width = ( OUTPUT_AREA - cols - 1 ) / cols;

Порівняйте:

    int toprint = 0,
        posv[MAX_COLUMNS];
        curr_col = 0,
        curr_ch = 0,
        last_ch = 0,
        curr_ln = 0,
        col_width = ( OUTPUT_AREA - cols - 1 ) / cols;

Решту теж не обов'язково занулювати - тих, хто занулюється пізніше. Або тоді не треба їх занулювати пізніше.

printf("%c", *(*(ptr+curr_ln+curr_col)+last_ch+curr_ch));

краще

putc( ptr[ curr_ln+curr_col ][ last_ch+curr_ch ] );

Ну і вміст файлу покажіть.

19

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){
    printf("input :)\n");
    char c; // Приймає символ
    int curr_ln, curr_ch; // Поточний рядок, поточний символ
    FILE *fp; // Покажчик для лексем файлу
    fp = fopen("user_strings.txt", "r"); // Відкриваємо файл на читання
    if( !fp) // Файлу немає
        return (-1);
    curr_ln = curr_ch = 0; // Нульовий рядок і символ
    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 ] = 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++){
        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' ){
                    // просто друкувати по порядку
                    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++;
                }
            }
        }
    }
    printf("end of function :)\n");
}

Вміст файлу user_strings.txt

1: Hello, world!
2: How are you?
3: I'm fine, thx!

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

Білий Лунь

20

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

Трохи таки спробував розібрати ваш код, і зрозумів, що не зовсім розумію завдання - чи, може, розумію його не так, як ви. Нам треба вивести рядки в колонки. Якщо колонок буде 3, то все ніби просто виглядатиме:

1: Hello, world!     |   2: How are you?         |  3: I'm fine, thx!

з точністю до ширини. А якщо 2?

1: Hello, world!     |   2: How are you?
3: I'm fine, thx!    |

чи

1: Hello, world!     |   3: I'm fine, thx!
2: How are you?    |

Тобто питання по загальній логіці завдання: коли рядок скінчився - що має записуватися в його колонці далі, який рядок? Якщо нічого - то нащо запитувати кількість колонок, їх же має бути стільки ж, скільки й рядків?