简体   繁体   English

在字符串中反转字顺序

[英]Reversing the word order in a string in place

I'm trying to reverse the order of words in a sentence in place, eg: 我试图在一个句子中改变单词的顺序,例如:

This sentences words are reversed. 这句话是相反的。

becomes

reversed. 逆转。 are words sentences This 是单词句子这个

This is what I have so far, which almost works: I use the strrev function to reverse the string, and then the inprev function to send each word to the strrev function individually, to reverse them back to the original orientation, but in reversed order. 这是我到目前为止,它几乎可以工作:我使用strrev函数来反转字符串,然后使用inprev函数将每个单词单独发送到strrev函数,将它们反转回原始方向,但顺序相反。 Sending a pointer for the start and end of the strrev function might seem a bit silly, but it allows the same function to be used in inprev(), sending off a pointer to the start and end of individual words. 为strrev函数的开始和结束发送指针可能看起来有点傻,但它允许在inprev()中使用相同的函数,发送指向单个单词的开头和结尾的指针。

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

void strrev(char * start, char * end);
void inprev(char * start);

int main(void)
{
   char str[] = "Foobar my friends, foobar";
   char * end = (str + strlen(str) -1);
   puts(str);
   strrev(str, end);
   puts(str);
   inprev(str);

   puts(str);

   return 0;
}

void strrev(char * start, char * end)
{
   char temp;

while (end > start)
   {
     temp = *start;
     *start = *end;
     *end = temp;
      start++;
      end--;
   }
}

void inprev(char * start)
{
     char * first = start;
     char * spcpnt = start;
     while (*spcpnt)
     {
        while (*spcpnt != ' ' && *spcpnt)
           spcpnt++;
        strrev(start, spcpnt-1);         // removing the -1 sends the space on the 
        start = spcpnt++;                // other side to be reversed, doesn't stop 
                                         // the problem.  

     }

}

Here is the output: 这是输出:

Foobar my friends, foobar Foobar我的朋友,foobar

raboof ,sdneirf ym rabooF raboof,sdneirf ym rabooF

foobarfriends, my Foobar foob​​arfriends,我的Foobar

The problem is that the lack of a final space at the end of the final word means that a space is missing between that word and the preceeding one in the final string, and instead gets thrown onto the end of the last word, which was the first word in the original string. 问题是在最后一个单词的末尾缺少最后一个空格意味着该单词和最后一个单词中的前一个单词之间缺少一个空格,而是被抛到最后一个单词的末尾,这就是原始字符串中的第一个单词。 Sending off the space on the other side of the word only moves the problem elsewhere. 发送单词另一侧的空格只会将问题移到其他地方。 Can anyone see a solution? 有谁能看到解决方案?

You just need to move the start pointer in the inprev function to skip the space between words. 您只需要在inprev函数中移动start指针以跳过单词之间的空格。 As this appears to be homework (correct me if I'm wrong) I'll just say that all you need to do is move the location of one operator. 因为这似乎是家庭作业(纠正我,如果我错了)我只会说你需要做的就是移动一个操作员的位置。

But, this produces a problem, namely, the inprev performs a buffer overrun because the search isn't terminated properly. 但是,这会产生一个问题,即inprev执行缓冲区溢出,因为搜索未正确终止。 A better way to do it is: 更好的方法是:

while not end of string
  search for start of word
  start = start of word
  search for end of word
  strrev (start, end)

and that will take care of multiple spaces too. 这也将照顾多个空间。 Also, U+0020 (ASCII 32, a space) is not the only white space character. 此外,U + 0020(ASCII 32,空格)不是唯一的空格字符。 There are standard library functions that test characters. 有标准的库函数来测试字符。 They are in <ctype.h> and start with is... , eg isspace . 它们位于<ctype.h>中,以is...开头,例如isspace

Sometimes things get easier if you don't use pointers but offsets. 如果你不使用指针而是偏移,有时事情变得更容易。 The strspn() and strcspn() library functions more or less force you to use offsets, and deal with the end-of-string condition quite nicely. strspn()和strcspn()函数函数或多或少地强制你使用偏移量,并且很好地处理字符串结束条件。

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

size_t revword(char *str);
void revmem(void *ptr, size_t len);

size_t revword(char *str) {
size_t pos,len;

for (pos=len=0; str[pos]; pos += len) {
        len = strspn( str+pos, " \t\n\r");
        if (len) continue;
        len = strcspn( str+pos, " \t\n\r");
        if (!len) continue;
        revmem( str+pos, len );
        }
revmem( str, pos );
return len;
}

void revmem(void *ptr, size_t len)
{
size_t idx;
char *str = (char*) ptr;

if (len-- < 2) return;

for (idx = 0; idx < len; idx++,len--) {
        char tmp = str[idx];
        str[idx] = str[len];
        str[len] = tmp;
        }
}

int main (int argc, char **argv)
{

if (!argv[1]) return 0;
revword(argv[1] );
printf("'%s'\n", argv[1] );

return 0;
}                                                                                           

The following algorithm is in-place and runs in 2 steps. 以下算法就地并以两个步骤运行。 First it reverses the entire string. 首先它反转整个字符串。 Then it reverses each word. 然后它反转每个单词。

#include <stdio.h>

void reverse(char *str, int len)
{
    char *p = str;
    char *e = str + len - 1;

    while (p != e) {
        *p ^= *e ^= *p ^= *e;
        p++;
        e--;
    }
}

void reverse_words(char *str)
{
    char *p;

    // First, reverse the entire string
    reverse(str, strlen(str));

    // Then, reverse each word
    p = str;
    while (*p) {
        char *e = p;
        while (*e != ' ' && *e != '\0') {
            e++;
        }

        reverse(p, e - p);
        printf("%.*s%c", e - p, p, *e);

        if (*e == '\0')
            break;
        else
            p = e + 1;
    }
}

int main(void) {
    char buf[] = "Bob likes Alice";
    reverse_words(buf);
    return 0;
}
void reverse_str(char* const p, int i, int j) // helper to reverse string p from index i to j
{
    char t;
    for(; i < j ; i++, j--)
        t=p[i], p[i]=p[j], p[j]=t;
}

void reverse_word_order(char* const p) // reverse order of words in string p
{
    int i, j, len = strlen(p);  // use i, j for start, end indices of each word

    reverse_str(p, 0, len-1);      // first reverse the whole string p
    for(i = j = 0; i < len; i = j) // now reverse chars in each word of string p
    {
        for(; p[i] && isspace(p[i]);) // advance i to word begin
            i++;
        for(j = i; p[j] && !isspace(p[j]);) // advance j to past word end
            j++;
        reverse_str(p, i, j-1);  // reverse chars in word between i, j-1
    }
}

Figured out a solution; 想出一个解决方案; here is my revised function that works properly. 这是我修改后的功能正常工作。

void inprev(char * str)
{
    _Bool inword = 0;
    char * wordend;
    char * wordstart;

     while(*str)
     {
         if(!isspace(*str) && (inword == 0))
         {
             wordstart = str;
             inword = 1;
         }
         else if (isspace(*str) && (inword == 1))
         {
             wordend = str-1;
             inword = 0;
             strrev(wordstart, wordend);
         }

         str++;
     }

     if (*str == '\0')
        strrev(wordstart, str-1);

}

char * wordend is uneccessary as you can just pass str-1 to the strrev function, but it makes it a bit more clear what's happening. char * wordend是不必要的,因为你可以将str-1传递给strrev函数,但它会更清楚地说明发生了什么。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM