186

反転した文字列を保持する別のバッファを必要とせずに、C または C++ で文字列を反転するにはどうすればよいですか?

4

20 に答える 20

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

これは、C++ で最も簡単な方法です。

于 2008-10-13T16:39:29.277 に答える
167

カーニハンとリッチーを読む

#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;
    }
}
于 2008-10-14T03:09:24.823 に答える
130

標準的なアルゴリズムは、始点/終点へのポインターを使用し、それらが途中で出会うか交差するまで内側に向かって歩きます。あなたが行くように交換してください。


逆 ASCII 文字列、つまり、すべての文字が 1 に収まる 0 で終わる配列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 スワップを使用していました。この人気のある質問の将来の読者のために修正されました 。XOR スワップは強くお勧めしません。読みにくく、コードのコンパイル効率が低下します。xor-swap を x86-64 用に gcc -O3 でコンパイルすると、asm ループ本体がどれほど複雑になるか、Godbolt コンパイラ エクスプローラで確認できます。)


では、UTF-8 文字を修正しましょう...

(これは XOR スワップのことです。self とのスワップを避ける必要があることに注意してください。*p*qが同じ場所である場合、a^a==0 でゼロにするためです。XOR スワップは、2 つの異なる場所を持つことに依存します。それぞれを一時的なストレージとして使用します。)

編集者注: 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 はテストされていません (フォントがないようで、hexeditor を使用する忍耐もないようです)。

例:

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

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

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

./strrev verrts/.
于 2008-10-13T16:58:34.760 に答える
43

文字列が null で終わる配列である一般的なケースを想定して、悪意のない C 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--;
  }
}
于 2008-10-13T17:00:05.553 に答える
31

しばらく経ちましたが、どの本でこのアルゴリズムを学んだか覚えていませんが、非常に独創的で理解しやすいと思いました。

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の厚意による、このアルゴリズムの視覚化:

文字列をその場で逆にするアルゴリズムの視覚化

于 2011-07-03T00:16:19.170 に答える
23

Note that the beauty of std::reverse is that it works with char * strings and std::wstrings just as well as std::strings

void strrev(char *str)
{
    if (str == NULL)
        return;
    std::reverse(str, str + strlen(str));
}
于 2008-10-13T18:26:36.157 に答える
12

NULL で終了するバッファを逆にする場合は、ここに掲載されているほとんどのソリューションで問題ありません。しかし、Tim Farley がすでに指摘したように、これらのアルゴリズムは、文字列が意味的にバイトの配列 (つまり、1 バイト文字列) であると仮定することが有効な場合にのみ機能します。これは間違った仮定だと思います。

たとえば、文字列 "año" (スペイン語で年) を考えてみましょう。

Unicode コード ポイントは 0x61、0xf1、0x6f です。

最も使用されているエンコーディングのいくつかを検討してください。

Latin1 / iso-8859-1 (1 バイト エンコーディング、1 文字は 1 バイト、またはその逆):

オリジナル:

0x61、0xf1、0x6f、0x00

逆行する:

0x6f、0xf1、0x61、0x00

結果はOKです

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

2 番目のバイトは NUL ターミネータとして扱われます。結果は、文字「a」を含む文字列 0x61, 0x00 になります。

于 2008-10-13T18:55:29.830 に答える
12

完全を期すために、さまざまなプラットフォームに文字列の表現があり、文字ごとのバイト数が文字によって異なることを指摘しておく必要があります。昔ながらのプログラマーは、これをDBCS (Double Byte Character Set)と呼んでいました。現代のプログラマーは、UTF-8 (およびUTF-16など) でこれに遭遇することがよくあります。このようなエンコーディングは他にもあります。

これらの可変幅エンコーディング スキームのいずれにおいても、ここに掲載されている単純なアルゴリズム (非悪、またはそれ以外) はまったく正しく機能しません! 実際、文字列が判読不能になったり、そのエンコーディング スキームで不正な文字列になったりする可能性さえあります。いくつかの良い例については、 Juan Pablo Califano の回答を参照してください。

std::reverse() は、プラットフォームの標準 C++ ライブラリ (特に文字列反復子) の実装がこれを適切に考慮している限り、この場合でも機能する可能性があります。

于 2008-10-13T18:05:57.243 に答える
8

別の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
于 2012-09-28T13:10:54.460 に答える
6
#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
于 2008-10-13T16:36:32.480 に答える
4

文字列を元の場所に戻す再帰関数(余分なバッファなし、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;
}
于 2012-02-22T10:39:46.120 に答える
4

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;
}
于 2011-03-15T17:33:27.910 に答える
4

GLib を使用している場合、g_strreverse()g_utf8_strreverse()の 2 つの関数があります。

于 2008-10-14T05:24:47.780 に答える
2

ATL/MFC を使用している場合はCString、単に を呼び出しますCString::MakeReverse()

于 2013-10-31T21:40:22.267 に答える
0

さらに別の:

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

}
于 2012-06-08T19:40:47.697 に答える