【问题标题】:Switching letters (no strings or strings functions)切换字母(无字符串或字符串函数)
【发布时间】:2018-10-30 08:28:09
【问题描述】:

每次出现在文本中时,我都想将“cat”这个词切换为“dog”。 我不能使用字符串或字符串函数。

我的代码:

#include <stdio.h>

int main()
{   
    int i; // loop counter 
    int size; // size of arry  
    int input[20];
    printf("enter text here\n");  

    while((input[i] = getchar()) != '\n') // input text to the arry
    {
        if(input[i]=='c' && input[i+1]=='a' && input[i+2]=='t') // switching characters
        {
            input[i]='d'; input[i+1]='o'; input[i+2]='g'; 
        }

        i++; 
        size++;  
    }

    i=0; // reset for next loop

    while(i <= size) // printing the text out ofthe arry 
    {
         putchar(input[i]); 
         i++;
    }

    printf("\n"); 
    return 0;
}

输出:

enter text here                                                                                                                                  
cat                                                                                                                                              
cat                                                                                                                                              
ȵm�� $$ŵ��p��$���Zտ ��$��M��v���������������������      ������������!��d@8                                                                    $  

�                                                                                                                                                
�����������5_Segmentation fault

【问题讨论】:

  • 您正在使用未初始化的变量。打开编译器警告!
  • 您在第二次循环之前将i 重置为零,但您一开始就没有设置它。你应该初始化i。您还试图展望未来:您针对 input[i + 1]input[i + 2] 进行测试——您尚未读取的值。
  • @danglingpointer 但它不工作

标签: c arrays text


【解决方案1】:

这里的问题很少。

  1. 未初始化的局部变量。

    int i = 0; // loop counter int size = 0; // size of array

  2. 您正在检查尚未读取的 at 字符。

    因此检查当前输入字符中的t是否匹配,然后检查ac 在以前输入的字符中,如下所示。

    if(i>=2 && input[i-2]=='c' && input[i-1]=='a' && input[i]=='t') // switching characters
    {
        input[i]='g'; input[i-1]='o'; input[i-2]='d';
    }
    

