简体   繁体   English

如何在 C 或 C++ 中将字符串反转到位?

[英]How do you reverse a string in place in C or C++?

How do you reverse a string in C or C++ without requiring a separate buffer to hold the reversed string?如何在 C 或 C++ 中反转字符串,而不需要单独的缓冲区来保存反转的字符串?

#include <algorithm>
std::reverse(str.begin(), str.end());

This is the simplest way in C++.这是 C++ 中最简单的方法。

Read Kernighan and Ritchie读 Kernighan 和 Ritchie

#include <string.h>

void reverse(char s[])
{
    int length = strlen(s) ;
    int c, i, j;

    for (i = 0, j = length - 1; i < j; i++, j--)
    {
        c = s[i];
        s[i] = s[j];
        s[j] = c;
    }
}

The standard algorithm is to use pointers to the start / end, and walk them inward until they meet or cross in the middle.标准算法是使用指向开始/结束的指针,然后将它们向内移动,直到它们在中间相遇或交叉。 Swap as you go.随走随换。


Reverse ASCII string, ie a 0-terminated array where every character fits in 1 char .反转 ASCII 字符串,即以 0 结尾的数组,其中每个字符都适合 1 个char (Or other non-multibyte character sets). (或其他非多字节字符集)。

void strrev(char *head)
{
  if (!head) return;
  char *tail = head;
  while(*tail) ++tail;    // find the 0 terminator, like head+strlen
  --tail;               // tail points to the last real char
                        // head still points to the first
  for( ; head < tail; ++head, --tail) {
      // walk pointers inwards until they meet or cross in the middle
      char h = *head, t = *tail;
      *head = t;           // swapping as we go
      *tail = h;
  }
}

// test program that reverses its args
#include <stdio.h>

int main(int argc, char **argv)
{
  do {
    printf("%s ",  argv[argc-1]);
    strrev(argv[argc-1]);
    printf("%s\n", argv[argc-1]);
  } while(--argc);

  return 0;
}

The same algorithm works for integer arrays with known length, just use tail = start + length - 1 instead of the end-finding loop.相同的算法适用于已知长度的整数数组,只需使用tail = start + length - 1而不是结束查找循环。

(Editor's note: this answer originally used XOR-swap for this simple version, too. Fixed for the benefit of future readers of this popular question. XOR-swap is highly not recommended ; hard to read and making your code compile less efficiently. You can see on the Godbolt compiler explorer how much more complicated the asm loop body is when xor-swap is compiled for x86-64 with gcc -O3.) (编者注:这个答案最初也为这个简单版本使用了 XOR-swap。为了这个流行问题的未来读者的利益而修复。 强烈不推荐 XOR-swap ;难以阅读并且使您的代码编译效率降低。你可以在 Godbolt 编译器资源管理器上看到当使用 gcc -O3 为 x86-64 编译 xor-swap 时,asm 循环体有多复杂。)


Ok, fine, let's fix the UTF-8 chars...好的,好吧,让我们修复 UTF-8 字符...

(This is XOR-swap thing. Take care to note that you must avoid swapping with self, because if *p and *q are the same location you'll zero it with a^a==0. XOR-swap depends on having two distinct locations, using them each as temporary storage.) (这是 XOR-swap 的事情。请注意,您必须避免与 self 交换,因为如果*p*q是相同的位置,您将使用 a^a==0 将其归零。XOR-swap 取决于两个不同的位置,每个位置都用作临时存储。)

Editor's note: you can replace SWP with a safe inline function using a tmp variable.编者注:您可以使用 tmp 变量将 SWP 替换为安全的内联函数。

#include <bits/types.h>
#include <stdio.h>

#define SWP(x,y) (x^=y, y^=x, x^=y)

void strrev(char *p)
{
  char *q = p;
  while(q && *q) ++q; /* find eos */
  for(--q; p < q; ++p, --q) SWP(*p, *q);
}

void strrev_utf8(char *p)
{
  char *q = p;
  strrev(p); /* call base case */

  /* Ok, now fix bass-ackwards UTF chars. */
  while(q && *q) ++q; /* find eos */
  while(p < --q)
    switch( (*q & 0xF0) >> 4 ) {
    case 0xF: /* U+010000-U+10FFFF: four bytes. */
      SWP(*(q-0), *(q-3));
      SWP(*(q-1), *(q-2));
      q -= 3;
      break;
    case 0xE: /* U+000800-U+00FFFF: three bytes. */
      SWP(*(q-0), *(q-2));
      q -= 2;
      break;
    case 0xC: /* fall-through */
    case 0xD: /* U+000080-U+0007FF: two bytes. */
      SWP(*(q-0), *(q-1));
      q--;
      break;
    }
}

int main(int argc, char **argv)
{
  do {
    printf("%s ",  argv[argc-1]);
    strrev_utf8(argv[argc-1]);
    printf("%s\n", argv[argc-1]);
  } while(--argc);

  return 0;
}
  • Why, yes, if the input is borked, this will cheerfully swap outside the place.为什么,是的,如果输入被中断,这将在外面愉快地交换。
  • Useful link when vandalising in the UNICODE: http://www.macchiato.com/unicode/chart/在 UNICODE 中破坏时的有用链接: http : //www.macchiato.com/unicode/chart/
  • Also, UTF-8 over 0x10000 is untested (as I don't seem to have any font for it, nor the patience to use a hexeditor)此外,超过 0x10000 的 UTF-8 未经测试(因为我似乎没有任何字体,也没有耐心使用十六进制编辑器)

Examples:例子:

$ ./strrev Räksmörgås ░▒▓○◔◑◕●

░▒▓○◔◑◕● ●◕◑◔○▓▒░

Räksmörgås sågrömskäR

./strrev verrts/.

Reverse a string in place (visualization): 在原处反转字符串(可视化):

将字符串反转到位

Non-evil C, assuming the common case where the string is a null-terminated char array:非邪恶 C,假设字符串是一个以空char结尾的char数组的常见情况:

#include <stddef.h>
#include <string.h>

/* PRE: str must be either NULL or a pointer to a 
 * (possibly empty) null-terminated string. */
void strrev(char *str) {
  char temp, *end_ptr;

  /* If str is NULL or empty, do nothing */
  if( str == NULL || !(*str) )
    return;

  end_ptr = str + strlen(str) - 1;

  /* Swap the chars */
  while( end_ptr > str ) {
    temp = *str;
    *str = *end_ptr;
    *end_ptr = temp;
    str++;
    end_ptr--;
  }
}

您可以使用C ++标准库中的std::reverse算法。

It's been a while and I don't remember which book taught me this algorithm, but I thought it was quite ingenious and simple to understand:已经有一段时间了,我不记得是哪本书教了我这个算法,但我认为它非常巧妙且易于理解:

char input[] = "moc.wolfrevokcats";

int length = strlen(input);
int last_pos = length-1;
for(int i = 0; i < length/2; i++)
{
    char tmp = input[i];
    input[i] = input[last_pos - i];
    input[last_pos - i] = tmp;
}

printf("%s\n", input);

A visualization of this algorithm, courtesy of slashdottir :该算法的可视化,由slashdottir 提供

将字符串原地反转的算法的可视化

Use the std::reverse method from STL : 使用STLstd :: reverse方法:

std::reverse(str.begin(), str.end());

You will have to include the "algorithm" library, #include<algorithm> . 您将必须包含“ algorithm”库#include<algorithm>

Note that the beauty of std::reverse is that it works with char * strings and std::wstring s just as well as std::string s请注意, std::reverse 的美妙之处在于它可以与char *字符串和std::wstring ,就像std::string

void strrev(char *str)
{
    if (str == NULL)
        return;
    std::reverse(str, str + strlen(str));
}

In the interest of completeness, it should be pointed out that there are representations of strings on various platforms in which the number of bytes per character varies depending on the character.为了完整起见,应该指出在各种平台上都有字符串表示,其中每个字符的字节数字符异。 Old-school programmers would refer to this as DBCS (Double Byte Character Set) .老派程序员将其称为DBCS(双字节字符集) Modern programmers more commonly encounter this in UTF-8 (as well as UTF-16 and others).现代程序员更常在UTF-8 (以及UTF-16和其他)中遇到这种情况。 There are other such encodings as well.还有其他这样的编码。

In any of these variable-width encoding schemes, the simple algorithms posted here ( evil , non-evil or otherwise ) would not work correctly at all!在任何这些可变宽度编码方案中,这里发布的简单算法( 邪恶非邪恶其他)根本无法正常工作! In fact, they could even cause the string to become illegible or even an illegal string in that encoding scheme.事实上,它们甚至可能导致字符串变得难以辨认,甚至在该编码方案中成为非法字符串。 See Juan Pablo Califano's answer for some good examples.有关一些很好的例子,请参阅Juan Pablo Califano 的回答

std::reverse() potentially would still work in this case, as long as your platform's implementation of the Standard C++ Library (in particular, string iterators) properly took this into account. std::reverse() 在这种情况下可能仍然有效,只要您的标准 C++ 库的平台实现(特别是字符串迭代器)正确地考虑了这一点。

If you're looking for reversing NULL terminated buffers, most solutions posted here are OK.如果您正在寻找反转 NULL 终止的缓冲区,这里发布的大多数解决方案都可以。 But, as Tim Farley already pointed out, these algorithms will work only if it's valid to assume that a string is semantically an array of bytes (ie single-byte strings), which is a wrong assumption, I think.但是,正如 Tim Farley 已经指出的那样,这些算法只有在假设字符串在语义上是字节数组(即单字节字符串)是有效的情况下才有效,我认为这是错误的假设。

Take for example, the string "año" (year in Spanish).以字符串“año”(西班牙语中的年份)为例。

The Unicode code points are 0x61, 0xf1, 0x6f. Unicode 代码点是 0x61、0xf1、0x6f。

Consider some of the most used encodings:考虑一些最常用的编码:

Latin1 / iso-8859-1 (single byte encoding, 1 character is 1 byte and vice versa): Latin1 / iso-8859-1 (单字节编码,1个字符为1个字节,反之亦然):

Original:原来的:

0x61, 0xf1, 0x6f, 0x00 0x61、0xf1、0x6f、0x00

Reverse:逆转:

0x6f, 0xf1, 0x61, 0x00 0x6f、0xf1、0x61、0x00

The result is OK结果正常

UTF-8: UTF-8:

Original:原来的:

0x61, 0xc3, 0xb1, 0x6f, 0x00 0x61、0xc3、0xb1、0x6f、0x00

Reverse:逆转:

0x6f, 0xb1, 0xc3, 0x61, 0x00 0x6f、0xb1、0xc3、0x61、0x00

The result is gibberish and an illegal UTF-8 sequence结果是胡言乱语和非法的 UTF-8 序列

UTF-16 Big Endian: UTF-16 大端:

Original:原来的:

0x00, 0x61, 0x00, 0xf1, 0x00, 0x6f, 0x00, 0x00 0x00, 0x61, 0x00, 0xf1, 0x00, 0x6f, 0x00, 0x00

The first byte will be treated as a NUL-terminator.第一个字节将被视为 NUL 终止符。 No reversing will take place.不会发生逆转。

UTF-16 Little Endian: UTF-16 小端:

Original:原来的:

0x61, 0x00, 0xf1, 0x00, 0x6f, 0x00, 0x00, 0x00 0x61、0x00、0xf1、0x00、0x6f、0x00、0x00、0x00

The second byte will be treated as a NUL-terminator.第二个字节将被视为 NUL 终止符。 The result will be 0x61, 0x00, a string containing the 'a' character.结果将是 0x61, 0x00,一个包含 'a' 字符的字符串。

Another C++ way (though I would probably use std::reverse() myself :) as being more expressive and faster)另一种 C++ 方式(尽管我自己可能会使用 std::reverse() :) 因为更具表现力和速度更快)

str = std::string(str.rbegin(), str.rend());

The C way (more or less :) ) and please, be careful about XOR trick for swapping, compilers sometimes cannot optimize that. C方式(或多或少:)),请注意交换的XOR技巧,编译器有时无法优化它。

