K&R 练习 1.21

K&R Exercise 1.21

目前正在努力解决 ex 1.21。任务是:编写一个程序entab,用最少的数字替换空白字符串 制表符和空格以达到相同的间距。使用与 detab 相同的制表位。什么时候 制表符或单个空白足以到达制表位,应该给出 偏好?

以下是我到目前为止编写的代码。我输入小于 tabsize 的空格没有问题,但是当它大于 tabsize 时,我会遇到分段错误。有谁能帮帮我吗?

#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] = '[=10=]';
        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] = '[=10=]';
    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;
    }
}

因为 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] = '[=10=]';
        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] = '[=10=]';
    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;
}

在我的解决方案中,我使用外部变量作为输出行的索引计数器。 我制作了用于将输入数组转换为输出数组的分离函数。

#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] = '[=10=]';
    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] != '[=10=]') {
        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] = '[=10=]';
}

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;
}