C语言 使用(或不使用)数组替换TAB的空格

oknwwptz  于 2023-10-16  发布在  其他
关注(0)|答案(4)|浏览(93)

我试着遵循一些建议,开始阅读C编程语言的书。书中每章都有几个练习题和结尾。
我刚刚做了以下练习:
写一个程序 entab,用最小数量的制表符和空格来替换空格字符串,以达到相同的间距。当一个拉环或一个空白就足以到达一个停止拉环时,哪一个会被优先考虑?
我对这个问题的解决方法如下:

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

int get_string(char string[], int index)
{
    for(int position = 0; position < index; position++)
    {
        string[position] = getchar();
        if(string[position] == '\n')
        {
            string[position] = '\0';
            return position;
        }
    }
    return index;
}

int get_spaces(char string[], int i)
{
    int temp_i = 1;
    while(string[i+1] == ' ')
    {
        temp_i++;
        i++;
    }
    return temp_i;
}

int get_tabs(int spaces)
{
    int tabs = 0;
    tabs = spaces / 4;
    return tabs;
}

void entab(char string[], int max_index)
{
    int i = 0;
    int spaces = 0;
    int tabs = 0;
    while(i <= max_index)
    {
        if(string[i] == '\t')
        {
            printf("\t");
            i++;
        }
        else if(string[i] == ' ')
        {
            spaces = get_spaces(string, i);
            i = i + spaces;
            if(spaces == 4)
            {
                printf("\t");
                spaces = 0;
            }
            else if (spaces < 4)
            {
                for(int i = 0; i < spaces; i++)
                {
                    putchar(' ');
                }
                spaces = 0;
            }
            else
            {
                tabs = get_tabs(spaces);
               // printf("%d", tabs);
                spaces = spaces - (tabs*4);
               // printf("%d", spaces);
                for(int i = 0; i < tabs; i++)
                    printf("\t");
                for(int i = 0; i < spaces; i++)
                    printf(" ");
                tabs = 0;
                spaces = 0;
            }
        }
        else
        {
            putchar(string[i]);
            i++;
        }
    }
}
int main()
{

    char string[100] = "";
    int howlong = get_string(string, 100);

    entab(string, howlong);

    return 0;
}

