【问题标题】:Reversing words in a sentence将句子中的单词颠倒过来
【发布时间】:2010-07-18 17:50:36
【问题描述】:

我目前正在通过 K.N. King 的C 编程:一种现代方法。我已经完成了第 8 章(数组)的正文,我很想继续阅读第 9 章,但是我还没有解决每章末尾的所谓“编程项目”。不幸的是,14 号……困扰我

编写一个程序来反转句子中的单词。

Enter a sentence: you can cage a swallow can't you?
Reversal of sentence: you can't swallow a cage can you?

提示:使用循环逐个读取字符并将它们存储在一维char数组中。让循环在句点、问号或感叹号(“终止字符”)处停止,它们保存在单独的 char 变量中。然后使用第二个循环在数组中向后搜索最后一个单词的开头。打印最后一个单词,然后向后搜索倒数第二个单词。重复直到到达数组的开头。最后,打印结束符。

我一直在考虑将单词定义为空格之间的字符序列。因此,当到达一个空格时,向后退,打印每个字符,直到找到另一个空格。我的 first 版本的程序只打印了第一个单词。它的 current 版本只打印其他单词。我已经坚持了两天,所以任何帮助都非常感谢。这是我的代码以及输出示例。希望我已经正确记录了我的代码。提前致谢!

代码

/* Include the standard I/O library */
#include<stdio.h>

/* Define main */
int main(void) {

    /**
     * Declare an array of characters storing the sentence, as well as
     * a character representing the current character under cursor and
     * the terminating character
     */
    char sentence[100] = { ' ' }, c, tc;

    /**
     * Declare a loop counter already initialized at 0, an incremental
     * variable, as well as the size of the read sentence
     */
    int i = 0, j = 1, size = 0;

    /* Get the sentence */
    printf("Enter a sentence: \n");
    for(c = getchar(); (c != '.') && (c != '!') && 
        (c != '?') && (c != '\n'); c = getchar(), i++) {

        sentence[i] = c; /* Store the current character in the array */
        size++; /* Increase the sentence's size */
    }

    tc = c; /* Get the terminating character */

    /**
     * Go backward through the array, printing each sequence of characters
     * between spaces
     */
    for(i = 99; i >= 0; i--) {

        if(sentence[i] == ' ') {

            while(sentence[i + j] != ' ') {

                printf("%c", sentence[i + j]);
                j++;
            }

            j = 1; /* Reset the incremental variable */
            printf(" "); /* Print a tailing space */
        }
    }

    /**
     * Delete the tailing blank space and print the terminating character,
     * as well as a new line 
     */
    printf("\b%c\n", tc);

    return 0; /* Return 0 upon successful program execution */
}

输出:

http://drp.ly/1nYt5J

【问题讨论】:

标签: c arrays


【解决方案1】:

将每个单词压入堆栈,并从索引 0 到 N-1 读取堆栈

【讨论】:

  • 如果这是他第一次在 C 中弄乱字符串,这在 C 中很难做到。我们不是在 C++ 或 perl 领域,toto。 :-)
  • 我已经编辑了我的帖子,删除了“字符串”标签。我想很多误解都遇到了。除了这不是真正的“家庭作业”之外,它也与字符串无关。到目前为止,我应该只知道循环、条件、控制台 I/O、基本数据类型,以及现在的数组。没有字符串或函数。所以我想我必须想出一个临时解决方案。堆栈是......两章之外。
【解决方案2】:

要考虑的另一种方法:

you can cage a swallow can't you?
uoy t'nac wollaws a egac nac uoy?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
you t'nac wollaws a egac nac uoy?
^^^
you can't wollaws a egac nac uoy?
    ^^^^^
you can't swallow a egac nac uoy?
          ^^^^^^^
you can't swallow a egac nac uoy?
                  ^
you can't swallow a cage nac uoy?
                    ^^^^
