1

Тема: Побудова гістограми у терміналі.

Вітаю всіх з Новим роком!
Я новачок на форумі. В університеті вивчали С один семестр. Мені сподобалось писати програми і я вирішив надалі вивчати С.
Розпочав читати книгу Кернігана і Рітчі "Мова програмування C" третє видання.
Проблема з завдання на на створення гістограми.

Саме завдання (цитата з книги, книга російською):
"Упражнение 1.13. Напишите программу, печатающую гистограммы длин вводимых слов. Гистограмму легко
рисовать горизонтальными полосами. Рисование вертикальными полосами — более трудная задача."

Проблема полягає в тому, що гістограма створюється горизонтально. Мені потрібно щоб гістограма створювалась вертикально. Як це можна зробити? Підскажіть будь ласка. Без використання gotoxy і йому подібних функцій.
Мій код:

/*
Напишите программу, печатающую гистограммы длин вводимых слов. Гистограмму легко
рисовать горизонтальными полосами. Рисование вертикальными полосами — более трудная задача.
*/
#include <stdio.h>
#include <stdlib.h>
#define SIZE 10
void histogram (int *arr, int N); // виведення гістограми на екран

int main (void){
    int word, space = 0;                                                        // вводиться слов
    int counter = 0;                                                            // лічильник слів
    int *arr, *psarr;                                                           // один масив зберігає значення інший передає значення у випадку перевиділення пам'яті
    int N = 10, i = 0, j;                                                       // N - розмфр масиву, інші змінні - лічильники
    arr = (int*) calloc (N, sizeof (int));
    psarr = (int*) calloc (N, sizeof (int));
    while ((word = getchar ()) != EOF){ // лічильник слів та інші перевірки
        if (word == '\n' || word == ' ' || word == '\t')
            if (space == 0){
                arr[i] = counter;                                                  // елементу масиву присваюється кількість символів у слові
                psarr[i] = arr[i];                                                 // допоміжному масиву присваюється це саме знач.
                ++i;                                                               // індекс масиву збільшується
                if (i == N){                                                       // якщо була досягнута межа масиву
                    N += SIZE;                                                      // N збільшується на 10
                    arr = (int*) calloc (N, sizeof (int));                          // перевиділяється пам'ять з розміром на 10 більшим за попередній
                    for(j = 0; j < N - SIZE; j++)                                   // новоствореному масиву присваюються значення
                        arr[j] = psarr[j];
                    psarr = (int*) calloc (N, sizeof (int));                        // перевиділяється пам'ять допоміжного масиву на 10 більша
                    for(j = 0; j < N - SIZE; j++)                                   // присваюються знач. новому масиву
                        psarr[j] = arr[j];
                }
                counter = 0;
                space = 1;

             }
        if (word != '\n' && word != ' ' && word != '\t'){
            ++counter;
            space = 0;
        }
    }
    printf ("Your words:\n");
    for (i = 0; i < N; i++){ // вивід масиву на екран
        if(arr[i] == NULL)
            break;
        printf ("%4d", arr[i]);
    }
    histogram (arr, N);
    free (arr);
    free (psarr);
    printf ("\n");
    system ("pause");
    return 0;
}

void histogram (int *arr, int N){
    int i, j;
    printf("\n");
    for (i = 0; i < N; i++){
       if (arr[i] == NULL)
           break;
       for (j = 0; j < arr[i]; j++)
            printf("|");

    }
}

2

Re: Побудова гістограми у терміналі.

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

Вивід можна організувати порядково наступним чином:
1. Створити масив результатів гістограми (масив довжин).
2. У циклі виводити певну кількість пробілів/табів між стовпцями, а також зірочку, якщо довжина відповідного стовпця не менша від поточного номера рядка, який виводиться.
3. Алгоритм буде трохи відрізнятися у разі, якщо гістограма матиме основу вгорі або внизу вікна.

3

Re: Побудова гістограми у терміналі.

Точно таке ж запитання
http://www.cyberforum.ru/c-beginners/thread1511081.html
Можливо допоможе.

Подякували: leofun01, GVIG2

4

Re: Побудова гістограми у терміналі.

Ну і якщо вже беретесь за графіку в консолі, буде корисною псевдографіка

Подякували: GVIG, 0xDADA11C72

