c - C 中每第 n 列(K&R 1-22)折叠输入行

标签 c arrays text-processing kernighan-and-ritchie

Write a program to "fold" long input lines into two or more shorter lines after the last non-blank character that occurs before the n-th column of input. Make sure your program does something intelligent with very long lines, and if there are no blanks or tabs before the specified column.

我决定遵循的算法如下:

  1. 如果输入行的长度 < maxcol(必须折叠的列),则按原样打印该行。
  2. 如果没有,从 maxcol 中,我检查它的左边,右边找到最接近的非空格字符,并将它们保存为“第一个”和“最后一个”。然后,我打印从 line[0] 到 line[first] 的字符数组,然后打印数组的其余部分,从 line[last] 到 line[len] 成为新的行数组。

这是我的代码:

#include <stdio.h>

#define MAXCOL 5

int getline1(char line[]);

int main()
{
    char line[1000];
    int len, i, j, first, last;

    len = getline1(line);

    while (len > 0) {
        if (len < MAXCOL) {
            printf("%s\n", line);
            break;
        }
        else {
            for (i = MAXCOL - 1; i >= 0; i--) {
                if (line[i] != ' ') {
                    first = i; 
                    break;
                }
            }
            for (j = MAXCOL - 1; j <= len; j++) {
                if (line[j] != ' ') {
                    last = j; 
                    break;
                }
            }
            //printf("first %d last %d\n", first, last);
            for (i = 0; i <= first; i++) 
                putchar(line[i]);
            putchar('\n');
            for (i = 0; i < len - last; i++) {
                line[i] = line[last + i];
            }
            len -= last;
            first = last = 0;
        }
    }
    return 0;
}

int getline1(char line[])
{
    int c, i = 0;

    while ((c = getchar()) != EOF && c != '\n') 
        line[i++] = c;

    if (c == '\n')
        line[i++] = '\n';

    line[i] = '\0';

    return i;
}

问题如下:

  • 它不会对很长的行执行智能操作(这很好,因为我可以将其添加为边缘情况)。
  • 它不会对选项卡执行任何操作。
  • 我无法理解部分输出。

例如,输入:

asd        de             def          deffff

我得到输出:

asd
de
def
defff //Expected until here
//Unexpected lines below
ff
fff
      deffff
        deffff
    deffff

问题 1 - 为什么会打印出意外的行?如何使我的程序/算法更好?

最终,在花了相当长的时间解决这个问题后,我放弃了并决定检查 clc-wiki寻求解决方案。这里的每个程序都工作,除了一个(其他程序没有工作,因为它们没有涵盖某些边缘情况)。有效的是最大的一个,它对我来说没有任何意义。它没有任何注释,我也无法正确理解变量名称及其代表的含义。但这是 wiki 中唯一有效的程序。

#include <stdio.h>

#define YES 1
#define NO 0

int main(void)
{
  int TCOL = 8, ch, co[3], i, COL = 19, tabs[COL - 1];
  char bls[COL - 1], bonly = YES;

  co[0] = co[1] = co[2] = 0;

  while ((ch = getchar()) != EOF)
  {
      if (ch != '\t') {
          ++co[0];
          ++co[2];
      }

      else {
          co[0] = co[0] + (TCOL * (1 + (co[2] / TCOL)) - co[2]);
          i = co[2];
          co[2] = TCOL + (co[2] / TCOL) * TCOL;
      }

      if (ch != '\n' && ch != ' ' && ch != '\t')
      {
          if (co[0] >= COL) {
              putchar('\n');

              co[0] = 1;
              co[1] = 0;
          }

          else
              for (i = co[1]; co[1] > 0; --co[1])
              {
                  if (bls[i - co[1]] == ' ')
                      putchar(bls[i - co[1]]);

                  else
                      for (; tabs[i - co[1]] != 0;)

                          if (tabs[i - co[1]] > 0) {
                              putchar(' ');
                              --tabs[i - co[1]];
                          }

                          else {
                              tabs[i - co[1]] = 0;
                              putchar(bls[i - co[1]]);
                          }
              }

          putchar(ch);

          if (bonly == YES)
              bonly = NO;
      }

      else if (ch != '\n')
      {
          if (co[0] >= COL)
          {
              if (bonly == NO) {
                  putchar('\n');

                  bonly = YES;
              }

              co[0] = co[1] = 0;
          }

          else if (bonly == NO) {
              bls[co[1]] = ch;

              if (ch == '\t') {

                  if (TCOL * (1 + ((co[0] - (co[2] - i)) / TCOL)) -
                    (co[0] - (co[2] - i)) == co[2] - i)
                      tabs[co[1]] = -1;

                  else
                      tabs[co[1]] = co[2] - i;
              }

              ++co[1];
          }

          else
              co[0] = co[1] = 0;
      }

      else {
          putchar(ch);

          if (bonly == NO)
              bonly = YES;

          co[0] = co[1] = co[2] = 0;
      }
  }

  return 0;
}

