【问题标题】:Replace every word in text with the word shifted to right 'k' times将文本中的每个单词替换为向右移动“k”次的单词
【发布时间】:2015-12-12 18:35:51
【问题描述】:

我尝试制作一个函数,将文本中的每个单词替换为向右移动“k”次的单词。 代码如下所示:

void operation_3(char *string, int k){
    int len = 0, i;
    int string_len = strlen(string);
    char *word;
    char s[12] = " .,?!\"'";
    char *dup;
    dup = strdup(string);
    word = strtok(dup, s);
    while (word != NULL) {
            len = strlen(word);
            char *new_word = (char *)malloc(len * sizeof(char));
            for (i = 0; i < k; i++) {
                    new_word = shift_to_right(word);
            }
            string = replace_word(string, word, new_word);
            word = strtok(NULL, s);
    }
 }

shift_to_right 是:

char *shift_to_right(char *string){
    char temp;
    int len = strlen(string) - 1;
    int i;
    for (i = len - 1; i >= 0; i--) {
            temp = string[i+1];
            string[i+1] = string[i];
            string[i] = temp;

    }
    return string;
}

replace_word 是:

char *replace_word(char *string, char *word, char *new_word) {
    int len = strlen(string) + 1;
    char *temp = malloc(len * sizeof(char));
    int temp_len = 0;
    char *found;
    while (found = strstr(string, word)) {
            if (strlen(found) != strlen(word) || isDelimitator(*(found - 1)) == 1) {
                    break;
            }
            memcpy(temp + temp_len, string, found - string);

            temp_len = temp_len + found - string;

            string = found + strlen(word)

            len = len - strlen(word) + strlen(new_word);
            temp = realloc(temp, len * sizeof(char));

            memcpy(temp + temp_len, new_word, strlen(new_word));

            temp_len = temp_len + strlen(new_word);
    }

    strcpy(temp + temp_len, string);

return temp;
 }

而isDelimitator是:

int isDelimitator(char c) {
    if(c == ' ' || c == '.' || c == ',' || c == '?' || c == '!' ||
       c == '"' || c == '\0' || c == '\'') {
            return 0;
    }
    else return 1;
 }

我测试了 shift_to_right、replace_word 和 isDelimitator 并且工作正常。但是最后一个函数 operation_3 没有按预期工作。例如,对于输入:“Hi I am John”,对于 k = 1,输出是:“Hi I am John”。基本上 operation_3 不会修改字符串。有什么建议,请指正?

【问题讨论】:

  • 您能否举一个“单词向右移动 'k' 次”的示例,您发布的示例没有告诉我们任何信息,因为输出显然与输入相同。
  • 抱歉,我说的不是很准确。对于 word = abcd 和 k = 1,输出应该是 dabc。所以对于:“Hi I am John”和 k = 1 应该是:“iH I manJoh”
  • 在您的replace_word 中,您有一个if 条件strlen(found) != strlen(word) 我认为strstrstring 中返回一个指针,从那里子字符串匹配,在这种情况下strlen(found) != strlen(word)总是正确的,除非这个词是 string 中的最后一个词,所以你在大多数代码中没有做任何事情就打破了循环。
  • 如果发现这样的内容,我尝试设置停止条件:当我只需要约翰时,约翰。 Strstr 查找所有子字符串。你有什么解决办法吗?

标签: c string function pointers replace


【解决方案1】:

我看到有几件事可能是错误的原因。

1)operation_3 中你这样做:new_word = shift_to_right(word); 并且,在char *shift_to_right(char *string) 的定义中你修改string 本身并返回一个指向它的指针。所以,如果你调用了shift_to_right(word)word = "Hi",那么在执行shift_to_right 之后,wordnew_word 现在都指向同一个字符串"iH",所以在replace_word 中,当你传递这两个词时并检查子字符串word,你总是会得到NULL,因为没有子字符串"iH"

一个可能的解决方案,在shift_to_right添加声明,

char *new_string = strdup(string);

而不是交换string 中的字符,而是交换new_string 中的字符并从函数返回new_string

您的代码应如下所示::

char *shift_to_right(char *string){
    char temp;
    int len = strlen(string) - 1;
    char *new_string = strdup(string);
    int i;
    for (i = len - 1; i >= 0; i--) {
        temp = new_string[i+1];
        new_string[i+1] = new_string[i];
        new_string[i] = temp; 
    }
    return new_string;
}

2) 在函数replace_word 中,暂时让我们考虑一下没有发生上述错误,并使用参数:: replace_word(string, "Hi", "iH"); 调用replace_word。 因此,当您执行found = strstr(string, word) 时,它会为您提供指向Hi 开始的第一个字母的指针。所以,在这种情况下,如果你的字符串是"Hi I am John",那么你会得到一个指向第一个H的指针,当你执行strlen(found)时,你会得到12(从指针开始的字符串长度)为输出,而strlen(word) 将总是更少(除非found 指向字符串中的最后一个单词),因此在大多数情况下,您的if 条件为真,并且您从循环中中断,没有任何交换。