2个问题:
1.要到达制表位,制表符可能是必要的,这怎么可能呢(cit.当一个拉环或一个空白就足以到达一个停止拉环时,哪一个会被优先考虑?我的意思是,你需要一个空间,所以你不能选择一个或另一个,这是两种不同的情况。还是不去?
1.我想不可能直接在第一个while循环中执行而不将其存储在数组中?是吗
我对C编程很陌生,所以不要高估我的能力,我想你可以看到我不擅长它,我正在努力学习。
我不知道如何缩小程序。这似乎是一项容易的任务,但我不知道如何解决这个问题。
我觉得它不能正常工作
编辑:
我已经重写了一切。我肯定它的工作,但不知道为什么它打印更多的空间,这些空间是从哪里来的

qq24tv8q

qq24tv8q1#

当一个拉环或一个空白就足以到达一个停止拉环时,哪一个会被优先考虑?
关键是减少输出的大小。由于空格和制表符的大小相同,所以这并不重要。所以随便你用。
我想不可能直接在第一个while循环中执行而不将其存储在数组中?是吗
不需要数组。这只是一个跟踪三个变量的问题:

  • 行中的当前位置。
  • 无论我们目前是否处于空白状态。
  • 当前空白范围开始的位置。
    算法

(支持空格、制表符和LF作为控制字符。)
1.将当前位置初始化为1。
1.将in whitespace标志初始化为0。
1.创建空白起始位置变量。
1.循环
1.读一个字符。
1.如果已经达到EOF,
1.跳出循环。
1.如果字符是LF,
1.将in whitespace标志设置为0。
1.将当前位置设置为1。
1.否则
1.如果字符是空格,
1.如果空白标志为假,
1.将in whitespace标志设置为1。
1.将空白开始设置为当前位置。
1.在当前位置添加一个。
1.否则
1.字符是制表符,
1.如果空白标志为假,
1.将in whitespace标志设置为1。
1.将空白开始设置为当前位置。
1.在当前位置上添加适当的量。
1.否则
1.如果空白标志为真,
1.根据当前位置和空白起始位置计算要输出的制表符和空格的适当数量。
1.输出它们。
1.将in whitespace标志设置为false。
1.输出字符。
1.在当前位置添加一个。

ar5n3qh5

ar5n3qh52#

#include <stdio.h>

#define TabWidth    4

int main(void)
{
    int CC = 0; //  Current physical output column is zero.
    int DC = 0; //  Current desired output column is zero.

    while (1)
    {
        //  Request next character.  If none, exit.
        int c = getchar();
        if (c == EOF)
            break;

        //  For a newline character, output it and reset for new line.
        else if (c == '\n')
        {
            putchar(c);
            CC = 0;
            DC = 0;
        }

        //  For a space, update desired column.
        else if (c == ' ')
            ++DC;

        /*  For a tab, update desired column to next tab stop.  We do this by
            "backing up" as many positions as we are beyond the current tab
            stop and then advancing a full tab width.
        */
        else if (c == '\t')
            DC = DC - DC % TabWidth + TabWidth;

        /*  For any other character, output suitable tabs and spaces and then
            the character.
        */
        else
        {
            /*  Output tabs until we reach the tab stop at or before the
                desired position.
            */
            while (CC/TabWidth < DC/TabWidth)
            {
                putchar('\t');
                CC = CC - CC % TabWidth + TabWidth;
            }

            //  Output spaces until we reach the desired position.
            while (CC < DC)
            {
                putchar(' ');
                ++CC;
            }

            //  Output the character.
            putchar(c);
            ++CC;
        }
    }
}
ttp71kqs

ttp71kqs3#

可以不使用数组。entab()只是一个输入过滤器,你可以不使用数组:所有重要的是制表位的大小和在输出上的位置。如果你需要entab()一个字符串,那么你可以使用两个指针。

正如我在上面的评论中所写的,制表位是行中的位置。最好把它们看作列编号。如果制表符宽度为4,第一列为0,则制表位位于第4,8,12,16,20列......。如果一个字符串是X -a.k.a. C表示法中的x1m2 n1 X-X将显示在第4列。如果你使用空格,并希望在第10列中有一个X,那么字符串当然是" X",并使用11个字节。您在entab中的使命是将这样一个字符串转换为"\t\t X",它只使用5个字节,并在屏幕上显示相同的内容。

C示例

假设我们有

int entab(
    const char* file_in, const char* file_out,
    const int tab);

而且使大家

int detab(
    const char* file_in, const char* file_out,
    const int tab);

因此,如果我们将entab的输出馈送到detab:,则预计会获得与entab的原始文件相同的文件。

main.c用于此类测试

int main(void)
{
    const char* in     = "original20.c";
    const char* interm = "tab.txt";
    const char* out    = "out.c";

    const int tab_s = 4;

    int status = entab(in, interm, tab_s);
    printf(
        "\tentab(\"%s\",\"%s\",%d) returned %d\n", in,
        interm, tab_s, status);

    status = detab(interm, out, tab_s);
    printf(
        "\tdetab(\"%s\",\"%s\",%d) returned %d\n", interm,
        out, tab_s, status);

    return 0;
}

original20.c示例

我将使用作者发布的原始代码的前20行:

#include <stdio.h>
#include <stdlib.h>
#define MAX_L                                              \
    1000  // max length of array is 999. -> [1000] is \0
#define TAB_W 4  // width of atab

void entab(void)
{
    char string[MAX_L];

    char c;
    int  i = 0;
    // get string
    while ((c = getchar()) != '\n')
    {
        string[i] = c;
        if (string[i] == '\n') { string[i + 1] = '\0'; }
        else { string[i] = c; }
        ++i;
    }

测试输出

entab("original20.c","tab.txt",4) returned 0
       detab("tab.txt","out.c",4) returned 0
PS C:\SO-EN> cmd /c fc original20.c out.c
Comparing files original20.c and OUT.C
FC: no differences encountered

tab.txtentab的输出

原始大小为475字节。tab.txt有395个。

#include <stdio.h>
#include <stdlib.h>
#define MAX_L                                              \
    1000  // max length of array is 999. -> [1000] is \0
#define TAB_W 4  // width of atab

void entab(void)
{
    char string[MAX_L];

    char c;
    int  i = 0;
    // get string
    while ((c = getchar()) != '\n')
    {
        string[i] = c;
        if (string[i] == '\n') { string[i + 1] = '\0'; }
        else { string[i] = c; }
        ++i;
    }

完整的C代码

#define ENTER '\n'
#define TAB '\t'
#define SPACE 0x20
#include <stdio.h>
#include <stdlib.h>

int detab(
    const char* file_in, const char* file_out,
    const int tab);
int entab(
    const char* file_in, const char* file_out,
    const int tab);

int flush(char* const, size_t, FILE*);

int main(void)
{
    const char* in     = "original20.c";
    const char* interm = "tab.txt";
    const char* out    = "out.c";

    const int tab_s = 4;

    int status = entab(in, interm, tab_s);
    printf(
        "\tentab(\"%s\",\"%s\",%d) returned %d\n", in,
        interm, tab_s, status);

    status = detab(interm, out, tab_s);
    printf(
        "\tdetab(\"%s\",\"%s\",%d) returned %d\n", interm,
        out, tab_s, status);

    return 0;
}

int detab(
    const char* file_in, const char* file_out,
    const int tab_s)
{
    int    ch    = 0;
    size_t n_col = 0;
    FILE*  in    = fopen(file_in, "r");
    if (in == NULL) return -1;
    FILE* out = fopen(file_out, "w");
    if (in == NULL) return -2;

    while ((ch = fgetc(in)) >= 0)
    {
        switch (ch)
        {
            case TAB:
                for (int i = 0; i < tab_s; i += 1)
                {   // up to tab_s spaces
                    fprintf(out, " ");
                    n_col += 1;
                    if (n_col % tab_s == 0) break;
                }
                break;
            case ENTER:
                fprintf(out, "%c", ch);
                n_col = 0;
                break;
            default:
                fprintf(out, "%c", ch);
                n_col += 1;
                break;
        }
    }
    fclose(in);
    fclose(out);
    return 0;
}

int entab(
    const char* file_in, const char* file_out,
    const int tab_s)
{
    int    ch       = 0;
    size_t n_col    = 0;
    char   n_spaces = 0;

    if (tab_s < 2) return -1;
    FILE* in = fopen(file_in, "r");
    if (in == NULL) return -2;
    FILE* out = fopen(file_out, "w");
    if (in == NULL) return -3;
    char* buf = malloc(tab_s);
    if (buf == NULL) return -4;
    size_t ix = 0;

    while ((ch = fgetc(in)) >= 0)
    {
        switch (ch)
        {
            case ENTER:
                if (ix == 0)
                {
                    fprintf(out, "\n");
                    break;
                };
                // not empty
                flush(buf, ix, out);
                ix = 0;
                fprintf(out, "\n");
                break;
            default:
                *(buf + ix) = ch;
                ix += 1;
                if (ix == tab_s)
                {
                    flush(buf, ix, out);
                    ix = 0;
                }
                break;
        }
    };
    if (ix > 0)
    {
        flush(buf, ix, out);
        fprintf(out, "\n");
    }
    fclose(in);
    fclose(out);
    free(buf);
    return 0;
}

int flush(char* const buf, size_t ix, FILE* out)
{
    if (out == NULL) return -1;
    if (buf == NULL) return -2;
    char* rr = buf + ix - 1;
    for (char* p = rr ;p >= buf; p-=1)
    {
        if (*p != SPACE) break;
        *p = TAB;
        rr = p;
    };
    for (char*p = buf; p<=rr; p += 1)
        fprintf(out, "%c", *p);
    return 0;
}

这个例子使用了一个制表位大小的小数组,没有特殊的原因。请注意,这里的性能并不是一个重要的问题,因为我们使用的是文件I/O,这本身就是一件很慢的事情。

3npbholx

3npbholx4#

1.要到达制表位,制表符可能是必要的,这怎么可能呢(cit.当一个拉环或一个空白就足以到达一个停止拉环时,哪一个会被优先考虑?)?我的意思是要达到它你需要一个空间,所以你不能偏好一个或另一个,它是两种不同的情况。还是不去?
假设制表符宽度为4,当在第3列中时,打印空格或制表符具有相同的结果:到达柱4。由于空格和制表符只是一个字符,为了减少打印的字符数,打印其中一个并不重要。
1.我想不可能直接在第一个while循环中执行而不将其存储在数组中?是吗
可以在一个循环中用制表符替换空格,而不使用数组。代码如下:

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

#define TAB_WIDTH 4
#define TAB_OR_SPACE '\t'

static void endtab(void) {
    char c;
    int nb_spaces;
    int column;

    column = 0;
    nb_spaces = 0;
    while ((c = getchar()) != EOF) {
        if (c == ' ') {
            column++;
            nb_spaces++;
            if (column % TAB_WIDTH == 0) {
                // A tab position is reached, so a tab can replace some spaces
                if (nb_spaces == 1) {
                    // Tab or space ? both works
                    c = TAB_OR_SPACE;
                } else {
                    c = '\t';
                }
                nb_spaces = 0;
            } else {
                // We could use a simple "continue" here,
                // but I find more elegant this way to skip the "putchar" to be consistant with the use of "if/else if" pattern
                c = 0;
            }
        } else if (c == '\t') {
            column = 0; // We don't need the exact column, we just need to be aligned on a tab stop
            nb_spaces = 0;
        } else {
            while (nb_spaces > 0) {
                putchar(' ');
                nb_spaces--;
            }
            if (c == '\r' || c == '\n') {
                column = 0;
            } else {
                column++;
            }
        }
        if (c != 0) {
            putchar(c);
        }
    }
    // Don't forget remaining spaces
    while (nb_spaces > 0) {
        putchar(' ');
        nb_spaces--;
    }
    putchar('\n');
}

int main(int argc, char **argv) {
    endtab();
    return EXIT_SUCCESS;
}

更正:行和多行字符串末尾可能缺少一些空格。

相关问题