转载:“去除多余WS”的代码实例分析

11月25日 · 2016年

转载

原文标题:《20161116 – 一次DEBUG的小结》

原文链接:http://www.starrylyc.com/wordpress/?p=77

原文作者:Summerfirefly

转载授权:

sp161125_010012


//本程序目的为去除一段文字开头和每行末尾的Whitespace
//以及单词之间多余的whitespace,仅保留第一个whitespace
//ECNU OJ Problem 3167
#include <stdio.h>
int main()
{
    int i,n;
    char a[1000],x;
    i=0;
    scanf("%c",&x);
    for (;x==' '||x=='\t'||x=='\n';)
        scanf ("%c",&x);
    a[i]=x;
    i++;
    while (scanf("%c",&x)!=EOF)
    {
        if ((x==' '||x=='\t'||x=='\n')&&a[i-1]!=' '&&a[i-1]!='\t'&&a[i-1]!='\n')
        {
            a[i]=x;
            i++;
        }
        if(x!=' '&&x!='\t'&&x!='\n')
        {
            a[i]=x;
            i++;
        }
    }
    if(a[i-1]==' '||a[i-1]=='\t'||a[i-1]=='\n')
    {
        i=i-1;
        a[i]='\0';
    }
    else
        a[i]='\0';
    for (n=0;n<i;n++)
        printf ("%c",a[n]);
    return 0;
}

以上为一位同学于20161116晚发来的一段代码,EOJ测试Wrong Answer,本地测试通过(注:这里的本地测试就是本次总结的重点之一)无法理解便交给我,整体debug用时一个多小时(注:总结重点之二,debug的方式要注意)

拿到代码后第一步在Code::Blocks下进行debug测试,用单行数据进行测试,完全无误(事后证明确实无误),用文件重定向在终端模拟OJ的文件测试多行数据,无误,但事后证明这里的测试出现严重的失误,对于可能的输入方式没有考虑完全,导致浪费大量时间

经过长时间的不全面的测试数据进行测试后想到存在如下情况:

Input:

\t\taaaaa     aaa\t      \t\n

aa        a\n

EOF

即多行数据,且非末行的末尾换行符之前连接一串whitespace,上述错误的代码输出结果为

Output:

aaaaa aaa\taa a\n

显然不正确,本应输出两行,最终只有一行,问题出在Line 19的判断条件上,由于本算法是一次性处理整个文件,而又没有考虑到连续多个空白字符之后紧跟换行符的情况,导致\t\t\n这类型序列只存入了\t而丢弃了\n,导致行的缺失,debug时同样没有注意到这一点,白白浪费大量时间

总结:

  1. 本地测试时一定要将所有输入的可能性考虑完全

  2. 考虑可能的输入时一定要结合自己的算法的特点,如本例,由于我自己写的算法是逐行处理,将整个文件作为类似二维的处理,所有换行符都在一行的末尾,因此不存在丢失应有的换行符的问题,但上述代码为整体处理,类似在一维上处理多行文本,换行符不一定在数组末尾。如果仍然按照单行处理的思路,即两个单词之间的whitespace只保留第一个,来一次性处理多行文本,就会有丢失换行符的可能性。

Debug是一项非常重要的技能!非常重要!同时Debug的时候一定要注意方法和以前的经验教训,否则将会浪费大量时间。

全面的测试数据将为程序的调试节省大量时间

2016-11-17 Summerfirefly

2 条回应
  1. 钱 栋炜2016-12-8 · 15:21

    这题难主要是因为末尾就算有whitespace输出也看不到,所以不如假定n t ‘ ‘是实在的abc,然后输出会比较容易的看出程序问题

    • jxtxzzw2016-12-8 · 17:25

      对。所以对于字符串的输出,一种常见的做法是“printf(“#BEGIN#%s#END#”,s);”,这样就会输出形如#BEGIN#Hello,World#END#的结果,一方面可以看到所输出字符串的开始和结束,另一方面便于观察WS。