In such case it is usually much slower. 在这种情况下,它通常要慢得多。

char* reverse(char* s)
{
    char* beg = s, *end = s, tmp;
    while (*end) end++;
    while (end-- > beg)
    { 
        tmp  = *beg; 
        *beg++ = *end;  
        *end =  tmp;
    }
    return s;
} // fixed: check history for details, as those are interesting ones
#include <cstdio>
#include <cstdlib>
#include <string>

void strrev(char *str)
{
        if( str == NULL )
                return;

        char *end_ptr = &str[strlen(str) - 1];
        char temp;
        while( end_ptr > str )
        {
                temp = *str;
                *str++ = *end_ptr;
                *end_ptr-- = temp;
        }
}

int main(int argc, char *argv[])
{
        char buffer[32];

        strcpy(buffer, "testing");
        strrev(buffer);
        printf("%s\n", buffer);

        strcpy(buffer, "a");
        strrev(buffer);
        printf("%s\n", buffer);

        strcpy(buffer, "abc");
        strrev(buffer);
        printf("%s\n", buffer);

        strcpy(buffer, "");
        strrev(buffer);
        printf("%s\n", buffer);

        strrev(NULL);

        return 0;
}

This code produces this output:此代码产生此输出:

gnitset
a
cba

如果您使用 GLib,它有两个函数, g_strreverse()g_utf8_strreverse()

