Wie kann man eine Zeichenfolge in C oder C ++ umkehren, ohne dass ein separater Puffer für die umgekehrte Zeichenfolge erforderlich ist?
Wie kann man eine Zeichenfolge in C oder C ++ umkehren, ohne dass ein separater Puffer für die umgekehrte Zeichenfolge erforderlich ist?
Antworten:
Der Standardalgorithmus besteht darin, Zeiger auf den Anfang / das Ende zu verwenden und sie nach innen zu bewegen, bis sie sich in der Mitte treffen oder kreuzen. Tauschen Sie, während Sie gehen.
Reverse ASCII-Zeichenfolge, dh ein Array mit 0-Terminierung, bei dem jedes Zeichen in 1 passt char
. (Oder andere Nicht-Multibyte-Zeichensätze).
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;
}
Der gleiche Algorithmus funktioniert für ganzzahlige Arrays mit bekannter Länge. Verwenden Sie einfach tail = start + length - 1
anstelle der Endfindungsschleife.
(Anmerkung des Herausgebers: Diese Antwort verwendete ursprünglich XOR-Swap auch für diese einfache Version. Behoben zum Nutzen zukünftiger Leser dieser beliebten Frage. XOR-Swap wird dringend nicht empfohlen ; schwer zu lesen und das Kompilieren Ihres Codes weniger effizient. Sie kann im Godbolt-Compiler-Explorer sehen, wie viel komplizierter der asm-Schleifenkörper ist, wenn xor-swap für x86-64 mit gcc -O3 kompiliert wird.)
(Dies ist eine XOR-Swap-Sache. Beachten Sie, dass Sie das Tauschen mit sich selbst vermeiden müssen , denn wenn *p
und an *q
derselben Stelle, werden Sie sie mit einem ^ a == 0 auf Null setzen. XOR-Swap hängt von zwei unterschiedlichen Orten ab. jeweils als temporäre Speicherung verwenden.)
Anmerkung des Herausgebers: Sie können SWP mithilfe einer tmp-Variablen durch eine sichere Inline-Funktion ersetzen.
#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;
}
Beispiele:
$ ./strrev Räksmörgås ░▒▓○◔◑◕●
░▒▓○◔◑◕● ●◕◑◔○▓▒░
Räksmörgås sågrömskäR
./strrev verrts/.
#include <algorithm>
std::reverse(str.begin(), str.end());
Dies ist der einfachste Weg in C ++.
Lesen Sie Kernighan und 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;
}
}
s
in einer Array-Form deklariert werden muss. Mit anderen Worten, char s[] = "this is ok"
anstatt char *s="cannot do this"
weil letztere zu einer Zeichenfolgenkonstante führt, die nicht geändert werden kann
Umkehren einer Zeichenfolge an Ort und Stelle (Visualisierung):
Nicht böses C, unter der Annahme, dass die Zeichenfolge ein nullterminiertes char
Array ist:
#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--;
}
}
int temp
sieht diese Lösung am besten aus. +1
!(*str)
obwohl entfernen .
strchr()
Suche nach '\0'
. Ich dachte an beides. Keine Notwendigkeit für eine Schleife.
Sie verwenden einen std::reverse
Algorithmus aus der C ++ Standard Library.
Es ist eine Weile her und ich erinnere mich nicht, welches Buch mir diesen Algorithmus beigebracht hat, aber ich fand ihn ziemlich genial und einfach zu verstehen:
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);
size_t
obwohl sein.
Verwenden Sie die Methode std :: reverse von STL :
std::reverse(str.begin(), str.end());
Sie müssen die "Algorithmus" -Bibliothek einschließen #include<algorithm>
.
Beachten Sie, dass das Schöne an std :: reverse ist, dass es mit char *
Strings und std::wstring
s genauso gut funktioniert wie mit std::string
s
void strrev(char *str)
{
if (str == NULL)
return;
std::reverse(str, str + strlen(str));
}
*
nach Typ und sondern nach Namen schreibt . Wunderbar. Ich gebe zu, dass ich so etwas wie ein Purist bin, aber ich erschrecke jedes Mal, wenn ich Code wie sehe char* str;
.
Wenn Sie nach NULL-terminierten Puffern suchen, sind die meisten hier veröffentlichten Lösungen in Ordnung. Aber, wie Tim Farley bereits betont hat, funktionieren diese Algorithmen nur, wenn angenommen werden kann, dass eine Zeichenfolge semantisch ein Array von Bytes (dh Einzelbyte-Zeichenfolgen) ist, was meiner Meinung nach eine falsche Annahme ist.
Nehmen Sie zum Beispiel die Zeichenfolge "año" (Jahr auf Spanisch).
Die Unicode-Codepunkte sind 0x61, 0xf1, 0x6f.
Betrachten Sie einige der am häufigsten verwendeten Codierungen:
Latin1 / iso-8859-1 ( Einzelbyte- Codierung, 1 Zeichen ist 1 Byte und umgekehrt):
Original:
0x61, 0xf1, 0x6f, 0x00
Umkehren:
0x6f, 0xf1, 0x61, 0x00
Das Ergebnis ist OK
UTF-8:
Original:
0x61, 0xc3, 0xb1, 0x6f, 0x00
Umkehren:
0x6f, 0xb1, 0xc3, 0x61, 0x00
Das Ergebnis ist Kauderwelsch und eine illegale UTF-8-Sequenz
UTF-16 Big Endian:
Original:
0x00, 0x61, 0x00, 0xf1, 0x00, 0x6f, 0x00, 0x00
Das erste Byte wird als NUL-Terminator behandelt. Es findet keine Umkehrung statt.
UTF-16 Little Endian:
Original:
0x61, 0x00, 0xf1, 0x00, 0x6f, 0x00, 0x00, 0x00
Das zweite Byte wird als NUL-Terminator behandelt. Das Ergebnis ist 0x61, 0x00, eine Zeichenfolge, die das Zeichen 'a' enthält.
Der Vollständigkeit halber sei darauf hingewiesen, dass es auf verschiedenen Plattformen Darstellungen von Zeichenfolgen gibt, bei denen die Anzahl der Bytes pro Zeichen je nach Zeichen variiert . Old-School-Programmierer würden dies als DBCS (Double Byte Character Set) bezeichnen . Moderne Programmierer begegnen diesem häufiger in UTF-8 (sowie in UTF-16 und anderen). Es gibt auch andere solche Codierungen.
In jedem dieser Codierungsschemata mit variabler Breite würden die hier veröffentlichten einfachen Algorithmen ( böse , nicht böse oder auf andere Weise ) überhaupt nicht richtig funktionieren! Tatsächlich könnten sie sogar dazu führen, dass die Zeichenfolge unleserlich wird oder sogar eine unzulässige Zeichenfolge in diesem Codierungsschema. In der Antwort von Juan Pablo Califano finden Sie einige gute Beispiele.
std :: reverse () würde in diesem Fall möglicherweise immer noch funktionieren, solange die Implementierung der Standard-C ++ - Bibliothek durch Ihre Plattform (insbesondere String-Iteratoren) dies ordnungsgemäß berücksichtigt.
Ein anderer C ++ - Weg (obwohl ich wahrscheinlich selbst std :: reverse () verwenden würde :) als ausdrucksstärker und schneller)
str = std::string(str.rbegin(), str.rend());
Der C-Weg (mehr oder weniger :)) und bitte seien Sie vorsichtig mit dem XOR-Trick zum Tauschen, Compiler können das manchmal nicht optimieren.
In diesem Fall ist es normalerweise viel langsamer.
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
strlen
, um das Ende der Zeichenfolge zu finden, wenn es möglicherweise lang ist. Eine gute Bibliotheksimplementierung verwendet SIMD-Vektoren, um schneller als 1 Byte pro Iteration zu suchen. Bei sehr kurzen Zeichenfolgen while (*++end);
wird dies jedoch durchgeführt, bevor ein Bibliotheksfunktionsaufruf mit der Suche beginnt.
_mm_shuffle_epi8
(PSHUFB) die Reihenfolge eines 16B-Vektors umkehren, wenn der richtige Shuffle-Kontrollvektor gegeben ist. Es kann wahrscheinlich mit einer gewissen Geschwindigkeit mit nahezu sorgfältiger Optimierung ausgeführt werden, insbesondere mit AVX2.
char* beg = s-1
hat undefiniertes Verhalten (zumindest wenn es s
auf das erste Element eines Arrays zeigt, was der häufigste Fall ist). while (*++end);
hat undefiniertes Verhalten, wenn s
es sich um eine leere Zeichenfolge handelt.
s-1
das Verhalten definiert wurde, auch wenn es s
auf das erste Element eines Arrays verweist. Sie sollten also in der Lage sein, den Standard zur Unterstützung zu zitieren.
#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;
}
Dieser Code erzeugt diese Ausgabe:
gnitset
a
cba
Wenn Sie GLib verwenden, hat es zwei Funktionen dafür, g_strreverse () und g_utf8_strreverse ()
Ich mag Evgenys K & R-Antwort. Es ist jedoch schön, eine Version mit Zeigern zu sehen. Ansonsten ist es im Wesentlichen dasselbe:
#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;
}
Rekursive Funktion zum Umkehren einer Zeichenfolge (kein zusätzlicher Puffer, malloc).
Kurzer, sexy Code. Schlechte, schlechte Stapelnutzung.
#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;
}
char
auf den Stapel zählt nicht als "an Ort und Stelle". Vor allem nicht, wenn Sie tatsächlich 4 * 8B pro Zeichen drücken (auf einem 64-Bit-Computer: 3 Argumente + eine Absenderadresse).
Wenn Sie ATL / MFC verwenden CString
, rufen Sie einfach an CString::MakeReverse()
.
Noch ein anderer:
#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");
}
Meine Antwort wäre den meisten ähnlich, aber bitte finden Sie meinen Code hier.
//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;
}
Ich denke, es gibt eine andere Möglichkeit, die Zeichenfolge umzukehren. Holen Sie sich die Eingabe vom Benutzer und kehren Sie sie um.
void Rev() {
char ch;
cin.get(ch);
if(ch != '\n') {
Rev();
cout.put(ch);
}
}
Wenn Sie es nicht speichern müssen, können Sie den Zeitaufwand folgendermaßen reduzieren:
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");
}
Hier ist meine Einstellung zu C. Habe es zum Üben gemacht und versucht, so präzise wie möglich zu sein! Sie geben eine Zeichenfolge über die Befehlszeile ein, dh ./Programmname "Geben Sie hier eine Zeichenfolge ein"
#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);
}
Aber ich denke, der XOR-Swap-Algorithmus ist der beste ...
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;
}
strlen()
in der Schleifenbedingung und im Schleifenkörper wird möglicherweise nicht optimiert.
Hier ist der sauberste, sicherste und einfachste Weg, einen String in C ++ umzukehren (meiner Meinung nach):
#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);
}
Eine Alternative ist die Verwendung std::swap
, aber ich mag es, meine eigenen Funktionen zu definieren - es ist eine interessante Übung und Sie brauchen include
nichts extra.
#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;
}
Dies ist optimierter Code in der Sprache C zum Umkehren einer Zeichenfolge ... Und es ist einfach; Verwenden Sie einfach einen einfachen Zeiger, um die Arbeit zu erledigen ...