you can't swallow a cage can uoy?
                         ^^^
you can't swallow a cage can you?
                             ^^^

对于您想要反转的每一件事(无论是整个句子还是单词):

  1. 找到开头和结尾
  2. 交换开始和结束字符
  3. “向内”移动一次
  4. 继续前进,直到到达“中间”

由于反转一段字符串是一种常见的操作,因此将其作为自己的函数是有意义的。由于该功能需要完成其工作的唯一信息是:

  1. 字符串
  2. 开始索引
  3. 结束索引

你认为函数的参数是什么?

需要一遍又一遍地完成的另一件常见事情是“找到”一些东西,无论是空格还是标点符号。您可能需要自己编写,或者如果您可以使用库函数,或者需要提示,请查看:

man strcspn

【讨论】:

  • 事情是……我对编程并不陌生,我在 PHP(和相关的 Web 技术)方面做了很多工作。但是两周前我想选 C,这一次是永远的(是的,我确实有过尝试,但是上学时间太长了,所以我不得不放弃)。所以我买了这本书,并试图彻底地遵循它。有很多地方我可以使用...函数之类的东西,但我不想这样做,因为,正如我所说,这是下一章的主题,我不想急于求成。我会尽量充分利用你的建议。谢谢!
  • 没问题,很高兴为您提供帮助!这是一个在 c 中搞乱字符串的大型第一个项目。我提到的每个子问题(每个子问题都应该进入一个单独的函数)都是一个很好的起点。编写一个程序以在字符串中查找'.''?',并找出索引。编写一个程序来反转字符串中的所有字符,一直到最后(不是'\0'!)。并编写一个程序来查找并打印出句子中的单个单词。如果你可以分别做这三件事,你可以把它们加在一起来解决这个更大的问题。 :-)
  • 我正在考虑编写您建议的第一个函数,因为通过使用具有可变数量参数的函数来查找输入字符串的结尾会更容易。但是我不应该知道如何编写函数,所以,正如我所说,我必须想出一个临时解决方案。至于第二个函数,我需要两个数组,还是我遗漏了什么?最后,我将单词定义为由空格分隔的字符序列的想法是否合适?
  • 您不需要可变数量的参数。查看strcspn。第一个参数是要搜索的字符串,第二个是包含多个字符的字符串,其中任何一个都会触发查找。 strtok 和其他几个也以这种方式工作。除非您真的非常需要,否则请远离 c 中的可变参数函数。对于第二个功能,您可以就地完成。想想杂耍。如果您将一个球保持在空中,则可以交换两个球。只需为那个球腾出空间,进行交换,然后继续进行下一个交换。是的,空格分隔是最简单的,即使不是 100% 正确 - 从那里开始。
【解决方案3】:

这是我提到的一个例子。首先,将每个单词反转到位,然后反转整个字符串。这是一个reverse() 函数,它使用给定的定界字符反转字符串。如果您愿意,您可以扩展以使用多个分隔符。

char *reverse(char *str, char delim)
{
  char *end = strchr(str, delim);
  char *ret;
  char tmp;

  if (end == NULL)
    end = strchr(str, '\0');

  ret = end + 1;
  end--;

  while (end > str)
  {
    tmp = *str;
    *str = *end;
    *end = tmp;

    end--;
    str++;
  }

  return ret;
}

这是一个带有小示例程序的用例:

int main(int argc, char **argv)
{
  char *end = strchr(argv[1], '\0');
  char *str = argv[1];

  while (str < end)
    str = reverse(str, ' ');

  reverse(argv[1], '\0');
  printf("%s\n", argv[1]);

  return 0;
}

使用示例:

$ ./example "the quick red fox jumps over the lazy brown dog"
dog brown lazy the over jumps fox red quick the

