Come invertire una stringa in C o C ++ senza richiedere un buffer separato per contenere la stringa invertita?
Come invertire una stringa in C o C ++ senza richiedere un buffer separato per contenere la stringa invertita?
Risposte:
L'algoritmo standard prevede l'uso dei puntatori all'inizio / alla fine e li guida verso l'interno fino a quando si incontrano o si incrociano nel mezzo. Scambia mentre vai.
Stringa ASCII inversa, ovvero un array con terminazione 0 in cui ogni carattere rientra in 1 char
. (O altri set di caratteri non multibyte).
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;
}
Lo stesso algoritmo funziona per array di numeri interi con lunghezza nota, basta usare tail = start + length - 1
invece il ciclo di ricerca finale.
(Nota del redattore: questa risposta originariamente utilizzava XOR-swap anche per questa versione semplice. Risolto per il beneficio dei futuri lettori di questa domanda popolare. XOR-swap non è altamente raccomandato ; difficile da leggere e rendere la compilazione del codice meno efficiente. può vedere sull'esploratore del compilatore Godbolt quanto è più complicato il corpo del loop asm quando xor-swap viene compilato per x86-64 con gcc -O3.)
(Questo è XOR-swap. Fai attenzione a notare che devi evitare di scambiare con te stesso, perché se *p
e *q
nella stessa posizione lo azzererai con un ^ a == 0. XOR-swap dipende dall'avere due posizioni distinte, usandoli ciascuno come memoria temporanea.)
Nota dell'editore: puoi sostituire SWP con una funzione inline sicura usando una variabile tmp.
#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;
}
Esempi:
$ ./strrev Räksmörgås ░▒▓○◔◑◕●
░▒▓○◔◑◕● ●◕◑◔○▓▒░
Räksmörgås sågrömskäR
./strrev verrts/.
#include <algorithm>
std::reverse(str.begin(), str.end());
Questo è il modo più semplice in C ++.
Leggi Kernighan e 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
deve essere dichiarata in una forma di matrice. In altre parole, char s[] = "this is ok"
anziché char *s="cannot do this"
perché quest'ultima si
Invertire una stringa in posizione (visualizzazione):
C non male, presupponendo il caso comune in cui la stringa è un char
array con terminazione null :
#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
, questa soluzione sembra migliore. +1
!(*str)
però.
strchr()
alla ricerca '\0'
. Ho pensato a entrambi. Non è necessario un loop.
Si utilizza l' std::reverse
algoritmo dalla libreria standard C ++.
È passato un po 'di tempo e non ricordo quale libro mi abbia insegnato questo algoritmo, ma ho pensato che fosse abbastanza ingegnoso e semplice da capire:
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
pensiero.
Utilizzare il metodo std :: reverse da STL :
std::reverse(str.begin(), str.end());
Si dovrà includere la libreria "algoritmo", #include<algorithm>
.
Nota che la bellezza di std :: reverse è che funziona con char *
stringhe e std::wstring
s così come con std::string
s
void strrev(char *str)
{
if (str == NULL)
return;
std::reverse(str, str + strlen(str));
}
*
per il tipo e piuttosto lo chiama per nome. Meraviglioso. Devo ammettere di essere un purista, ma rabbrividisco ogni volta che vedo un codice simile char* str;
.
Se stai cercando di invertire i buffer terminati NULL, la maggior parte delle soluzioni pubblicate qui sono OK. Ma, come ha già sottolineato Tim Farley, questi algoritmi funzioneranno solo se è valido supporre che una stringa sia semanticamente una matrice di byte (ovvero stringhe a byte singolo), il che è un presupposto errato, credo.
Prendiamo ad esempio la stringa "año" (anno in spagnolo).
I punti di codice Unicode sono 0x61, 0xf1, 0x6f.
Considera alcune delle codifiche più utilizzate:
Latin1 / iso-8859-1 (codifica a byte singolo, 1 carattere è 1 byte e viceversa):
Originale:
0x61, 0xf1, 0x6f, 0x00
Inversione:
0x6f, 0xf1, 0x61, 0x00
Il risultato è OK
UTF-8:
Originale:
0x61, 0xc3, 0xb1, 0x6f, 0x00
Inversione:
0x6f, 0xb1, 0xc3, 0x61, 0x00
Il risultato è incomprensibile e una sequenza UTF-8 illegale
UTF-16 Big Endian:
Originale:
0x00, 0x61, 0x00, 0xf1, 0x00, 0x6f, 0x00, 0x00
Il primo byte verrà trattato come un terminatore NUL. Non si verificherà alcuna inversione.
UTF-16 Little Endian:
Originale:
0x61, 0x00, 0xf1, 0x00, 0x6f, 0x00, 0x00, 0x00
Il secondo byte verrà trattato come un terminatore NUL. Il risultato sarà 0x61, 0x00, una stringa contenente il carattere 'a'.
Nell'interesse della completezza, si dovrebbe sottolineare che ci sono rappresentazioni di stringhe su varie piattaforme in cui il numero di byte per carattere varia a seconda del carattere. I programmatori della vecchia scuola si riferirebbero a questo come DBCS (Double Byte Character Set) . I programmatori moderni lo incontrano più comunemente in UTF-8 (così come in UTF-16 e altri). Esistono anche altre codifiche simili.
In uno qualsiasi di questi schemi di codifica a larghezza variabile, i semplici algoritmi pubblicati qui ( male , non male o altro ) non funzionerebbero affatto correttamente! In effetti, potrebbero persino rendere la stringa illeggibile o addirittura una stringa illegale in quello schema di codifica. Vedi la risposta di Juan Pablo Califano per alcuni buoni esempi.
std :: reverse () potenzialmente funzionerebbe comunque in questo caso, fintanto che l'implementazione della tua piattaforma C ++ Library standard (in particolare, iteratori di stringhe) lo terrà correttamente in considerazione.
Un altro modo C ++ (anche se probabilmente userei std :: reverse () me stesso :) come più espressivo e più veloce)
str = std::string(str.rbegin(), str.rend());
Nel modo C (più o meno :)) e, per favore, stai attento al trucco XOR per lo scambio, i compilatori a volte non possono ottimizzarlo.
In tal caso, di solito è molto più lento.
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
per trovare la fine della stringa, se è potenzialmente lunga. Una buona implementazione della libreria utilizzerà i vettori SIMD per eseguire ricerche più veloci di 1 byte per iterazione. Ma per stringhe molto brevi, while (*++end);
verrà eseguito prima che inizi una ricerca della funzione di libreria.
_mm_shuffle_epi8
(PSHUFB) può invertire l'ordine di un vettore 16B, dato il giusto vettore di controllo shuffle. Probabilmente può funzionare a velocità quasi memcpy con un'attenta ottimizzazione, specialmente con AVX2.
char* beg = s-1
ha un comportamento indefinito (almeno se s
punta al primo elemento di un array, che è il caso più comune). while (*++end);
ha un comportamento indefinito se s
è una stringa vuota.
s-1
ha definito il comportamento anche se s
punta al primo elemento di un array, quindi sei tu che dovresti essere in grado di citare lo standard nel supporto.
#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;
}
Questo codice produce questo output:
gnitset
a
cba
Nel caso in cui si stia utilizzando GLib, ha due funzioni per questo, g_strreverse () e g_utf8_strreverse ()
Mi piace la risposta K&R di Evgeny. Tuttavia, è bello vedere una versione usando i puntatori. Altrimenti, è essenzialmente lo stesso:
#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;
}
Funzione ricorsiva per invertire una stringa in atto (nessun buffer aggiuntivo, malloc).
Codice breve e sexy. Cattivo, cattivo utilizzo dello stack.
#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
sulla pila non conta come "sul posto". Soprattutto quando stai effettivamente spingendo 4 * 8B per personaggio (su una macchina a 64 bit: 3 arg + un indirizzo di ritorno).
Se stai usando ATL / MFC CString
, chiama semplicemente CString::MakeReverse()
.
Ancora un altro:
#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");
}
La mia risposta sarebbe simile alla maggior parte di loro, ma per favore trova il mio codice qui.
//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;
}
Penso che ci sia un altro modo per invertire la stringa. ottenere l'input dall'utente e invertirlo.
void Rev() {
char ch;
cin.get(ch);
if(ch != '\n') {
Rev();
cout.put(ch);
}
}
Se non è necessario memorizzarlo, è possibile ridurre il tempo trascorso in questo modo:
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");
}
Ecco la mia opinione in C. L'ho fatto per la pratica e ho cercato di essere il più conciso possibile! Si immette una stringa tramite la riga di comando, ad es. Nome_programma / "inserire la stringa qui"
#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);
}
Ma penso che l'algoritmo di scambio XOR sia il migliore ...
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()
nella condizione di loop e nel corpo del loop potrebbe non essere ottimizzata.
Ecco il modo più pulito, più sicuro e più semplice per invertire una stringa in C ++ (secondo me):
#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);
}
Un'alternativa è usare std::swap
, ma mi piace definire le mie funzioni: è un esercizio interessante e non hai bisogno di include
nulla in più.
#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;
}
Questo è un codice ottimizzato nel linguaggio C per invertire una stringa ... Ed è semplice; basta usare un semplice puntatore per fare il lavoro ...