切换字母(无字符串或字符串功能)

时间:2018-10-30 08:28:09

标签: c arrays text

我希望每次在文本中将“猫”一词转换为“狗”。 我不能使用字符串或字符串函数。

我的代码:

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

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

输出:

pinnedData

4 个答案:

答案 0 :(得分:5)

这里没有什么问题。

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

答案 1 :(得分:2)

此问题非常适合finite-state machine

这是有限状态机如何完成这项工作的流程图: Example finite state machine flowchart

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

作为另一个示例,请考虑是否尝试匹配cocoa,而我们的输入是cococoa。在第五个字符处,我们读c而不是a(已经匹配coco,但尚未输出任何内容),因此我们需要输出co,然后返回第二个c的匹配位置。

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

不过,这种特殊情况很容易手动实现。我们要做的是将第一个状态(“获取下一个字符”)放入循环外,执行只要“ 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;
}

有限状态机的问题在于代码往往是只写的。要了解代码或在任何时间范围内对其进行验证或维护,您确实需要定义有限状态机,以便可以将实现的代码与有限状态机进行比较。

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

使用精心设计,极其紧密和高效的代码来解决问题,如果没有办法修改或修复代码中的错误,则一文不值。 (即使世界上最优秀的程序员也会在各种复杂程度上犯错误和错误。如果有人声称自己不这样做,那就是在撒谎。)

我们可以通过在上面的代码中插入注释来解释有限状态机,从而对它进行记录。那就好了;注释应始终说明程序员的意图,一段代码旨在实现的目的或任务。取而代之的是,我们经常写注释来告诉代码做什么,而这没什么用处,因为我们可以很容易地阅读代码以查看代码的作用。我们不知道的是,代码是否符合程序员的意图,或者程序员的意图是否是对潜在问题的有效解决方案!

另一种可能性是包括该图,也许为动作(椭圆形)和测试(平行四边形)编号,并在引用该图的代码中添加注释。这比较容易,但遵循起来却不那么容易(因为您需要不断交叉引用代码和图)。

可悲的是,通常会忽略文档部分(“等更多时间我会再做” ),只需验证代码是否正确即可。输入。通常不可能对所有可能的输入都完全测试代码(尽管这很简单),因此发现了许多错误。没有文档,人就可以检查代码并尝试评估它在逻辑上是否正确(即,“流程图”或它实现的有限状态机是否正确),以及是否正确该代码是否与工作模型匹配,在实践中只有被它们咬住才能发现错误。真讨厌。

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

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

答案 2 :(得分:0)

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

答案 3 :(得分:-2)

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

input[i + 2]


在您的情况下:

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