【讨论】:

  • -1 为需要推动正确方向的人提供全面的解决方案。 XLR3204S在给贴现成的解决方案时如何学会思考?!
  • @sbi,他说他对编程并不陌生——只是对 C 语言不熟悉。重点是帮助他用他正在尝试学习的语言编写一个写得很好的例子。算法不是这里的问题,学习特定的语言语法才是。当我在学习一些东西时,我通常希望看到一个好的例子——比我想要一些关于正确语法的模糊提示要多得多。
  • 谁在粘贴任何东西?如果我愿意,我可以跳过这个问题和接下来的 N 章。我试图通过查找我需要的部分来充分利用他的响应/程序。我认为 homework 标签会诱使人们认为我这样做是为了上课之类的。
  • 标签被编辑,这个是由eruciform添加的。我以为这是基本程序,我看到其他自学成才的有 homework 标签。我可以在没有受到制裁的情况下将其删除吗?
  • 我的错,它看起来真的像一个家庭作业问题。我很抱歉。
【解决方案4】:
int main()
{

    char sent[50],last,s;
    int i,j,length,k,temp,b;
    clrscr();
    i=0;
    printf("Enter a sentence: ");
    sent[i]=getchar();
    while(sent[i]!='\n'&&sent[i]!='.'&&sent[i]!='?'&&sent[i]!='!')
    {
        sent[++i]=getchar();
    }
    last=sent[i];//storing last char
    b=i; //length of string
    printf("Reverse of sentence: ");
    for(;;)
    {
        k=b-1;// begin from last position
        temp=k;
        while(sent[k]!=' ' && k!=-1)
        k--;
        s=k;//storing space here
        b=s;
        for(j=b+1;j<=temp;j++)
        putchar(sent[j]);
        if(s!=-1)
        putchar(sent[s]);
        if(b==-1)
        break;
    }
    putchar(last);
    getch();
    return 0;
}