I like Evgeny's K&R answer.我喜欢 Evgeny 的 K&R 答案。 However, it is nice to see a version using pointers.但是,很高兴看到使用指针的版本。 Otherwise, it's essentially the same:否则,它本质上是相同的:

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

char *reverse(char *str) {
    if( str == NULL || !(*str) ) return NULL;
    int i, j = strlen(str)-1;
    char *sallocd;
    sallocd = malloc(sizeof(char) * (j+1));
    for(i=0; j>=0; i++, j--) {
        *(sallocd+i) = *(str+j);
    }
    return sallocd;
}

int main(void) {
    char *s = "a man a plan a canal panama";
    char *sret = reverse(s);
    printf("%s\n", reverse(sret));
    free(sret);
    return 0;
}

Recursive function to reverse a string in place (no extra buffer, malloc).递归函数来反转字符串(没有额外的缓冲区,malloc)。

Short, sexy code.简短而性感的代码。 Bad, bad stack usage.糟糕,糟糕的堆栈使用。

#include <stdio.h>

/* Store the each value and move to next char going down
 * the stack. Assign value to start ptr and increment 
 * when coming back up the stack (return).
 * Neat code, horrible stack usage.
 *
 * val - value of current pointer.
 * s - start pointer
 * n - next char pointer in string.
 */
char *reverse_r(char val, char *s, char *n)
{
    if (*n)
        s = reverse_r(*n, s, n+1);
   *s = val;
   return s+1;
}

/*
 * expect the string to be passed as argv[1]
 */
int main(int argc, char *argv[])
{
    char *aString;

    if (argc < 2)
    {
        printf("Usage: RSIP <string>\n");
        return 0;
    }

    aString = argv[1];
    printf("String to reverse: %s\n", aString );

    reverse_r(*aString, aString, aString+1); 
    printf("Reversed String:   %s\n", aString );

    return 0;
}

如果您使用 ATL/MFC CString ,只需调用CString::MakeReverse()

Use std::reverse() 使用std::reverse()

reverse(begin(str), end(str));

And that's it. 就是这样。

Share my code. 分享我的代码。 As a C++ learner, as an option to use swap(), I am humbly asking for comments. 作为一名C ++学习者,作为使用swap()的一种选择,我谦卑地要求发表评论。

void reverse(char* str) {
    int length = strlen(str);
    char* str_head = str;
    char* str_tail = &str[length-1];
    while (str_head < str_tail) 
        swap(*str_head++, *str_tail--);
}

Yet another:完后还有:

#include <stdio.h>
#include <strings.h>

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

  char *reverse = argv[argc-1];
  char *left = reverse;
  int length = strlen(reverse);
  char *right = reverse+length-1;
  char temp;

  while(right-left>=1){

    temp=*left;
    *left=*right;
    *right=temp;
    ++left;
    --right;

  }

  printf("%s\n", reverse);

}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

unsigned char * utf8_reverse(const unsigned char *, int);
void assert_true(bool);

