繁体   English   中英

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

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

如何在 C 或 C++ 中反转字符串,而不需要单独的缓冲区来保存反转的字符串?

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

这是 C++ 中最简单的方法。

读 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;
    }
}

标准算法是使用指向开始/结束的指针,然后将它们向内移动,直到它们在中间相遇或交叉。 随走随换。


反转 ASCII 字符串,即以 0 结尾的数组,其中每个字符都适合 1 个char (或其他非多字节字符集)。

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;
}

相同的算法适用于已知长度的整数数组,只需使用tail = start + length - 1而不是结束查找循环。

(编者注:这个答案最初也为这个简单版本使用了 XOR-swap。为了这个流行问题的未来读者的利益而修复。 强烈不推荐 XOR-swap ;难以阅读并且使您的代码编译效率降低。你可以在 Godbolt 编译器资源管理器上看到当使用 gcc -O3 为 x86-64 编译 xor-swap 时,asm 循环体有多复杂。)


好的,好吧,让我们修复 UTF-8 字符...

(这是 XOR-swap 的事情。请注意,您必须避免与 self 交换,因为如果*p*q是相同的位置,您将使用 a^a==0 将其归零。XOR-swap 取决于两个不同的位置,每个位置都用作临时存储。)

编者注:您可以使用 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;
}
  • 为什么,是的,如果输入被中断,这将在外面愉快地交换。
  • 在 UNICODE 中破坏时的有用链接: http : //www.macchiato.com/unicode/chart/
  • 此外,超过 0x10000 的 UTF-8 未经测试(因为我似乎没有任何字体,也没有耐心使用十六进制编辑器)

例子:

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

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

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

./strrev verrts/.

在原处反转字符串(可视化):

将字符串反转到位

非邪恶 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算法。

已经有一段时间了,我不记得是哪本书教了我这个算法,但我认为它非常巧妙且易于理解:

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);

该算法的可视化,由slashdottir 提供

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

使用STLstd :: reverse方法:

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

您将必须包含“ algorithm”库#include<algorithm>

请注意, std::reverse 的美妙之处在于它可以与char *字符串和std::wstring ,就像std::string

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

为了完整起见,应该指出在各种平台上都有字符串表示,其中每个字符的字节数字符异。 老派程序员将其称为DBCS(双字节字符集) 现代程序员更常在UTF-8 (以及UTF-16和其他)中遇到这种情况。 还有其他这样的编码。

在任何这些可变宽度编码方案中,这里发布的简单算法( 邪恶非邪恶其他)根本无法正常工作! 事实上,它们甚至可能导致字符串变得难以辨认,甚至在该编码方案中成为非法字符串。 有关一些很好的例子,请参阅Juan Pablo Califano 的回答

std::reverse() 在这种情况下可能仍然有效,只要您的标准 C++ 库的平台实现(特别是字符串迭代器)正确地考虑了这一点。

如果您正在寻找反转 NULL 终止的缓冲区,这里发布的大多数解决方案都可以。 但是,正如 Tim Farley 已经指出的那样,这些算法只有在假设字符串在语义上是字节数组(即单字节字符串)是有效的情况下才有效,我认为这是错误的假设。

以字符串“año”(西班牙语中的年份)为例。

Unicode 代码点是 0x61、0xf1、0x6f。

考虑一些最常用的编码:

Latin1 / iso-8859-1 (单字节编码,1个字符为1个字节,反之亦然):

原来的:

0x61、0xf1、0x6f、0x00

逆转:

0x6f、0xf1、0x61、0x00

结果正常

UTF-8:

原来的:

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

逆转:

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

结果是胡言乱语和非法的 UTF-8 序列

UTF-16 大端:

原来的:

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

第一个字节将被视为 NUL 终止符。 不会发生逆转。

UTF-16 小端:

原来的:

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

第二个字节将被视为 NUL 终止符。 结果将是 0x61, 0x00,一个包含 'a' 字符的字符串。

另一种 C++ 方式(尽管我自己可能会使用 std::reverse() :) 因为更具表现力和速度更快)

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

C方式(或多或少:)),请注意交换的XOR技巧,编译器有时无法优化它。

在这种情况下,它通常要慢得多。

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;
}

此代码产生此输出:

gnitset
a
cba

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

我喜欢 Evgeny 的 K&R 答案。 但是,很高兴看到使用指针的版本。 否则,它本质上是相同的:

#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;
}

递归函数来反转字符串(没有额外的缓冲区,malloc)。

简短而性感的代码。 糟糕,糟糕的堆栈使用。

#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()

使用std::reverse()

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

就是这样。

分享我的代码。 作为一名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--);
}

完后还有:

#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");
}

使用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;
   };

我的答案与大多数答案相似,但请在此处找到我的代码。

//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;
}

我认为还有另一种方法可以反转字符串。 从用户那里获取输入并进行反转。

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

C++ 多字节 UTF-8 逆向器

我的想法是你永远不能只是交换结尾,你必须总是从头到尾移动,在字符串中移动并寻找“这个字符需要多少字节?” 我从原始结束位置开始附加字符,然后从字符串的前面删除字符。

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;
}

在 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());
    }
}

输入字符串,返回字符串,不需要其他库

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

如果你不需要存储它,你可以像这样减少花费的时间:

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");
}

这是我在C语言中的观点。它是为练习而设计的,并尽可能简洁! 您通过命令行输入一个字符串,即./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);
}

但是我认为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;
}

这是C语言中用于反转字符串的优化代码。 只需使用简单的指针即可完成工作...

这是在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);

}

一种替代方法是使用std::swap ,但是我喜欢定义自己的函数-这是一个有趣的练习,您不需要include任何其他内容。

暂无
暂无

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

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