K&R Упражнение 1.21

В настоящее время пытается решить ex 1.21. Задача: Написать entab-программу, которая заменяет строки пробелов минимальным количеством табуляции и пробелов для достижения того же интервала. Используйте те же позиции табуляции, что и для detab . Когда для достижения позиции табуляции достаточно табуляции или одного пробела, чему следует отдать предпочтение?

Ниже мой код, который я сделал до сих пор. У меня нет проблем с вводом количества пробелов меньше размера табуляции, но когда оно больше этого, я получаю ошибку сегментации. Кто-нибудь может мне помочь, пожалуйста?

#include <stdio.h>
#include <ctype.h>
#define TAB 8
#define MAXLINE 1000

int getmine(char s[], int lim);
int entab(char output[], int pos, int space);

int main()
{
    char line[MAXLINE], output[MAXLINE];
    int i;
    while ((i = getmine(line, MAXLINE)) > 0)
    {
        int space = 0;
        int pos = 0;
        int count = 0;
        while (line[count] != '\n')
        {
            if (line[count] != ' ')
            {
                space = 0;
                output[count] = line[count];
            }
            else if (line[count] == ' ')
            {
                pos = count - space;
                space++;
                if (line[count + 1] != ' ')
                {
                    if (space > TAB)
                    {
                        int z = entab(output, pos, space);
                        count = z;
                    }
                    else
                    {
                        for (int a = 0; a < space; a++)
                            output[pos + a] = ' ';
                    }
                }
            }
            count++;
        }
        if (line[count] == '\n')
        {
            output[count] = line[count];
            count++;
        }
        output[count] = '\0';
        printf("%s", output);
    }
}

int getmine(char s[],int lim)
{
    int c, i;
    for (i=0; i < lim-1 && (c=getchar())!=EOF && c!='\n'; ++i)
        s[i] = c;
    if (c == '\n') 
    {
        s[i] = c;
        ++i;
    }
    s[i] = '\0';
    return i;
}

int entab(char output[], int pos, int space)
{
    int nTabs = 0;
    int nSpaces = 0;
    int x = TAB - (pos % TAB);
    if (x > 0)
    {
        output[pos] = '\t';
        space = space - x;
        nTabs = space / TAB;
        nSpaces = space % TAB;
        for (int a = 0; a < nTabs; a++)
            output[pos + 1 + a] = '\t';
        for (int b = 0; b < nSpaces; b++)
            output[pos + 1 + nTabs + b] = ' ';
        return pos + nTabs + nSpaces + 1;
    }
    else if (x == 0)
    {
        nTabs = space / TAB;
        nSpaces = space % TAB;
        for (int a = 0; a < nTabs; a++)
            output[pos + a] = '\t';
        for (int b = 0; b < nSpaces; b++)
            output[pos + nTabs + b] = ' ';
        return pos + nTabs + nSpaces;
    }
}

person Viapacific    schedule 11.01.2020    source источник
comment
Пробовали ли вы отлаживать и детально смотреть, что происходит не так, когда и как? ericlippert.com/2014/03/05/how-to -отладка-маленьких-программ   -  person Yunnosch    schedule 11.01.2020
comment
Как отмечается в первом ответе, этот код должен отслеживать количество байтов, подготовленных для вывода, отдельно от количества байтов, обрабатываемых в настоящее время из ввода. Эти числа расходятся и не могут храниться в одной переменной count. Однако этот код сложнее, чем нужно. Нет необходимости в линейном буфере. Нужно просто знать текущий выходной столбец по модулю TAB и количество пробелов, увиденных с момента последнего не-пробела…   -  person Eric Postpischil    schedule 11.01.2020
comment
… Предположим, мы только что получили символ, и это пробел. Если текущий столбец равен column (начиная с 0 в начале строки), а количество видимых пробелов равно spaces, то, если current + spaces равно TAB, напишите символ табуляции и установите для spaces значение 0. Если новый символ — это новой строки, напишите его и установите column на 0 и spaces на 0. Если новый символ является табуляцией, напишите его и установите current на 0 и spaces на 0. В противном случае напишите новый символ, установите column на (column+1) % TAB и установите spaces на 0.   -  person Eric Postpischil    schedule 11.01.2020


Ответы (2)


Поскольку последовательности пробелов из 8 символов заменяются одним символом (табуляция), количество прочитанных байтов на входе не совпадает с записанными байтами на выходе. Простое решение для этого состоит в том, чтобы просто отслеживать две переменные, одну для считанных байтов со входа и одну для записанных байтов на выходе. Вы могли бы сделать это более аккуратно с помощью указателей, хотя, если вы следуете книге в линейной манере, указатели IIRC будут представлены позже.