【讨论】:

    【解决方案2】:

    这个问题与finite-state machine 完美匹配。

    这是有限状态机如何完成这项工作的流程图:

    令人惊讶的是,不匹配的情况会返回检查最新字符是否匹配“C”,而不是仅仅打印它并获取一个新字符。这样做的原因是,如果我们读到 ccatcacat,我们想检查我们是否仍然匹配(较短的)前缀,在本例中为 c

    作为一个不同的例子,考虑我们是否尝试匹配cocoa,而我们的输入是cococoa。在第五个字符处,我们读取c 而不是a(已经匹配coco,但还没有输出任何东西),所以我们需要输出co,然后返回匹配第二个@ 987654338@.

    我们人类通常不会手动构建这样的状态机。我们已经有一个用于字符串的,作为库或内置到 POSIX 兼容的 C 库(Linux、Mac、BSD):regular expression matching。如果我们使用基本的 POSIX,那么regcomp() 会根据我们的规范构建一个高效的状态机,regexec() 在上面处理输入数据。

    不过,这种特殊情况很简单,可以手动实现。我们要做的是将第一个状态(“Get next char”)放在循环之外,只要“char = EOF”不正确,就执行一个循环,然后在循环内执行其余的操作。最终的“完成”状态在循环之后。在pseudocode

    ch = Next char
    While ch != EOF:
    
        If ch != 'c':
            Output ch
            ch = Next char
            Continue
        End If
    
        # The second "Get next char" in the flowchart:
        ch = Next char
    
        If ch == EOF:
            Output 'c'
            Break
        Else
        If ch != 'a':
            Output 'c'
            Continue
        End If
    
        # The third "Get next char" in the flowchart
        ch = Next char
    
        If ch == EOF:
            Output 'c'
            Output 'a'
            Break
        Else
        If ch != 't':
            Output 'c'
            Output 'a'
            Continue
        End If
    
        # 'c' 'a' 't' matched (with ch == 't').
        Output 'd'
        Output 'o'
        Output 'g'
    
        ch = Next char
    End While
    Done
    

    读取标准输入的 C 程序,将每次出现的 cat 转换为 dog,区分大小写,因此可以写成

    #include <stdlib.h>
    #include <stdio.h>
    
    void cat_to_dog(FILE *in, FILE *out)
    {
        int ch;
    
        ch = fgetc(in);
        while (ch != EOF) {
            if (ch != 'c') {
                fputc(ch, out);
                ch = fgetc(in);
                continue;
            }
    
            ch = fgetc(in);
            if (ch == EOF) {
                fputc('c', out);
                break;
            } else
            if (ch != 'a') {
                fputc('c', out);
                continue;
            }
    
            ch = fgetc(in);
            if (ch == EOF) {
                fputc('c', out);
                fputc('a', out);
                break;
            } else
            if (ch != 't') {
                fputc('c', out);
                fputc('a', out);
                continue;
            }
    
            fputc('d', out);
            fputc('o', out);
            fputc('g', out);
    
            ch = fgetc(in);
        }
    }
    
    int main(void)
    {
        cat_to_dog(stdin, stdout);
        return EXIT_SUCCESS;
    }
    

    有限状态机的问题在于代码往往是只写。要理解代码,或者在任何时间尺度上验证或维护代码,您确实需要定义有限状态机,以便将实现的代码与有限状态机进行比较。

    在这里,我们终于找到了这个“答案”:正确的文档。

    如果无法修改或修复代码中的错误,那么一旦使用精心设计、极其紧凑和高效的代码来解决问题,就毫无价值。 (即使是世界上最优秀的程序员也会犯各种复杂程度的错误和错误。如果有人声称他们没有,那他们就是在撒谎。)

    我们可以通过在上面代码中穿插的 cmets 中解释有限状态机来记录它。那很好; cmets 应该始终解释程序员的意图、一段代码要完成的目的或任务。相反,我们经常编写 cmets 来告诉代码做什么,这不太有用,因为我们可以很容易地阅读代码来查看它的作用。我们不知道的是代码是否符合程序员的意图,或者程序员的意图是否是潜在问题的有效解决方案!

    另一种可能性是包含图表,可能对动作(椭圆)和测试(平行四边形)进行编号,并在引用图表的代码中添加 cmets。这更容易,但不那么容易理解(因为您需要不断交叉引用代码和图表)。

    遗憾的是,省略文档部分是很常见的(“等我有时间再做”),并简单地验证代码是否正确输入。通常不可能完全测试所有可能输入的代码(尽管这个非常简单),所以很多错误都没有被发现。在没有文档的情况下,人类可以检查代码并尝试评估它是否逻辑上正确(即“流程图”或它实现的有限状态机是否正确),以及是否代码与工作模型是否匹配,错误只有在实践中被它们咬才能发现。这很讨厌。

    有限状态机是 cmets(和文档)重要性的一个典型例子,但它确实适用于您编写的所有代码。学习尝试在您的 cmets 中描述您的推理(解决方案模型)和意图(您希望代码完成什么),并从一开始就编写大量 cmets。以后很难养成这个习惯;经过几十年的编程,我个人仍在为此苦苦挣扎。如果后来发现评论是不必要的,则只需不到几分之一秒即可将其删除;但如果它解释了我们人类通常不会注意到的关键怪癖或复杂细节,它可能会在以后节省数小时、数天甚至数周的开发时间。

    这也意味着注释掉未使用代码的做法不是很有用,因为实际的 cmets 很快就会与编译器(或解释器)看到的代码不同。相反,学习使用版本控制系统来管理您的资源;我推荐git。它适用于几乎所有操作系统(请参阅here),您可以在计算机上本地使用它,也可以在分布式项目中使用它,以防您想将代码放在 GitHub 或类似服务上(甚至设置你自己的 git 服务器)。这样你就可以让你的代码和它的 cmets 保持同步;并且在修改代码时,您可以单独描述这些更改(变更集)的原因。掌握了窍门后,你会发现它不是负担,反而会加速你的代码开发!

    【讨论】:

      【解决方案3】:

      Kiran 在他的回答中提到 “您正在检查尚未读取的 a 和 t 字符”。您可以通过使用argcargv 来解决这个问题。

      这是我使用argcargv 编写的程序版本。您会注意到它还会阻止您限制输入缓冲区(即没有 input[20])。

      #include <stdio.h>
      
      int main(int argc, char **argv)
      {
          int i = 0, j = 0;
      
          for(i=1; i<argc; i++)
          {
            while(argv[i][j] != '\0')
            {
              if(argv[i][j] == 'c')
              {
                if(((argv[i][j+1]) == 'a') && (argv[i][j+2] == 't'))
                {
                  argv[i][j] = 'd';
                  argv[i][j+1] = 'o';
                  argv[i][j+2] = 'g';
                }
              }
              printf("%c", argv[i][j]);
              j++;
            }
            j=0;
            printf(" ");
          }
          printf("\n");
          return 0;
      }
      

      示例输入和输出:

      $ ./test my favourite cartoon when i was a kid was catdog
      my favourite cartoon when i was a kid was dogdog 
      
      $ ./test i used to love cats but now i love dogs
      i used to love dogs but now i love dogs 
      

      PS:以防万一你出生太晚或电视上没有看很多卡通片;这是参考:https://en.wikipedia.org/wiki/CatDog

      【讨论】:

        【解决方案4】:

        您正在尝试访问 input[i]input[i + 1]input[i + 2]
        使用:

        while (input[i + 2] &amp;&amp; input[i + 2] != '\n')


        在你的情况下:

        #include <stdio.h>
        
        int main()
        {
            int i = 0; // loop counter
            int size = 0; // size of array  
            int input[20];
            printf("enter text here\n");  
        
            while((input[i] = getchar()) != '\n' && i < 19) // input text to the array
            {
        /*        if(input[i]=='c' && input[i+1]=='a' && input[i+2]=='t') // switching characters
                {
                    input[i]='d'; input[i+1]='o'; input[i+2]='g'; 
                }
        */
        
                i++; 
                size++;  
            }
            input[i] = 0;//ALWAYS null-terminate arrays.
            if (i >= 2);
                while (input[i + 2]) {
                    if (input[i] == 'c' && input[i + 1] == 'a' && input[i + 2] == 't') {
                        input[i] = 'd';
                        input[i + 1] = 'o';
                        input[i + 2] = 'g';
                    }
                }
            }
        
            i=0; // reset for next loop
        
            while(i < size) // printing the text out ofthe arry 
            {
                putchar(input[i]); 
                i++;
            }
        
            printf("\n"); 
            return 0;
        }
        

        【讨论】:

        • 你的回答没有回答真正的问题,即未初始化的变量isize导致UB。
        猜你喜欢
        • 2016-01-28
        • 2018-02-01
        • 2021-10-28
        • 2015-04-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多