5

Re: Побудова гістограми у терміналі.

Дякую, за поради!
Буду розбиратись!

6

Re: Побудова гістограми у терміналі.

Ще варіант: побудувати гістограму у двовимірному масиві символів, а потім вивести його рядок за рядком.

Подякували: GVIG, leofun013

7

Re: Побудова гістограми у терміналі.

P.Y. написав:

Ще варіант: побудувати гістограму у двовимірному масиві символів, а потім вивести його рядок за рядком.

Мені здається, що це буде найпростіше!
Дякую. Напишу, покажу, що вийшло....

Подякували: 0xDADA11C7, leofun012

8 Востаннє редагувалося leofun01 (04.01.2016 23:36:23)

Re: Побудова гістограми у терміналі.

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

#include <stdio.h>
#include <stdlib.h>

#ifdef uint
#undef uint
#endif
#define uint unsigned int

// по замовчуванню повертає маскимум із { v1, v2 }.
double getMinOrMax(const double v1, const double v2, const bool min = false) { return (min ^ (v1 > v2)) ? v1 : v2; }

// getMinOrMaxElementIndex : повертає найменший індекс [найменшого/найбільшого] елемента масиву data.
// count : довжина масиву data, або кількість елементів, серед яких відбувається пошук.
// data : масив даних типу double, серед яких відбувається пошук [найменшого/найбільшого] елемента.
// const double *const data - неможна змінити вказівник і неможна змінити значення елементів масиву.
// min : true - шукає найменший елемент, false - шукає найбільший елемент.
// min = false : по замовчуванню функція шукає найбільший елемент.
int getMinOrMaxElementIndex(const uint count, const double *const data, const bool min = false)
{
    uint mI = 0,                // в змінній "mI" будемо тримати індекс [найменшого/найбільшого] елемента.
        i = 0;                  // змінною "i" будемо послідовно перебирати всі елементи масиву data.
    double mV = data[mI], v;    // : тут будуть зберігатися значення елементів масиву.
    while(++i < count) {        // : i = [1..(count-1)] в циклі перебираємо всі елементи.
        v = data[i];
        if(min ^ (v > mV))      // : якщо data[i] краще підходить ніж значення, яке було раніше,
        { mI = i; mV = v; }     // : то оновлюємо індекс і значення кращого елемента.
    }
    return mI;                  // : повертаємо знайдений індекс.
}

// getMinOrMaxElement : повертає найменший або найбільший елемент масиву data.
double getMinOrMaxElement(const uint count, const double *const data, const bool min = false)
{
    return data[getMinOrMaxElementIndex(count, data, min)];
}

// getRepeat : повертає в кілька разів більше стрічок ніж було передано в src.
// repeat : кількість повторень кожного рядка.
char *getRepeat(const uint count, const char *const src, const uint repeat)
{
    const uint strLen = count * repeat;
    char *str = (char*)malloc((strLen + 1) * sizeof(char));
    uint strI = 0, start, end = 0, i = 0;
    while(i < count) {
        while(src[i] != '\n' && src[i] != '\0')
            ++i;
        start = end;
        end = ++i;
        for(uint r = 0; r < repeat; ++r)
            for(i = start; i < end; ++i, ++strI)
                str[strI] = src[i];
        i = end;
    }
    str[strLen] = '\0';
    return str;
}

// transposeCharMatrix : повертає вказівник на масив символів,
// масив є транспонованою матрицею від масиву src.
// src : масив типу char, дані якого транспонуємо, радки розділені '\n'.
// width : ширина матриці src.
// height : висота матриці src.
char *transposeCharMatrix(const uint width, const uint height, const char *const src)
{
    const uint strLen = width * (height + 2) + 1;
    char *str = (char*)malloc(strLen * sizeof(char));
    uint strI = 0, x, y;
    for(y = 0; y < width; ++y) {
        for(x = 0; x < height; ++x)
            str[strI++] = src[(width + 2) * x + y];
        str[strI++] = '\r';
        str[strI++] = '\n';
    }
    str[strLen - 1] = '\0';
    return str;
}

// getFramedCharMatrix : повертає обрамлений текст.
char *getFramedCharMatrix(const uint width, const uint height, const char *const src)
{
    const uint strLen = (width + 4) * (height + 2);
    char *str = (char*)malloc((strLen + 1) * sizeof(char));
    uint x = 0, y = 0;
    str[0] = '\xC9';
    while(x < width)
        str[++x] = '\xCD';
    str[++x] = '\xBB';
    str[++x] = '\r';
    str[++x] = '\n';
    while(++y <= height) {
        x = 0;
        str[(width + 4) * y] = '\xBA';
        while(x < width)
            str[(width + 4) * y + x + 1] = src[(width + 2) * (y - 1) + x++];
        str[(width + 4) * y + ++x] = '\xBA';
        str[(width + 4) * y + ++x] = '\r';
        str[(width + 4) * y + ++x] = '\n';
    }
    y *= (width + 4);
    x = 0;
    str[y] = '\xC8';
    while(x < width)
        str[y + ++x] = '\xCD';
    x += y;
    str[++x] = '\xBC';
    str[++x] = '\r';
    str[++x] = '\n';
    str[strLen] = '\0';
    return str;
}

// getHistogram : повертає вказівник на масив символів,
// масив є стрічковим представленням гістограми побудованої на основі даних масиву data.
// maxLen : довжина гістограми (вимірюється в кількості символів),
// не рекомендується використовувати значення більше ніж [ширина/висота] вікна.
// vertical : true - стовпці будуть вертикальними, false - стовпці будуть горизонтальними.
// vertical = false : по замовчуванню функція робить стовпці горизонтальними.
// framed : true - обрамлена гістограма, false - не обрамлена гістограма.
char *getHistogram(const uint count, const double *const data, const uint maxLen,
                   const int start = 0, const uint repeat = 1,
                   const bool vertical = false, const bool framed = false)
{
    const uint strLen = count * (maxLen + 2),
               minI = getMinOrMaxElementIndex(count, data, true),         // : індекс мінімального елемента.
               maxI = getMinOrMaxElementIndex(count, data, false);        // : індекс максимального елемента.
    const double minV = data[minI] < start ? data[minI] : start,
                 maxV = data[maxI] > start ? data[maxI] : start,
                 rangeV = maxV - minV,                                    // : діапазон даних.
                 scalFactor = (double)maxLen / (rangeV > 0 ? rangeV : 1); // : задає маштабування.
    char *str = (char*)malloc((strLen + 1) * sizeof(char));  // calloc(strLen + 1, sizeof(char)); - теж можна використовувати.
    uint dataI, strI = 0, i, v,
        zero = (uint)((start - minV) * scalFactor + 0.5);    // : позиція нуля в гістограмі.
    for(dataI = 0; dataI < count; ++dataI) {
        v = (uint)((data[dataI] - minV) * scalFactor + 0.5);  // : знаходимо розмір стовпця в гістограмі.
        for(i = 0; i < maxLen; ++i)
            // наступний рядок просто заповнює гістограму.
            str[strI++] = ((i >= zero) && (i >= v) || (i < zero) && (i < v)) ? '\x20' : '\xDB'; // '\u0032' : '\u0219';
        str[strI++] = '\r';   // : переходимо на початок рядка.
        str[strI++] = '\n';   // : переходимо на наступний рядок.
    }
    str[strLen] = '\0';       // : завжди додаю його в кінець стрічки.
    char *trash;
    if(repeat > 1) {
        str = getRepeat(strLen, trash = str, repeat);                   // : повторюємо стовпці, якщо потрібно.
        free(trash);
    }
    if(vertical) {
        str = transposeCharMatrix(maxLen, count * repeat, trash = str); // : транспонуємо, якщо потрібно.
        free(trash);
    }
    if(framed) {
        trash = str;
        str = vertical ?
            getFramedCharMatrix(count * repeat, maxLen, trash) :        // : ставимо в рамку, якщо потрібно.
            getFramedCharMatrix(maxLen, count * repeat, trash);
        free(trash);
    }
    return str;
}