此外,正如您自己在 cmets 中指出的那样,如果您想要一个子字符串 Johnstrstr 也将返回 Johns,唯一的解决方案是运行一个循环并在 string 中检查它在John之后如果有分隔符,如果没有分隔符,那么这不是你需要的子串。

replace_word 应该看起来像这样 ::

void replace_word(char *string, char *word, char *new_word) {
    char *found = strstr(string, word);
    int len = strlen(word);
    while(found) {
        char temp = *(found + len);
        if(isDelimeter(temp) == 0) {
            break;
        } else {
            found = strstr(found + len + 1);
        }
    }
    if(found != NULL) {
        for(int i = 0; i < len; i++) {
            *(found + i) = new_word[i]; // *(found + i) is accessing the i^th, character in string from the pointer found
        }
    }
}

我觉得这个replace_word应该可以了,你可以直接修改string,不需要实际创建一个temp字符串并返回。这减少了分配新内存和保存该指针的需要。

我希望这会有所帮助!

EDIT :: 由于我们一直在代码中使用strdup,它会动态分配字符串大小的内存,并为\0 字符提供额外的块,所以我们要小心明确地释放它,所以根据我的说法,在我们退出函数之前释放replace_word中分配的内存是个好主意,因为new_word在它之后是无用的。

另外,我在你的代码中看到了一条语句::

1) char *new_word = (char *)malloc(len * sizeof(char));

就在你开始转换单词之前,我希望你明白你不需要这样做。 new_word 只是一个指针,因为我们现在在 strdup 中为它分配了内存,所以我们不需要这样做。甚至在之前,考虑到您编写的代码,没有理由将内存分配给new_word,因为您正在返回数组的地址,该地址已经在堆栈中,并且会一直保留在堆栈中直到执行结束程序。

【讨论】:

  • @RobertD 哈哈,我很高兴,这会有所帮助。:D
  • 良好而仔细的解释。可以肯定的是,我希望看到的唯一注释是关于strdup 分配的内存的注释。必须保留指向每个新内存块的起始地址的指针,以便在不再需要时可以将其释放。我没有深入修改过的代码,但值得注意的是确保内存不会从裂缝中掉下来。您的添加中没有丢失起始地址,但我在其余代码中也没有看到任何free
  • @DavidC.Rankin True.. 我现在就添加它们!
  • 我知道我忘了释放内存。我将在代码中再次查看并释放它应该在的内存。
【解决方案2】:

此代码比您拥有的代码更简单,它会打印输入字符串中的所有单词分隔符。它不是查找特定的标点符号,而是检查字母数字。

#include <stdio.h>
#include <string.h>
#include <ctype.h>

int main(void)
{
    char instr[] = "Hi! I am 'John' ;)";
    int lennin = strlen(instr);
    int shifts, i, len, index, start, next;

    printf("Working with  %s\n", instr);

    for(shifts=0; shifts<5; shifts++) {                         // various examples
        printf("Shifts = %d    ", shifts);
        start = 0;
        while(start < lennin) {
            while (start < lennin && !isalnum(instr[start])) {  // find next alphanum
                printf("%c", instr[start]);                     // output non-alphanum
                start++;
            }
            next = start + 1;
            while (isalnum(instr[next]))                        // find next non-alphanum
                next++;
            len = next - start;
            for(i=0; i<len; i++) {                              // shift the substring
                index = i - shifts;
                while(index < 0) index += len;                  // get index in range
                printf("%c", instr[start + (index % len)]);     // ditto
            }
            start = next;                                       // next substring
        }
        printf("\n");
    }
    return 0;
}

程序输出:

Working with  Hi! I am 'John' ;)
Shifts = 0    Hi! I am 'John' ;)
Shifts = 1    iH! I ma 'nJoh' ;)
Shifts = 2    Hi! I am 'hnJo' ;)
Shifts = 3    iH! I ma 'ohnJ' ;)
Shifts = 4    Hi! I am 'John' ;)

【讨论】:

  • 谢谢,但是因为我在笔记本电脑前花了几个小时来编写代码,所以我可能会保持这种形式:D。但是,我认为您的算法对于我现在的知识和我的理解来说有点复杂。再次感谢您。
  • 算法找到一个词的索引start和后面的next索引。然后它根据您的要求旋转该子字符串(通过操作index),然后继续下一个单词。它不是很复杂,但比你的算法更有效。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-21
  • 2020-04-16
  • 1970-01-01
  • 2015-03-02
  • 1970-01-01
相关资源
最近更新 更多