int main(void)
{
    unsigned char str[] = "mañana mañana";
    unsigned char *ret = utf8_reverse(str,  strlen((const char *) str) + 1);

    printf("%s\n", ret);
    assert_true(0 == strncmp((const char *) ret, "anãnam anañam", strlen("anãnam anañam") + 1));

    free(ret);

    return EXIT_SUCCESS;
}

unsigned char * utf8_reverse(const unsigned char *str, int size)
{
    unsigned char *ret = calloc(size, sizeof(unsigned char*));
    int ret_size = 0;
    int pos = size - 2;
    int char_size = 0;

    if (str ==  NULL) {
        fprintf(stderr, "failed to allocate memory.\n");
        exit(EXIT_FAILURE);
    }

    while (pos > -1) {

        if (str[pos] < 0x80) {
            char_size = 1;
        } else if (pos > 0 && str[pos - 1] > 0xC1 && str[pos - 1] < 0xE0) {
            char_size = 2;
        } else if (pos > 1 && str[pos - 2] > 0xDF && str[pos - 2] < 0xF0) {
            char_size = 3;
        } else if (pos > 2 && str[pos - 3] > 0xEF && str[pos - 3] < 0xF5) {
            char_size = 4;
        } else {
            char_size = 1;
        }

        pos -= char_size;
        memcpy(ret + ret_size, str + pos + 1, char_size);
        ret_size += char_size;
    }    

    ret[ret_size] = '\0';

    return ret;
}

void assert_true(bool boolean)
{
    puts(boolean == true ? "true" : "false");
}

With C++ lambda: 使用C ++ lambda:

 auto reverse = [](std::string& s) -> std::string {
        size_t start = 0, end = s.length() -1;
        char temp;

        while (start < end) {
          temp = s[start];
          s[start++] = s[end];
          s[end--] = temp;
        } 

        return s;
   };

My answer would be similar to most of them, but please find my code here. 我的答案与大多数答案相似,但请在此处找到我的代码。

//Method signature to reverse string
string reverseString(string str);

int main(void){
    string str;
    getline(cin, str);
    str =  reverseString(str);
    cout << "The reveresed string is : " << str;
    return 0;
}

/// <summary>
///     Reverses the input string.
/// </summary>
/// <param name="str">
///    This is the input string which needs to be reversed.
/// </param>
/// <return datatype = string>
///     This method would return the reversed string
/// </return datatype>

string reverseString(string str){
    int length = str.size()-1;
    char temp;
    for( int i=0 ;i<(length/2);i++)
    {
        temp = str[i];
        str[i] = str[length-i];
        str[length-i] = temp;
    }
    return str;
}

I think there is another way to reverse the string. 我认为还有另一种方法可以反转字符串。 get the input from user and reverse it. 从用户那里获取输入并进行反转。

void Rev() {
    char ch;
    cin.get(ch);
    if(ch != '\n') {
        Rev();
        cout.put(ch);
    }
}

C++ multi-byte UTF-8 reverser C++ 多字节 UTF-8 逆向器

My thought is that you can never just swap ends, you must always move from beginning-to-end, move through the string and look for "how many bytes will this character require?"我的想法是你永远不能只是交换结尾,你必须总是从头到尾移动,在字符串中移动并寻找“这个字符需要多少字节?” I attach the character starting at the original end position, and remove the character from the front of the string.我从原始结束位置开始附加字符,然后从字符串的前面删除字符。

void StringReverser(std::string *original)
{
  int eos = original->length() - 1;
  while (eos > 0) {
    char c = (*original)[0];
    int characterBytes;
    switch( (c & 0xF0) >> 4 ) {
    case 0xC:
    case 0xD: /* U+000080-U+0007FF: two bytes. */
      characterBytes = 2;
      break;
    case 0xE: /* U+000800-U+00FFFF: three bytes. */
      characterBytes = 3;
      break;
    case 0xF: /* U+010000-U+10FFFF: four bytes. */
      characterBytes = 4;
      break;
    default:
      characterBytes = 1;
      break;
    }

    for (int i = 0; i < characterBytes; i++) {
      original->insert(eos+i, 1, (*original)[i]);
    }
    original->erase(0, characterBytes);
    eos -= characterBytes;
  }
}
void reverseString(vector<char>& s) {
        int l = s.size();
        char ch ;
        int i = 0 ;
        int j = l-1;
        while(i < j){
                s[i] = s[i]^s[j];
                s[j] = s[i]^s[j];
                s[i] = s[i]^s[j];
                i++;
                j--;
        }
        for(char c : s)
                cout <<c ;
        cout<< endl;
}