int main(void)
{
    uint count = 5, i = 0;
    /*// ----------------------------------------------------- //
    scanf("%u", &count);
    double *data = (double*)malloc(count * sizeof(double));
    while(i < count) {
        //scanf("%g", &*(data + i++));    // not work
        scanf("%g", &data[i++]);        // not work
        //scanf("%g", data + i++);        // not work
    }
    // ----------------------------------------------------- //*/
    // ----------------------------------------------------- //
    double *data = (double*)malloc(count * sizeof(double));
    data[0] = 5;
    data[1] = 2;
    data[2] = 8.5;
    data[3] = 3;
    data[4] = 1;
    // ----------------------------------------------------- //*/
    char *str;
    str = getHistogram(count, data, 78, 0, 2, false, true);
    printf("\r\n Horizontal :\r\n%s", str);
    free(str);
    str = getHistogram(count, data, 20, 0, 6, true, true);
    printf("\r\n Vertical :\r\n%s", str);
    free(str);
    printf("\r\n");
    free(data);
}

все прекрасно працює крім введення чисел з допомогою scanf в функції main.
Тобто якщо статичну ініціалізацію масиву закоментувати, а динамічну розкоментувати, то воно не працює (працює неправильно).
Проблема конкретно у введенні чисел в масив.
В чому моя помилка ? чи то компілятор (VS2013) мене тролить ?

9 Востаннє редагувалося leofun01 (05.01.2016 00:22:18)

Re: Побудова гістограми у терміналі.

Kane 2.0, Ви мене врятували. Тепер все працює прекрасно.

Залишу тут робочу версію.

#include <stdio.h>
#include <stdlib.h>

#ifdef uint
#undef uint
#endif
#define uint unsigned int

// по замовчуванню повертає маскимум із { v1, v2 }.
double getMinOrMax(const double v1, const double v2, const bool min = false) { return (min ^ (v1 > v2)) ? v1 : v2; }

// getMinOrMaxElementIndex : повертає найменший індекс [найменшого/найбільшого] елемента масиву data.
// count : довжина масиву data, або кількість елементів, серед яких відбувається пошук.
// data : масив даних типу double, серед яких відбувається пошук [найменшого/найбільшого] елемента.
// const double *const data - неможна змінити вказівник і неможна змінити значення елементів масиву.
// min : true - шукає найменший елемент, false - шукає найбільший елемент.
// min = false : по замовчуванню функція шукає найбільший елемент.
int getMinOrMaxElementIndex(const uint count, const double *const data, const bool min = false)
{
    uint mI = 0,                // в змінній "mI" будемо тримати індекс [найменшого/найбільшого] елемента.
        i = 0;                  // змінною "i" будемо послідовно перебирати всі елементи масиву data.
    double mV = data[mI], v;    // : тут будуть зберігатися значення елементів масиву.
    while(++i < count) {        // : i = [1..(count-1)] в циклі перебираємо всі елементи.
        v = data[i];
        if(min ^ (v > mV))      // : якщо data[i] краще підходить ніж значення, яке було раніше,
        { mI = i; mV = v; }     // : то оновлюємо індекс і значення кращого елемента.
    }
    return mI;                  // : повертаємо знайдений індекс.
}

// getMinOrMaxElement : повертає найменший або найбільший елемент масиву data.
double getMinOrMaxElement(const uint count, const double *const data, const bool min = false)
{
    return data[getMinOrMaxElementIndex(count, data, min)];
}

// getRepeat : повертає в кілька разів більше стрічок ніж було передано в src.
// repeat : кількість повторень кожного рядка.
char *getRepeat(const uint count, const char *const src, const uint repeat)
{
    const uint strLen = count * repeat;
    char *str = (char*)malloc((strLen + 1) * sizeof(char));
    uint strI = 0, start, end = 0, i = 0;
    while(i < count) {
        while(src[i] != '\n' && src[i] != '\0')
            ++i;
        start = end;
        end = ++i;
        for(uint r = 0; r < repeat; ++r)
            for(i = start; i < end; ++i, ++strI)
                str[strI] = src[i];
        i = end;
    }
    str[strLen] = '\0';
    return str;
}

// transposeCharMatrix : повертає вказівник на масив символів,
// масив є транспонованою матрицею від масиву src.
// src : масив типу char, дані якого транспонуємо, радки розділені '\n'.
// width : ширина матриці src.
// height : висота матриці src.
char *transposeCharMatrix(const uint width, const uint height, const char *const src)
{
    const uint strLen = width * (height + 2) + 1;
    char *str = (char*)malloc(strLen * sizeof(char));
    uint strI = 0, x, y;
    for(y = 0; y < width; ++y) {
        for(x = 0; x < height; ++x)
            str[strI++] = src[(width + 2) * x + y];
        str[strI++] = '\r';
        str[strI++] = '\n';
    }
    str[strLen - 1] = '\0';
    return str;
}

// getFramedCharMatrix : повертає обрамлений текст.
char *getFramedCharMatrix(const uint width, const uint height, const char *const src)
{
    const uint strLen = (width + 4) * (height + 2);
    char *str = (char*)malloc((strLen + 1) * sizeof(char));
    uint x = 0, y = 0;
    str[0] = '\xC9';
    while(x < width)
        str[++x] = '\xCD';
    str[++x] = '\xBB';
    str[++x] = '\r';
    str[++x] = '\n';
    while(++y <= height) {
        x = 0;
        str[(width + 4) * y] = '\xBA';
        while(x < width)
            str[(width + 4) * y + x + 1] = src[(width + 2) * (y - 1) + x++];
        str[(width + 4) * y + ++x] = '\xBA';
        str[(width + 4) * y + ++x] = '\r';
        str[(width + 4) * y + ++x] = '\n';
    }
    y *= (width + 4);
    x = 0;
    str[y] = '\xC8';
    while(x < width)
        str[y + ++x] = '\xCD';
    x += y;
    str[++x] = '\xBC';
    str[++x] = '\r';
    str[++x] = '\n';
    str[strLen] = '\0';
    return str;
}

// getHistogram : повертає вказівник на масив символів,
// масив є стрічковим представленням гістограми побудованої на основі даних масиву data.
// maxLen : довжина гістограми (вимірюється в кількості символів),
// не рекомендується використовувати значення більше ніж [ширина/висота] вікна.
// vertical : true - стовпці будуть вертикальними, false - стовпці будуть горизонтальними.
// vertical = false : по замовчуванню функція робить стовпці горизонтальними.
// framed : true - обрамлена гістограма, false - не обрамлена гістограма.
char *getHistogram(const uint count, const double *const data, const uint maxLen,
                   const int start = 0, const uint repeat = 1,
                   const bool vertical = false, const bool framed = false)
{
    const uint strLen = count * (maxLen + 2);
    const double minV = getMinOrMax(getMinOrMaxElement(count, data, true), start, true),   // : мінімальний елемент.
                 maxV = getMinOrMax(getMinOrMaxElement(count, data, false), start, false), // : максимальний елемент.
                 rangeV = maxV - minV,                                    // : діапазон даних.
                 scalFactor = (double)maxLen / (rangeV > 0 ? rangeV : 1); // : задає маштабування.
    char *str = (char*)malloc((strLen + 1) * sizeof(char));  // calloc(strLen + 1, sizeof(char)); - теж можна використовувати.
    uint dataI, strI = 0, i, v,
        zero = (uint)((start - minV) * scalFactor + 0.5);    // : позиція нуля в гістограмі.
    for(dataI = 0; dataI < count; ++dataI) {
        v = (uint)((data[dataI] - minV) * scalFactor + 0.5);  // : знаходимо розмір стовпця в гістограмі.
        for(i = 0; i < maxLen; ++i)
            // наступний рядок просто заповнює гістограму.
            str[strI++] = ((i >= zero) && (i >= v) || (i < zero) && (i < v)) ? '\x20' : '\xDB'; // '\u0032' : '\u0219';
        str[strI++] = '\r';   // : переходимо на початок рядка.
        str[strI++] = '\n';   // : переходимо на наступний рядок.
    }
    str[strLen] = '\0';       // : завжди додаю його в кінець стрічки.
    char *trash;
    if(repeat > 1) {
        str = getRepeat(strLen, trash = str, repeat);                   // : повторюємо стовпці, якщо потрібно.
        free(trash);
    }
    if(vertical) {
        str = transposeCharMatrix(maxLen, count * repeat, trash = str); // : транспонуємо, якщо потрібно.
        free(trash);
    }
    if(framed) {
        trash = str;
        str = vertical ?
            getFramedCharMatrix(count * repeat, maxLen, trash) :        // : ставимо в рамку, якщо потрібно.
            getFramedCharMatrix(maxLen, count * repeat, trash);
        free(trash);
    }
    return str;
}

int main(void)
{
    uint count = 0, i = 0;
    printf("\r\n Enter size of array : ");
    scanf("%u", &count);
    double *data = (double*)malloc(count * sizeof(double));
    printf("\r\n Enter elements of array : ");
    while(i < count)
        scanf("%lf", &data[i++]);
    char *str;
    str = getHistogram(count, data, 78, 0, 2, false, true);
    printf("\r\n Horizontal :\r\n%s", str);
    free(str);
    str = getHistogram(count, data, 20, 0, 6, true, true);
    printf("\r\n Vertical :\r\n%s", str);
    free(str);
    printf("\r\n");
    free(data);
}

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

Подякували: GVIG1

10 Востаннє редагувалося GVIG (06.01.2016 00:02:48)

Re: Побудова гістограми у терміналі.

Сьогодні сидів і намагався написати гістограму через масив. Вона просто виводить на екран числа, які знаходяться у масиві arr.
Ось, що вийшло:

#include <stdio.h>
#include <stdlib.h>
void histogram (int *arr, int N);  // будує гістограму

int main (void){
    int N = 3;
    int arr[N] = {1, 4, 3}; // масив
    histogram (arr, N);
    return 0;
}
/*
 * i та j - лічильники для масивів.
 * **hist - масив (тип char) - гістограма.
 * Nmax - максимальна довжина гістограми (найбільший елемент масиву arr).
 * Алгоритм роботи програми (в загальному випадку):
 * 1) шукається найбільший елемент масиву Nmax;
 * 2) виділяється пам'ять для масиву вказівників розміром Nmax;
 * 3) виділяється пам'ять для кожного вказівника розміром N;
 * 4) масив:
 *    [0][0] [0][1] [0][2]
 *    [1][0] [1][1] [1][2]
 *    [2][0] [2][1] [2][2]
 *    [3][0] [3][1] [3][2]
 * 5) k = arr[j];
 * 6) береться [3][0], в циклі k--, поки k > 0, [3][0] = '_' (в даному випадку);
 * 7) береться [3][1], в циклі k--, поки k > 0, [3][1] = '_', [2][1] = '_', [1][1] = '_' [0][1] = '_';
 * 8) береться [3][2], в циклі k--, поки k > 0, [3][2] = '_', [2][2] = '_', [1][2] = '_';
 * 9) виводиться гістограма на екран
 */
void histogram (int *arr, int N){
    int i, j, k;
    int Nmax = arr[0];
    char **hist;
    for (i = 1; i < N; i++)
        if (Nmax < arr[i])
            Nmax = arr[i];
    hist = (char**) calloc (Nmax, sizeof (char*));
    for (i = 0; i < Nmax; i++)
        hist[i] = (char*) calloc (N, sizeof (char));
    printf ("Histogram:\n");
    for (j = 0; j < N; j++){
        k = arr[j];
        for (i = Nmax - 1; i >= 0; i--, k--)
            if (k > 0)
                hist[i][j] = '_';
            else i--;
    }
    for (i = 0; i < Nmax; i++){
        for (j = 0; j < N; j++)
            printf ("%4c", hist[i][j]);
        printf ("\n");
    }
    printf ("\n");
    for (i = 0; i < N; i++)
        printf ("%4d", arr[i]);
    printf ("\n");
    for (i = 0; i < Nmax; i++)
        free (hist[i]);
    free (hist);
}

Описав свій хід думок. Якщо щось не правильно, виправте будь ласка =)

11

Re: Побудова гістограми у терміналі.

Я написав це завдання:

/*
Напишите программу, печатающую гистограммы длин вводимых слов. Гистограмму легко
рисовать горизонтальными полосами. Рисование вертикальными полосами — более трудная задача.
*/
#include <stdio.h>
#include <stdlib.h>
char *getString (char *str, int *pN); // виділення пам'яті та введення рядку, повертає кількість символів у рядку
void printString (char *str, int N); // виведення рядку на екран
void histogram (char *str, int N); // виведення гістограми на екран