【讨论】:

    【解决方案5】:

    通过将输入作为字符数组,然后反转整个数组。 在此之后,将句子拆分为单词发生在“”,“?”,“\ 0”等处,逐字反转。 希望这会有所帮助。

     void reverse(char s[],int start,int stop){
     char t;
     while(start<stop){
        t = s[start];
        s[start]=s[stop];
        s[stop]=t;
        start++;
        stop--;
    }
    }
    
    int main() {
    
    char str[100];
    gets(str);
    
    int pos=0,begin=0,end;
    reverse(str,begin,strlen(str)-1); //since the last character is null
    
    while(pos<=strlen(str)){
        if((str[pos]==' ')||(str[pos]=='\0')||(str[pos]=='?')||(str[pos]=='!')){
            end = pos - 1; 
           reverse(str,begin,end);  
            begin = pos+1; //for the next word
        }
    
        pos++;
    
    }   
    
    cout<<str;
    return 0;
    }
    

    【讨论】:

      【解决方案6】:

      我没试过。希望对你有帮助。

      char temp[100];
      int j=0, k=100, l=0;
      for(i=size-1; i>=0; i--){
        if(sentence[i] == ' ' || i == 0){
          if(k-i >= 2){// at least one character
      
            if(i==0) j = 0;
            else j = i+1;
      
            for( l=0; j < k; j++, l++){
               temp[l] = sentence[j];
            }
            temp[l] = '\0';
            printf("%s ",temp);
          }
          k = i;
        }
      }
      printf("\b%c",tc);
      

      【讨论】:

      • 可能是因为它结合了你的部分代码和我的部分代码,但我没有否决你的答案,其他人这样做了。我会尝试反对它...
      • 谢谢。我已经按照你的模式,所以你可以很容易地得到它。我已经说过了,我在这里输入了它,没有经过测试-但逻辑应该可以正常工作。
      • Sadat:我对你的答案投了反对票,原因与我对 Carl 的投反对票的原因相同。很抱歉,如果您不理解这一点,我认为这是显而易见的。
      【解决方案7】:

      这是我的答案

      /* 编写一个程序,将句子中的单词倒转:

      输入一句话:你能把燕子关在笼子里,不是吗?

      反句:你不能吞一个笼子吗?

      提示:使用循环逐个读取字符,并将它们存储在一维 char 数组中。

      让循环在句号、问号或感叹号处停止 -- (“终止字符”),它被保存为一个单独的 char 变量。

      然后使用第二个循环在数组中向后搜索最后一个单词的开头。

      打印最后一个单词,然后向后搜索倒数第二个单词。 重复直到最终到达数组的开头。

      最后打印结束符。

      */
      #include<stdio.h>
      
      int main()
      {
      int ch;
      char sentence[200]; //hard set a limit of 200 character sentence
      char word[10] = {'\0','\0','\0','\0','\0','\0','\0','\0','\0'}; //hard set limit of 10 character words
      int i = 0; //character position in input
      int w = 9; //character position in word
      char terminator = '\0';
      printf("Enter a sentence:");
         while  ( (ch=getchar()) != '\n' )
             {
             if ( ch == '.' || ch == '?' || ch == '!')
                terminator = ch;
             else
               {
               sentence[i] = ch;
               i++;
               }
      //       printf("%d",i);
             }
             sentence[i] = '\0';//set last character to null
      
      
          int x;
          for ( x=i ; x >= 0 ; x-- )
              {
                 if ( sentence[x] == ' ' )
                 {
                  printf(" ");//print the space followed by what is in the word buffer/array
      //          printf("word length %d ",w);
                  printf("%c",word[0]); //probably should have a for loop here
                  printf("%c",word[1]);
                  printf("%c",word[2]);
                  printf("%c",word[3]);
                  printf("%c",word[4]);
                  printf("%c",word[5]);
                  printf("%c",word[6]);
                  printf("%c",word[7]);
                  printf("%c",word[8]);
                  printf("%c",word[9]);
                  w = 9 ;
                  word[0] = '\0'; //fill the word buffer/array with null
                  word[1] = '\0';
                  word[2] = '\0';
                  word[3] = '\0';
                  word[4] = '\0';
                  word[5] = '\0';
                  word[6] = '\0';
                  word[7] = '\0';
                  word[8] = '\0';
                  word[9] = '\0';
      //          printf("\n");
      //          printf("sentence position %d ",x);
                 }
                 else //assign the letters from sentence[] to letters in word[]
                 {
                  word[w] = sentence[x];
                  w--;
      //          printf("word length %d ",w);
      //          printf("%c",sentence[x]);
                 }
               }
      //print the first word because im using space to delimit the words unless i have a space at the
      //beginning of the sentence the code above will skip the first word inputed
          printf(" ");//print the space followed by what is in the word buffer/array
          printf("%c",word[0]);
          printf("%c",word[1]);
          printf("%c",word[2]);
          printf("%c",word[3]);
          printf("%c",word[4]);
          printf("%c",word[5]);
          printf("%c",word[6]);
          printf("%c",word[7]);
          printf("%c",word[8]);
          printf("%c",word[9]);
      
      
      
      if ( terminator != '\0' ) //prints a . ? or ! if it is including in the inputed sentence
          printf("%c",terminator);
      
          printf("\n");
          printf("\n");
      return 0;
      

      【讨论】:

        【解决方案8】:

        反转字符串中的单词(单词由一个或多个空格分隔) - 这个问题可以通过多种方式处理,到目前为止我看到的解决方案很少使用额外的内存。我们的想法是获得最佳解决方案,例如不使用时间复杂度 O(N) 的额外内存(就地)解决方案。

        让我们举个例子,假设字符串是“Hello World” - 并且预期的 O/P “World Hello”

        • 首先我们将整个句子反转IN PLACE,例如“dlroW olleH”(我们使用XOR操作进行交换。请看一下Swap()方法)
        • 现在我们增加索引并在遇到'时停止 '(空格)。
        • 一旦我们遇到任何空格,我们就知道我们得到了一个单词。让我们调用 对该单词的反向功能并反转该单词。所以在那 如果是这样,“世界 olleH”
        • 现在我们走得更远,当我们的索引达到长度时停止 句子。
        • 一旦我们到达末尾,抓住最后一个索引 -1 并反转最后一个 单词,所以它就像“World Hello”

        这里需要注意的一点是,当我们调用 Reverse 函数来反转单词时,我们将需要在相应的句子中提供该特定单词的起始索引和结束索引。

        示例代码如下所述。我还没有测试过边缘案例——但它会提供基本的方法思路。

          using System;
        
          namespace SampleString
          {
            class ReverseWordsInSetence
            {
                // Reverse words in a string (words are separated by one or more spaces).
                private static String GetReverseWordsInSetence(string sentence)
                {
                    char[] stringArray = sentence.ToCharArray();
                    int len = sentence.Length;
                    int startIndex = 0;
        
                    Swap(ref stringArray, ref startIndex , len-1);
                    startIndex = 0;
        
                    for (int currentIndex = 0; currentIndex < len; currentIndex++)
                    { 
                        if (stringArray[currentIndex].Equals(' '))
                        {
                            Swap(ref stringArray, ref startIndex, currentIndex-1);
                        }
                        else if (currentIndex == len - 1)
                        {
                            Swap(ref stringArray, ref startIndex, currentIndex);
                        }
        
                    }
        
                    return new string(stringArray);
                }
        
        
                private static void Swap(ref char[] a, ref int i, int j)
                {
                    int tempIndex = j;
        
                    while (i < j)
                    {
                        if (a[j].Equals('.'))
                        {
                            j--;
                        }
                        else
                        {
                            a[i] ^= a[j];
                            a[j] ^= a[i];
                            a[i++] ^= a[j--];
                        }
                    }
        
                    i = tempIndex + 2;
                }
        
                static void Main(string[] args)
                {        
                   Console.WriteLine(GetReverseWordsInSetence("Hello World."));
                   Console.ReadLine();
                }
            }
        }
        

        【讨论】:

          【解决方案9】:

          以下代码将单词压入堆栈,然后向后读出堆栈,如Quonux hinted at

          #include <stdlib.h>
          
          int main()
          {
          char s[20][20];
          int i=0,length=-1;
          for(i=0;;i++)
          {
              scanf("%s",s[i]);
              length++;
              if(getchar()=='\n')
                  break;
          }
          for(i=length;i>=0;i--)
              printf("%s ",s[i]);
          return 0;
          }
          

          【讨论】:

          • 请为未来的访问者提供一些解释。
          • 虽然此代码可以回答问题,但提供有关 如何 和/或 为什么 解决问题的附加上下文将改善答案的长期价值。 - From Review
          【解决方案10】:

          到目前为止的答案已经贡献了替代算法,可以分为两类:

          1. 反转整个句子反转其中的所有单词。有的版本先颠倒单词,而另一些版本则先颠倒句子;应用这些反转的顺序无关紧要。最终效果是单词以相反的顺序出现,但每个单词中的字符按正常顺序排列。
          2. 将单词放在堆栈中,然后再次从堆栈中取出它们,以便最后一个单词成为第一个单词。

          我不会建议另一种替代算法(可能属于上述两类之一),而是解释为什么原始问题中的代码不能按预期工作。

          首先观察题中的代码其实是class 1的变体,先把整个句子倒转,再把每个单词倒转:

          /* Outer loop goes backwards through the array, effectively reversing the sentence */
          for(i = 99; i >= 0; i--) {
          
              if(sentence[i] == ' ') {
          
                  /* Inner loop goes forward, reversing the word again */
                  while(sentence[i + j] != ' ') {
          
                      printf("%c", sentence[i + j]);
                      j++;
                  }
          
                  j = 1;
                  printf(" ");
              }
          }
          

          除了一些初学者的错误之外,这实际上是一个反转句子单词的最佳方法。它不使用额外的内存,也不会浪费时间。

          提问者指出,该算法按预期工作,只是它不打印原始句子的第一个单词(应该成为最后一个单词)。原因是数组遍历在两个方向上都停止在' '。当外循环到达句子的开头时,它没有找到空格,因为用户输入的第一个字符覆盖了sentence[0]中的空格:

          /* ... */
          char sentence[100] = { ' ' }, c, tc;
          
          /* ... */
          int i = 0, j = 1, size = 0;
          
          /* Get the sentence */
          printf("Enter a sentence: \n");
          for(c = getchar(); (c != '.') && (c != '!') && 
              (c != '?') && (c != '\n'); c = getchar(), i++) {
          
              sentence[i] = c; /* Store the current character in the array */
              size++; /* Increase the sentence's size */
          }
          

          因此,当i 在外循环中变为0 时,没有空格,并且应该打印从sentence[0] 开始的单词的内循环永远不会进入。 i 然后递减到-1 并且外部循环终止。

          您只需以用户身份运行程序即可在不更改代码的情况下进行测试。如果您输入一个空格作为第一个字符,程序的响应将是正确的:

          Enter a sentence:
           you can cage a swallow can't you?
          you can't swallow a cage can you?
          

          您可以通过两种方式强制在代码中包含第一个单词。第一个是简单地总是在sentence 数组的开头放置一个空格。你可以通过复制i = 1而不是i = 0的用户输入来做到这一点:

          /**
           * Declare a loop counter already initialized at 1, an incremental
           * variable, as well as the size of the read sentence
           */
          int i = 1, j = 1, size = 0;
          

          另一种不太优雅的方法是在外循环终止后简单地重复内循环:

          /**
           * Go backward through the array, printing each sequence of characters
           * between spaces
           */
          for(i = 99; i >= 0; i--) {
          
              if(sentence[i] == ' ') {
          
                  while(sentence[i + j] != ' ') {
          
                      printf("%c", sentence[i + j]);
                      j++;
                  }
          
                  j = 1; /* Reset the incremental variable */
                  printf(" "); /* Print a tailing space */
              }
          }
          
          /* print the last word */
          while(sentence[i + j] != ' ') {
          
              printf("%c", sentence[i + j]);
              j++;
          }
          

          您可以通过将内部循环分解为新函数来减少重复性。这是将内部循环分解为 print_word 函数的整个算法,跳过了 cmets 和空行:

          #include<stdio.h>
          
          void print_word(char[] sentence, int i) {
              int j = 1;
              while(sentence[i + j] != ' ') {
                  printf("%c", sentence[i + j]);
                  j++;
              }
          }
          
          int main(void) {
              char sentence[100] = { ' ' }, c, tc;
              int i = 0, j = 1, size = 0;
              printf("Enter a sentence: \n");
              for(c = getchar(); (c != '.') && (c != '!') && 
                  (c != '?') && (c != '\n'); c = getchar(), i++) {
                  sentence[i] = c; /* Store the current character in the array */
                  size++; /* Increase the sentence's size */
              }
              tc = c; /* Get the terminating character */
              for(i = 99; i >= 0; i--) {
                  if(sentence[i] == ' ') {
                      print_word(sentence, i);
                      printf(" "); /* Print a tailing space */
                  }
              }
              print_word(sentence, i);
              printf("\b%c\n", tc);
              return 0; /* Return 0 upon successful program execution */
          }
          

          最后,还有一件事你可以做得更好。现在,外部循环从i = 99 开始,这是sentence 数组中最后一个可能的字符。但是,在读取用户输入时,您更新了i 以指向下一个输入位置,因此就在外循环开始之前,i 已经指向句子后面的第一个字符。为什么不使用它而直接从i - 1 开始?

          【讨论】: