programing

C 또는 C++에서 문자열을 되돌리려면 어떻게 해야 합니까?

minecode 2022. 10. 21. 21:36
반응형

C 또는 C++에서 문자열을 되돌리려면 어떻게 해야 합니까?

C 또는 C++ 의 문자열을 리버스 하려면 , 그 리버스 된 문자열을 보관 유지하는 다른 버퍼를 필요로 하지 않습니다.

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

이것은 C++에서 가장 간단한 방법입니다.

표준 알고리즘은 시작/종료 포인터를 사용하여 중간에서 만나거나 교차할 때까지 포인터를 안쪽으로 걷는 것입니다.이동하면서 교환합니다.


ASCII 문자열. 모든 가 1의 「ASCII」, 「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-swap을 사용했습니다.이 일반적인 질문의 향후 독자를 위해 수정되었습니다.XOR-swap은 권장되지 않습니다.읽기 어렵고 코드 컴파일 효율이 떨어집니다.gcc - O3에서 xor-swap을 x86-64용으로 컴파일하면 Godbolt 컴파일러에서 asm 루프 본문이 얼마나 복잡한지 알 수 있습니다.)


좋아, UTF-8 문자를 고쳐서...

(이것은 XOR 스왑에 관한 것입니다.self 와의 교환은 피하도록 주의해 주세요.*p ★★★★★★★★★★★★★★★★★」*qa^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/
  • 또한 UTF-8 over 0x10000은 테스트되지 않았습니다(글씨도 없고 16진수 에디터를 사용할 수 있는 인내심도 없는 것 같기 때문에).

예:

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

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

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

./strrev verrts/.

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

오랜 시간이 흘렀고 어떤 책이 이 알고리즘을 가르쳐줬는지는 기억나지 않지만, 꽤 기발하고 이해하기 쉬웠다고 생각했습니다.

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를 사용하여 이 알고리즘을 시각화합니다.

문자열을 리버스하기 위한 알고리즘 시각화

다른 C++ 방식(단, std::reverse()를 보다 표현력 있고 고속으로 사용할 수 있습니다.

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

C way ( more or less : )및 스와핑을 위한 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

::reverse라는 std:와 함께 사용할 수 입니다.char *과 '''std::wstring는 「」와 같이 됩니다.std::strings

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

C이 늘 된 non-evil 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--;
  }
}
#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

문자열을 원래 위치로 되돌리는 재귀 함수입니다(추가 버퍼, 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;
}

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

완전성을 확보하기 위해 다양한 플랫폼 상에 문자열의 표현이 존재하며 문자당 바이트 수가 문자에 따라 다르다는 점에 유의해야 합니다.구식 프로그래머들은 이것을 DBCS(Double Byte Character Set)라고 부릅니다.현대의 프로그래머는 UTF-8(UTF-16 등)에서 이 문제를 자주 접하게 됩니다.다른 인코딩도 있습니다.

이러한 가변폭 부호화 방식에서는 여기에 게재되어 있는 단순한 알고리즘(이블, 비이블 또는 기타)은 전혀 올바르게 동작하지 않습니다.실제로 이 경우 해당 인코딩 방식에서 스트링을 판독할 수 없게 되거나 부정한 스트링이 될 수 있습니다.좋은 예시는 후안 파블로 캘리포니아노의 답변을 참고하세요.

std::reverse()는 사용하시는 플랫폼의 표준 C++ 라이브러리 구현(특히 문자열 반복기)이 적절히 고려되고 있는 한, 이 경우에도 동작할 가능성이 있습니다.

NULL 종료된 버퍼를 되돌리는 방법을 찾고 있는 경우, 여기에 기재되어 있는 대부분의 솔루션은 정상입니다.그러나 Tim Farley가 이미 지적한 바와 같이, 이러한 알고리즘은 문자열이 의미론적으로 바이트 배열(즉, 싱글 바이트 문자열)이라고 가정하는 것이 유효한 경우에만 유효합니다.이것은 잘못된 가정이라고 생각합니다.

예를 들어, 문자열 "anno"(스페인어로 year)를 예로 들 수 있습니다.

Unicode 코드 포인트는 0x61, 0xf1, 0x6f 입니다.

가장 많이 사용되는 인코딩을 몇 가지 생각해 보겠습니다.

Latin1 / iso-8859-1 (싱글 바이트 인코딩, 1 문자는 1 바이트, 그 반대):

오리지널:

0x61, 0⁄1, 0x6f, 0x00

역방향:

0x6f, 0µ1, 0x61, 0x00

결과는 OK입니다.

UTF-8:

오리지널:

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

역방향:

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

그 결과 횡설수설하고 UTF-8 시퀀스가 부정하게 됩니다.

UTF-16 Big Endian:

오리지널:

0x00, 0x61, 0x00, 0⁄1, 0x00, 0x6f, 0x00, 0x00

첫 번째 바이트는 NUL 터미네이터로 취급됩니다.후진은 일어나지 않습니다.

UTF-16 리틀 엔디언:

오리지널:

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

두 번째 바이트는 NUL 터미네이터로 취급됩니다.결과는 'a' 문자를 포함하는 문자열인 0x61, 0x00이 됩니다.

ATL/MFC를 사용하는 경우CString, 전화만 하면 됩니다.CString::MakeReverse().

GLib 를 사용하고 있는 경우는, g_strreverse() 와 g_utf8_strreverse() 의 2 개의 함수가 있습니다.

또 다른 것:

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

}

보존할 필요가 없는 경우는, 다음과 같이 시간을 단축할 수 있습니다.

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++에서는 그 반대가 함수에서 수행될 수 있습니다.

#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());
    }
}
#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++ 멀티바이트 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;
}

언급URL : https://stackoverflow.com/questions/198199/how-do-you-reverse-a-string-in-place-in-c-or-c

반응형