Непроверенный (а именно для демонстрационных целей) код ниже. ПРИМЕЧАНИЕ. Я позволил себе некоторые вольности при переформатировании вашего кода для краткости.

#include <stdio.h>
#include <ctype.h>
#define TAB 8
#define MAXLINE 1000

int getmine(char s[], int lim);
int entab(char output[], int pos, int space);

int main()
{
    char line[MAXLINE], output[MAXLINE];
    int i;
    while ((i = getmine(line, MAXLINE)) > 0) {
        int space = 0;
        int pos = 0;

        int read_bytes = 0;
        int write_bytes = 0;

        while(read_bytes < i) {
            if (line[read_bytes] != ' ') {
                space = 0;
                output[write_bytes++] = line[read_bytes];
            }
            else if (line[read_bytes] == ' ') {
                space++;
                if (line[read_bytes + 1] != ' ') {
                    if (space > TAB) {
                        write_bytes += entab(output, write_bytes, space);
                    } else {
                        for (int i = 0; i < space; i++)
                            output[write_bytes++] = ' ';
                    }
                }
            }
            read_bytes++;
        }
        if (line[read_bytes] == '\n') {
            output[write_bytes++] = line[read_bytes];
        }
        output[write_bytes] = '\0';
        printf("%s", output);
    }
}

int getmine(char s[],int lim)
{
    int c, i;
    for (i=0; i < lim-1 && (c=getchar())!=EOF && c!='\n'; ++i)
        s[i] = c;
    if (c == '\n')  {
        s[i] = c;
        ++i;
    }
    s[i] = '\0';
    return i;
}

int entab(char output[], int pos, int space)
{
    int nTabs = 0;
    int nSpaces = 0;
    int x = TAB - (pos % TAB);
    if (x > 0) {
        output[pos] = '\t';
        space = space - x;
        nTabs = space / TAB;
        nSpaces = space % TAB;
        for (int i = 0; i < nTabs; i++)
            output[pos + 1 + i] = '\t';
        for (int i = 0; i < nSpaces; i++)
            output[pos + 1 + nTabs + i] = ' ';
        return nTabs + nSpaces + 1;
    } else if (x == 0) {
        nTabs = space / TAB;
        nSpaces = space % TAB;
        for (int i = 0; i < nTabs; i++)
            output[pos + i] = '\t';
        for (int i = 0; i < nSpaces; i++)
            output[pos + nTabs + i] = ' ';
    }
    return nTabs + nSpaces;
}
person vmt    schedule 11.01.2020

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

#include <stdio.h>

#define TAB 8
#define MAX 100;

int j; //counter of newline
char line[MAX];
char newline[MAX];

int getter(void);
void converter(void);
int entab(int space_counter, int line_index);

int main()
{
    extern char line[];
    extern char newline[];
    int len;
    while ((len = getter()) > 0) {
        converter();
        printf("%s", newline);
    }
}

int getter() 
{
    int c;
    int i;
    i = 0;
    while ((c = getchar()) != EOF && c != '\n' && i < 30-1) {
        line[i] = c;
        ++i;
    }
    if (c == '\n') {
        line[i] = c;
        ++i;
    }
    line[i] = '\0';
    return i;
}

void converter(void)
{
    extern int j;
    extern char line[];
    extern char newline[];
    int space_counter;
    int i;
    i = j = 0;
    space_counter = 0;
    while (line[i] != '\0') {
        while (line[i] == ' ') {
            space_counter++;
            i++;
        }
        if (space_counter > 0) {
            j = entab(space_counter, i);
            space_counter = 0;
        }
        else {
            newline[j] = line[i];
            j++;
            i++;
        }
    }
    newline[j] = '\0';
}

int entab(int space_counter, int end_point)
{
    extern int j;
    extern char newline[];
    int new_index;
    int start_point;
    int tab_qty;
    int space_qty;
    start_point = end_point - space_counter;
    tab_qty = end_point / TAB - start_point / TAB;
    if (TAB > start_point) 
        space_qty = space_counter;
    else
        space_qty = end_point % TAB;
    for (tab_qty; tab_qty > 0; tab_qty--) {
        newline[j] = '\t';
        j++;
    }
    for (space_qty; space_qty > 0; space_qty--) {
        newline[j] = '|';
        j++;
    }
    return new_index = j;
}
person Albert Albert    schedule 20.07.2021
comment
Я подумал, что должен использовать внешние данные, потому что это упражнение находится ниже этой части) - person Albert Albert; 20.07.2021