In C++ the reverse can be done in a function:在 C++ 中,可以在函数中完成相反的操作:

#include <algorithm>
#include <string>

void backwards(vector<string> &inputs_ref) {
    for (auto i = inputs_ref.begin(); i != inputs_ref.end(); ++i) {
        reverse(i->begin(), i->end());
    }
}

input string, return string, No other library required输入字符串,返回字符串,不需要其他库

std::string reverse_string(std::string &str)
{   
  const char*buf = str.c_str();
  char *start = const_cast<char*>(buf);
  char *end = start + strlen(buf) - 1;
  char t;

  while(start < end)
  {
      t = *start;
      *start = *end;
      *end = t;
      start ++;
      end --;
  }
  str = buf;
  return str;
}
std::string md1 = "abcdefghijklmnopqrstuvwxyz0123456789";
std::cout << reverse_string(md1) << std::endl;

//9876543210zyxwvutsrqponmlkjihgfedcba

If you don't need to store it, you can reduce the time spent like this:如果你不需要存储它,你可以像这样减少花费的时间:

void showReverse(char s[], int length)
{
    printf("Reversed String without storing is ");
    //could use another variable to test for length, keeping length whole.
    //assumes contiguous memory
    for (; length > 0; length--)
    {
        printf("%c", *(s+ length-1) );
    }
    printf("\n");
}

Here's my take on it in C. Did it for practice and tried to be as concise as possible! 这是我在C语言中的观点。它是为练习而设计的,并尽可能简洁! You enter a string via the command line, ie ./program_name "enter string here" 您通过命令行输入一个字符串,即./program_name“在此处输入字符串”

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

void reverse(int s,int e,int len,char t,char* arg) {
   for(;s<len/2;t=arg[s],arg[s++]=arg[e],arg[e--]=t);
}

int main(int argc,char* argv[]) {
  int s=0,len=strlen(argv[1]),e=len-1; char t,*arg=argv[1];
  reverse(s,e,len,t,arg);
  for(s=0,e=0;e<=len;arg[e]==' '||arg[e]=='\0'?reverse(s,e-1,e+s,t,arg),s=++e:e++);
  printf("%s\n",arg);
}

But I think the XOR swap algorithm is the best... 但是我认为XOR交换算法是最好的...

char str[]= {"I am doing reverse string"};
char* pStr = str;

for(int i = 0; i != ((int)strlen(str)-1)/2; i++)
{
    char b = *(pStr+i);
    *(pStr+i) = *(pStr+strlen(str)-1-i);
    *(pStr+strlen(str)-1-i) = b;
}
#include<stdio.h>
#include<conio.h>

int main()
{
    char *my_string = "THIS_IS_MY_STRING";
    char *rev_my_string = my_string;

    while (*++rev_my_string != '\0')
        ;

    while (rev_my_string-- != (my_string-1))
    {
        printf("%c", *rev_my_string);
    }

    getchar();
    return 0;
}

This is optimised code in the C language for reversing a string... And it is simple; 这是C语言中用于反转字符串的优化代码。 just use a simple pointer to do the job... 只需使用简单的指针即可完成工作...

Here is the cleanest, safest, easiest way to reverse a string in C++ (in my opinion): 这是在C ++中反转字符串的最干净,最安全,最简单的方法(我认为):

#include <string>

void swap(std::string& str, int index1, int index2) {

    char temp = str[index1];
    str[index1] = str[index2];
    str[index2] = temp;

}

void reverse(std::string& str) {

    for (int i = 0; i < str.size() / 2; i++)
        swap(str, i, str.size() - i - 1);

}

An alternative is to use std::swap , but I like defining my own functions - it's an interesting exercise and you don't need to include anything extra. 一种替代方法是使用std::swap ,但是我喜欢定义自己的函数-这是一个有趣的练习,您不需要include任何其他内容。

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

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