问题 2 - 您能帮我理解这段代码及其工作原理吗?

它解决了我的解决方案的所有问题,并且还通过逐个字符读取来工作,因此看起来更有效。

最佳答案

Question 1 - Why do the unexpected lines print? How do I make my program/algorithm better?

您在输出中得到了意外的行,因为在打印数组后,您没有以空字符 \0 终止新的 line 数组 -

在这里,您将从 last 开始复制字符,直到 len - last,创建一个新的 line 数组:

        for (i = 0; i < len - last; i++) {
            line[i] = line[last + i];
        }

您已复制字符,但空终止字符仍位于其原始位置。假设输入字符串是:

asd        de             def          deffff

因此,最初 line 数组的内容将是:

"asd        de             def          deffff\n"
                                                ^
                                                |
                                  null character is here

现在打印 asd 后,您将从 linelast 索引复制字符到 len - last 索引到从 0 索引开始的 line 数组本身。因此,复制 line 数组的内容后将是:

"de             def          deffff\n    deffff\n"
                                     |____  _____|
                                          \/
                                This is causing the unexpected output 
                            (null character is still at the previous location)

因此,在 for 循环之后,您应该在复制的最后一个字符之后添加空字符,如下所示:

line [len - last] = '\0';

这样,将在 while 循环的下一次迭代中处理的 line 数组的内容将是:

"de             def          deffff\n"

还有一件事,在 line 数组中,您可以在末尾看到 \n (换行符)字符。也许你想在处理输入之前删除它,你可以这样做:

line[strcspn(line, "\n")] = 0;

您可以在程序中进行的改进:
1. 您可以做的一项非常明显的改进是在处理输入字符串时使用指向输入字符串的指针。在指针的帮助下,您不需要将数组的其余部分(除了已处理的部分)再次复制到同一个数组,直到程序处理整个输入。将指针初始化为输入字符串的开头,并且在每次迭代中只需将指针移动到适当的位置并从指针指向的位置开始处理。
2. 由于您首先将整个输入放入缓冲区,然后对其进行处理。您可以考虑使用 fgets() 来获取输入。它将更好地控制用户的输入。
3. 添加对 line 数组溢出的检查,以防输入很长。使用fgets(),您可以指定要从输入流复制到line数组的最大字符数。

Question 2 - Can you help me make sense of this code and how it works?

该程序非常简单,尝试至少自己理解一次。使用调试器或拿笔和纸,针对小尺寸输入试运行一次并检查输出。增加输入大小并添加一些变体,例如多个空格字符,并检查程序代码路径和输出。这样你就很容易理解了。

关于c - C 中每第 n 列(K&R 1-22)折叠输入行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51435943/

相关文章:

javascript - 有没有更有效的方法来对对象中的数组中的数组进行排序?

javascript - 在 angularjs 数组中使用切片

powershell - 如何使用不区分大小写的匹配替换 PowerShell 中的字符串,同时保留输入大小写

mysql - SQL : should I insert another column or parse every single row

c - numactl -- 硬件显示不正确的信息

c - 如果未设置规范模式,非阻塞读取会失败吗?

javascript - 推送后未定义的数组

Python:从字符串中删除重复字符的最佳方法

do-while 中的条件为 ||与&&

c - "__attribute__((packed, aligned(4))) "是什么意思