【问题标题】:String Manipulation and rot-13字符串操作和 rot-13
【发布时间】:2013-05-16 00:54:04
【问题描述】:

我基本上必须将输入的字符数组转换为使用 rot-13 的结果数组。所以,这就是我正在考虑做的事情:使用 for 循环,然后在 for 循环中使用条件来确定是否添加或减去 13 个位置。但我遇到的问题是编写 for 循环。

这是我必须实现的功能:

    void str_rot_13(char const input[], char result []);

我知道当你编写一个 for 循环时,它看起来像这样:

    for (int i = 0; i < size; i++)

所以我已经编写了我的测试用例,但是编译器无法编译。

#include "string.h"
#include "checkit.h"

void str_rot_13_tests(void)
{
   char input[3] = {'a', 'C', 'd'};
   char result[3] = {'n', 'P', 'q'};

   checkit_string(str_rot_13("aCd", 3), "nPq")
}

int main()
{
   str_rot_13_tests();

   return 0;
}

我不确定我做错了什么,编译器抱怨第 9 行。我认为这与我编写“checkit_string...”的方式有关,但我不太确定。

【问题讨论】:

    标签: c string for-loop rot13


    【解决方案1】:

    size = strlen(input),假设input 是一个以 NUL 结尾的字符串,即。简单地循环直到input[i] == '\0'。由于inputchar*,您可以将其递增到*input == '\0'

    如果它不是以 NUL 结尾的字符串,那么您必须将大小提供给 str_rot_13,否则当它传递给函数时您无法知道数组的长度(因为它会衰减为指针)。

    【讨论】:

    • 谢谢!这真的有帮助!我完全忘记了 NUL。
    • 哦,一个问题。所以在 for 循环中,我会说: for (int i = 0; input[i] != '\0'; i++)?
    • @KarenLee:是的,你可以这样做,或者for(; *input != '\0'; input++),或者while (*input != '\0') { ... input++; }。最好使用size_t 作为数组索引的类型,尽管int 通常就足够了。
    【解决方案2】:
    char char_rot_13(char c){
       char new_c = c;
    
       if (c >= 'a' && c <= 'z')
       {
          new_c = c + 13;
          if(new_c > 'z')
              new_c -= 26;
       }
       else if (c >= 'A' && c <= 'Z')
       {
          new_c = c + 13;
          if(new_c > 'Z')
              new_c -= 26;
       }
       return new_c;
    }
    void str_rot_13(char const input[], char result []){
        while(*input){
            *result++ = char_rot_13(*input++);
        }
        *result ='\0';
    }
    

    测试:

    #include <stdio.h>
    #include <string.h>
    #include <assert.h>
    
    char *str_rot_13(char const input[], char result[]){
        char *p=result;
        while(*input){
            *p++ = char_rot_13(*input++);
        }
        *p ='\0';
        return result;
    }
    int main (void){
        char result[128];
        assert(strcmp(str_rot_13("acd", result), "npq")==0);
        printf("%s", result);
    
      return 0;
    }
    

    【讨论】:

    • 一个问题,当我在编写测试用例并且我想使用“check it_string”时,我会说:checkit_string(str_rot_13("acd", 3), "npq")?我不确定括号中的“3”是否正确。 :(
    • C 中不需要 size 字符串,因为它在结尾处变成了一个 '\0' 的承诺。 (如果是这种情况,有超支的可能)
    • @KarenLee checkit_string(str_rot_13("acd", 3), "npq") char* str_rot_13(const char *, size_t ) :返回时会导致内存泄漏,以确保传递的新字符串区域是不可变的。
    • 代码的第二部分“char*...”是我必须放在 checkit_string... 之后的东西吗?
    • @Karen 没错。我被要求从void checkit_string 更改为char*。按照惯例,函数对字符串的操作会返回一个char*作为其他操作后的含义。
    【解决方案3】:

    这是一个在 C 中循环字符串的简单示例。

    #include <stdio.h>
    
    int length(char const *s)
    {
        int i;
    
        for (i = 0; s[i] != '\0'; ++i)
        {
            /* loop body is empty */
        }
    
        return i;
    }
    
    int main(int argc, char *argv[])
    {
        char const test[] = "hello";
    
        printf("String: '%s'  length: %d\n", test, length(test));
    }
    

    只看for 循环。分为三个部分:初始化、测试和步骤。然后有一个语句,或者一个“块”(一组零个或多个用花括号括起来的语句)。在我的示例中,该块是空的(除了不执行的人类可读注释)。

    初始化执行一次,应该用于以某种方式设置循环;在我的示例中,它用于将i 设置为零。 “测试”是在循环体执行之前评估的一些表达式;如果测试结果为假,则循环终止,因此在循环执行任何操作之前为假的条件将导致循环体永远不会被执行。如果测试结果为真,则执行循环体,然后执行“步骤”; “步骤”应该以某种方式推进循环。在我的示例中,测试是检查循环是否已经找到终止 NUL 字节,并且该步骤会增加循环计数器 i

    所以想想这个循环是如何工作的。我们从零开始i,然后循环立即检查字符串中的第一个字符是否为 NUL 字节。如果是,则循环已经结束,我们的length() 函数返回 0,这是正确的!如果字符串的第一个字节是终止 NUL 字节,那么这是一个“空字符串”并且正确的长度为 0。(在编写循环时考虑如果循环什么都不做会发生什么是很重要的。循环应该“什么都不做" 正确;这个可以。)

    关于 C 的 for 循环的一个有趣之处:循环的所有部分都是可选的。以下是循环的一些替代版本;这些都会起作用。

    i = 0;  /* initialize i before loop */
    for (; s[i] != '\0'; ++i)
        ;  /* looks weird but this is a statement that does nothing */
    

    本例中,我们在循环外初始化i,初始化部分留空。带有单个分号的空语句不常见但合法。更多时候,你会看到有人这样写:NULL; NULL 被求值,但值没有保存在任何地方,所以这也是一个无所事事的声明。

    for (i = 0; s[i] != '\0';)
    {
        ++i;  /* do the step part as the loop body */
    }
    

    在这个例子中,在测试之后,循环体被运行;这增加了i。那么循环的“step”部分就省略了。

    i = 0;  /* initialization */
    for (;;)
    {
        if (s[i] == '\0')  /* test */
            break;
        ++i;  /* step */
    }
    

    在这个例子中,for 循环的所有三个部分都被省略了,这是合法的,基本上意味着“永远循环,直到有东西停止循环”。然后在if 语句中,如果我们看到NUL 字节,我们执行break 语句,终止循环。最后我们增加i

    请注意,初始化、测试和步骤实际上是存在的;他们只是不在for 循环行中。对于像这样的简单循环,我推荐标准形式,而不是这种奇怪的形式。

    最后,有些人会编写一个棘手的循环来增加字符指针本身,而不是增加像i 这样的循环变量。这是一个例子:

    int length(char const *s)
    {
        char const *start;
    
        for (start = s; *s != '\0'; ++s)
        {
        }
    
        return (s - start);
    }
    

    在这个例子中,我们增加了变量s 本身。由于它是作为参数传递的,因此函数有自己的副本,它可以修改该副本而不影响函数之外的任何其他内容。这将保存初始指针的副本,递增直到找到终止 NUL,然后从新位置减去起始位置以找到长度。

    通常人们会缩短循环。如果测试表达式不为零,则测试为真,并且在字符串中,只有 NUL 字节为零。所以测试表达式可以简单地为*s,如果当前位置不是NUL字节,它将评估为真:

    int length(char const *s)
    {
        char const *start;
    
        for (start = s; *s; ++s)
        {
        }
    
        return (s - start);
    }
    

    最后,我们可以使用while 循环来缩短它:

    int length(char const *s)
    {
        char const *start = s;
    
        while (*s)
            ++s;
    
        return (s - start);
    }
    

    它简短而简洁,但是一旦你习惯了这些东西就会很清楚。

    【讨论】:

    • 天啊,非常感谢!你不知道这有多大帮助!谢谢!
    • 不客气! :-) 我希望你在学习 C 时会有很多乐趣。
    • 谢谢!我希望我做得好!没有编程经验的艺术背景很难做到这一点,哈哈。 :)
    最近更新 更多