当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《如何在C或C++中反转字符串?》经验,为你挑选了12个好方法。

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



1> Greg Rogers..:
#include 
std::reverse(str.begin(), str.end());

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


@fredsbend,所选答案的"荒谬长"版本处理这个简单答案没有的案例 - UTF-8输入.它显示了完全指定问题的重要性.除了问题是关于在C中也能工作的代码.
在C++中,字符串由字符串类表示.他没有要求"char star"或"char括号".保持优雅,C.
如果你使用UTF-8识别字符串类(或者可能是带有std :: basic_string的utf-8字符类),这个答案会处理这种情况.此外,问题是"C或C++",而不是"C和C++".C++只是"C或C++".

2> 小智..:

阅读Kernighan和Ritchie

#include 

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


在这个例子中需要注意的是,字符串`s`必须以数组形式声明.换句话说,`char s [] ="这是好的"`而不是`char*s ="不能这样做"`因为后者导致字符串常量无法修改
在我的iphone上测试这比使用原始指针地址慢15%左右
变量"c"不应该是char而不是int吗?
向"教父"道歉......"留下枪支,带上K&R".作为一个C bigot,我会使用指针,因为它们对于这个问题更简单,更直接,但代码对C#,Java等的可移植性较差.
@Eric这不会在O(log(n))时间内运行.如果您指的是代码执行的字符交换次数,则在O(n)中运行,对于字符串长度的n,则执行n次交换.如果我们正在谈论执行的循环量,那么它仍然是O(n) - 虽然是O(n/2)但是你用Big O表示法丢弃常量.

3> Anders Euren..:

邪恶的C:

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

(这是XOR交换的事情.请注意,你必须避免与self交换,因为a ^ a == 0.)


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

// test program that reverses its args
#include 

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

为什么,是的,如果输入是borked,这将愉快地交换到该地方之外.

在UNICODE中破坏时的有用链接:http://www.macchiato.com/unicode/chart/

此外,超过0x10000的UTF-8未经测试(因为我似乎没有任何字体,也没有使用hexeditor的耐心)

例子:

#include 
#include 

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


没有充分的理由在混淆的代码竞争之外使用XOR交换.
@Bill,这不是"就地"的通用定义.就地算法*可能*使用额外的内存.但是,这个额外内存的数量不能取决于输入 - 即它必须是常量.因此,使用额外存储交换值完全就位.
1. XOR-swap可以交换任意大小的元素而无需分配任何中间存储.(诚​​然不相关.)2.我喜欢*它.
在现代无序处理器上,XOR交换比通过寄存器交换慢.
你认为"就地"意味着"没有额外的记忆",甚至不是临时的O(1)记忆?str和返回地址的堆栈空间怎么样?
在xor交换消失之前,不要支持这一点.
我要说的是,如果你要求"就地"而不是更具体,它就是xor的东西.其他任何东西都不到位.也就是说,这在任何地方都没有生产代码.如果你甚至想要使用它,现在就退出工程.
作为一名采访者,我*停靠点使用旧的xor-swap技巧.它在现代处理器和任何使用它的人身上效率都低,因为它"整洁"不是一个伟大的程序员 - "好",可能,但不是"伟大的".
我用google搜索"就地字符串反转"*具体*来找到XOR技巧的实现.Haters会讨厌,但我找到了我想要的东西,所以+1来自我.
upvoting,XOR交换应该是低级别的常见做法.这个级别的程序员需要知道这样的技术.
@ sasha.sochka你错了.就地意味着一件事,只有一件事:*常量*内存,而不是O(logn).你得到了它:quicksort*不是*就地.谁声称这是错的.事实上,快速排序的许多(表面上"就地")实现甚至在最坏的情况下使用O(n)额外的内存(因为它们不保证对数递归深度.但就像我说的那样,快速排序实际上不是 - 开始的地方.
@All:Baah ..(不,真的,你太认真了!)@Chris Conway:是的,我应该使用^ =,但我一直在一些对这种事情不屑一顾的独裁统治中闲逛最近...... :-P
@Bill:哦,1.我*测试了它.2.它简洁易读.3.在很大程度上取决于你在做什么以及在什么环境中做什么.(这是更广泛意义上的环境,房屋编码和文档实践等)
我运行了一些测试,似乎应该改变"while(q &&*q)"测试.它应该是"while(*q)"或更改为单独的"if(!q){return;}"(如果意图捕获NULL指针).问题是在NULL的情况下,下一个语句,( - q; p "你必须避免与自己交换,因为'a ^ a == 0`" - 错了.`a ^ a == 0`,但那不是问题,因为那时你会做`a ^(a ^ a)```a ^ 0`是'a`.因此,即使两个swappees(有这样一个单词)相等,XOR交换也能正常工作.

4> slashdottir..:

反转字符串(可视化):

将字符串反转到位



5> Chris Conway..:

非邪恶的C,假设字符串是以空终止的char数组的常见情况:

#include 
#include 

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



6> Nemanja Trif..:

您使用std::reverseC++标准库中的算法.


对.我总是想知道为什么这么多人仍然把它称为"STL",尽管这只会让人感到困惑.如果有更多人喜欢你,并且只是将它称为"C++标准库"或STL并说"STandard Library":)会更好
标准模板库是一个预标准术语.C++标准没有提到它,以前的STL组件在C++标准库中.

7> karlphillip..:

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

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



8> 小智..:

使用STL中的std :: reverse方法:

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

你必须包括"算法"库,#include.



9> Eclipse..:

请注意,std :: reverse的优点在于它适用于char *字符串和std::wstrings以及std::strings

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



10> Juan Pablo 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 Big Endian:

原版的:

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

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

UTF-16 Little Endian:

原版的:

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

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



11> Tim Farley..:

为了完整性,应该指出在各种平台上存在字符串的表示,其中每个字符的字节数根据字符而变化.老派程序员将此称为DBCS(双字节字符集).现代程序员更常见的是UTF-8(以及UTF-16等).还有其他类似的编码.

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

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


std :: reverse不会考虑到这一点.它反转了value_type的.在std :: string的情况下,它会反转char的.不是人物.

12> pprzemek..:

另一种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

推荐阅读
U友50081205_653
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有