int main (void){
    int N; // кількість символів у рядку (включаючи нуль символ)
    char *str;
    str = (char*) calloc (1, sizeof (str));
    str = getString (str, &N);
    printString(str, N);
    histogram (str, N);
    free (str);
    system ("pause");
    return 0;
}
/*
 * str - основний масив
 * strd - допоміжний масив (копія str)
 * N - кількість символів у масиві.
 * i - лічильник. Коли і == N перевиділяється пам'ять для основного масиву та str присвоюються знач. strd
 * j - лічильник, для передачі знач. з одного масиву в інший
 * c - змінна в яку вводяться дані
 * Коли всі символи записані у масив (включаючи '\n' та нуль символ) N визначається знову:
 * N збільшується на один (для збереження нуль символа). Далі йде перевиділення пам'яті для str та передача знач. з strd
*/
char *getString (char *str, int *pN){ // виділення пам'яті та введення рядку, повертає кількість символів у рядку
    int N = 20;
    int i, j;
    char *strd;
    char c;
    printf ("Enter your string:\n");
    if ((str = (char*) calloc (N, sizeof (str))) == NULL){ // основний масив
        printf ("Error, cannot allocate memory!\n");
        exit(1);
    }
    if ((strd = (char*) calloc (N, sizeof (strd))) == NULL){ // допоміжний масив
        printf ("Error, cannot allocate memory!\n");
        exit(2);
    }
    for (i = 0; (c = getchar ()) != EOF && c != '\n'; i++){
        if (i == N - 1){
            N += N;
            if ((str = (char*) calloc (N, sizeof (str))) == NULL){ // основний масив
                printf ("Error, cannot allocate memory!\n");
                exit(3);
            }
            for (j = 0; j < N; j++)
                if (strd[j] == NULL)
                    break;
                else str[j] = strd[j];
            if ((strd = (char*) calloc (N, sizeof (strd))) == NULL){ // допоміжний масив
                printf ("Error, cannot allocate memory!\n");
                exit(3);
            }
            for (j = 0; j < N; j++)
                if (str[j] == NULL)
                    break;
                else strd[j] = str[j];
        }
        strd[i] = str[i] = c;
    }
    if (c == '\n')
        strd[i] = str[i] = '\n';
    N = i + 1;
    str = (char*) calloc (N, sizeof (str));
    for (j = 0; j < N; j++)
        str[j] = strd[j];
    *pN = N;
    free (strd);
    return str;
}

void printString (char *str, int N){ // виведення символьного рядку на екран
    int i;
    for (i = 0; i < N; i++)
        printf ("%c", str[i]);
}

void histogram (char *str, int N){ // виведення гістограми на екран
    int i, sym, j = 0;
    int Nnum = 0;
    int *numArr;
    char **hist;
    for (i = 0; i < N; i++)
        if (str[i] == ' ' || str[i] == '\n')
            Nnum++;
    numArr = (int*) calloc (Nnum, sizeof (int));
    for (i = 0; i < Nnum; i++)
        for (sym = 0; j < N; j++)
            if (str[j] != ' ' && str[j] != '\t' && str[j] != '\n')
                sym++;
            else {
               numArr[i] = sym;
               j++;
               break;
            }
    for (i = 0; i < Nnum; i++)
        printf ("%4d", numArr[i]);
    printf ("\n");

    int Nmax = numArr[0];

    for (i = 0; i < Nnum; i++)
        if (Nmax < numArr[i])
            Nmax = numArr[i];
    hist = (char**) calloc (Nmax, sizeof (char*));
    for (i = 0; i < Nmax; i++)
        hist[i] = (char*) calloc (Nnum, sizeof (char));
    for (j = 0; j < Nnum; j++){
        sym = numArr[j];
        for (i = Nmax - 1; i >= 0; i--, sym--)
            if (sym > 0)
                hist[i][j] = '_';
            else i--;
    }
    for (i = 0; i < Nmax; i++){
        for (j = 0; j < Nnum; j++)
            printf ("%4c", hist[i][j]);
        printf ("\n");
    }
}
Подякували: 0xDADA11C7, leofun012

12

Re: Побудова гістограми у терміналі.

Які бібліотеки є для роботи з координатами в терміналі?
Крім gotoxy. Я використовую Qt.

13

Re: Побудова гістограми у терміналі.

Беріть кросплатформовий NCurses, звичайно ви можете обрати й платформозалежні рішення -- ESC-послідовності для Linux або WinAPI

Подякували: GVIG, leofun012