Comment inverser une chaîne en C ou C ++ sans avoir besoin d'un tampon séparé pour contenir la chaîne inversée?
Comment inverser une chaîne en C ou C ++ sans avoir besoin d'un tampon séparé pour contenir la chaîne inversée?
Réponses:
L'algorithme standard consiste à utiliser des pointeurs vers le début / la fin et à les guider vers l'intérieur jusqu'à ce qu'ils se rencontrent ou se croisent au milieu. Échangez au fur et à mesure.
Chaîne ASCII inversée, c'est-à-dire un tableau terminé par 0 où chaque caractère tient dans 1 char
. (Ou d'autres jeux de caractères non multi-octets).
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;
}
Le même algorithme fonctionne pour les tableaux d'entiers de longueur connue, utilisez simplement à la tail = start + length - 1
place de la boucle de recherche de fin.
(Note de l'éditeur: cette réponse utilisait à l'origine l'échange XOR pour cette version simple également. Corrigé pour le bénéfice des futurs lecteurs de cette question populaire. L' échange XOR est fortement déconseillé ; difficile à lire et la compilation de votre code est moins efficace. Vous peut voir sur l'explorateur du compilateur Godbolt à quel point le corps de la boucle asm est plus compliqué lorsque xor-swap est compilé pour x86-64 avec gcc -O3.)
(Ceci est une chose de XOR-swap. Prenez soin de noter que vous devez éviter de permuter avec self, car si *p
et *q
sont le même emplacement, vous le mettrez à zéro avec un ^ a == 0. XOR-swap dépend d'avoir deux emplacements distincts, en les utilisant chacun comme stockage temporaire.)
Note de l'éditeur: vous pouvez remplacer SWP par une fonction en ligne sûre à l'aide d'une variable 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;
}
Exemples:
$ ./strrev Räksmörgås ░▒▓○◔◑◕●
░▒▓○◔◑◕● ●◕◑◔○▓▒░
Räksmörgås sågrömskäR
./strrev verrts/.
#include <algorithm>
std::reverse(str.begin(), str.end());
C'est le moyen le plus simple en C ++.
Lire Kernighan et 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
doit être déclarée sous forme de tableau. En d'autres termes, char s[] = "this is ok"
plutôt que char *s="cannot do this"
parce que ce dernier produit une constante de chaîne qui ne peut pas être modifiée
Inverser une chaîne en place (visualisation):
Non-evil C, en supposant le cas courant où la chaîne est un char
tableau terminé par 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
- être , cette solution semble la meilleure. +1
!(*str)
bien.
strchr()
recherche '\0'
. J'ai pensé aux deux. Pas besoin de boucle.
Vous utilisez l' std::reverse
algorithme de la bibliothèque standard C ++.
Cela fait un moment et je ne me souviens plus quel livre m'a appris cet algorithme, mais j'ai trouvé que c'était assez ingénieux et simple à comprendre:
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
argument.
Utilisez la méthode std :: reverse de STL :
std::reverse(str.begin(), str.end());
Vous devrez inclure la bibliothèque « algorithme », #include<algorithm>
.
Notez que la beauté de std :: reverse est qu'il fonctionne avec les char *
chaînes et std::wstring
s aussi bien que std::string
s
void strrev(char *str)
{
if (str == NULL)
return;
std::reverse(str, str + strlen(str));
}
*
par le type et le met plutôt par son nom. Merveilleux. J'avoue que je suis un peu puriste mais je grince des dents à chaque fois que je vois du code comme char* str;
.
Si vous recherchez l'inversion des tampons terminés par NULL, la plupart des solutions publiées ici sont OK. Mais, comme Tim Farley l'a déjà souligné, ces algorithmes ne fonctionneront que s'il est valide de supposer qu'une chaîne est sémantiquement un tableau d'octets (c'est-à-dire des chaînes à un octet), ce qui est une fausse hypothèse, je pense.
Prenons par exemple la chaîne "año" (année en espagnol).
Les points de code Unicode sont 0x61, 0xf1, 0x6f.
Considérez certains des encodages les plus utilisés:
Latin1 / iso-8859-1 (codage sur un octet, 1 caractère équivaut à 1 octet et vice versa):
Original:
0x61, 0xf1, 0x6f, 0x00
Inverser:
0x6f, 0xf1, 0x61, 0x00
Le résultat est OK
UTF-8:
Original:
0x61, 0xc3, 0xb1, 0x6f, 0x00
Inverser:
0x6f, 0xb1, 0xc3, 0x61, 0x00
Le résultat est du charabia et une séquence UTF-8 illégale
UTF-16 Big Endian:
Original:
0x00, 0x61, 0x00, 0xf1, 0x00, 0x6f, 0x00, 0x00
Le premier octet sera traité comme un terminateur NUL. Aucune inversion n'aura lieu.
UTF-16 Little Endian:
Original:
0x61, 0x00, 0xf1, 0x00, 0x6f, 0x00, 0x00, 0x00
Le deuxième octet sera traité comme un terminateur NUL. Le résultat sera 0x61, 0x00, une chaîne contenant le caractère «a».
Dans un souci d'exhaustivité, il convient de souligner qu'il existe des représentations de chaînes sur différentes plateformes dans lesquelles le nombre d'octets par caractère varie en fonction du caractère. Les programmeurs de la vieille école appelleraient cela DBCS (jeu de caractères à deux octets) . Les programmeurs modernes rencontrent plus souvent cela dans UTF-8 (ainsi que UTF-16 et autres). Il existe également d'autres codages de ce type.
Dans n'importe lequel de ces schémas d'encodage à largeur variable, les algorithmes simples affichés ici ( mauvais , non-mal ou autre ) ne fonctionneraient pas du tout correctement! En fait, ils pourraient même rendre la chaîne illisible ou même une chaîne illégale dans ce schéma de codage. Voir la réponse de Juan Pablo Califano pour quelques bons exemples.
std :: reverse () fonctionnerait potentiellement toujours dans ce cas, tant que l'implémentation de votre plate-forme de la bibliothèque C ++ standard (en particulier les itérateurs de chaînes) en prendrait correctement en compte.
Une autre façon C ++ (même si j'utiliserais probablement std :: reverse () moi-même :) comme étant plus expressif et plus rapide)
str = std::string(str.rbegin(), str.rend());
La manière C (plus ou moins :)) et s'il vous plaît, faites attention à l'astuce XOR pour l'échange, les compilateurs ne peuvent parfois pas optimiser cela.
Dans ce cas, il est généralement beaucoup plus lent.
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
pour trouver la fin de la chaîne, si elle est potentiellement longue. Une bonne implémentation de bibliothèque utilisera des vecteurs SIMD pour rechercher plus rapidement que 1 octet par itération. Mais pour les chaînes très courtes, while (*++end);
sera effectué avant qu'un appel de fonction de bibliothèque ne commence la recherche.
_mm_shuffle_epi8
(PSHUFB) peut inverser l'ordre d'un vecteur 16B, étant donné le bon vecteur de contrôle de lecture aléatoire. Il peut probablement fonctionner à une vitesse presque memcpy avec une optimisation minutieuse, en particulier avec AVX2.
char* beg = s-1
a un comportement indéfini (du moins s'il s
pointe vers le premier élément d'un tableau, ce qui est le cas le plus courant). while (*++end);
a un comportement indéfini si s
est une chaîne vide.
s-1
a défini le comportement même s'il s
pointe vers le premier élément d'un tableau, c'est donc vous qui devriez être en mesure de citer le standard à l'appui.
#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;
}
Ce code produit cette sortie:
gnitset
a
cba
Si vous utilisez GLib, il a deux fonctions pour cela, g_strreverse () et g_utf8_strreverse ()
J'aime la réponse K&R d'Evgeny. Cependant, il est agréable de voir une version utilisant des pointeurs. Sinon, c'est essentiellement la même chose:
#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;
}
Fonction récursive pour inverser une chaîne en place (pas de tampon supplémentaire, malloc).
Code court et sexy. Mauvaise utilisation de la pile.
#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
sur la pile ne compte pas comme «en place». Surtout pas quand vous poussez réellement 4 * 8B par caractère (sur une machine 64 bits: 3 arguments + une adresse de retour).
Partagez mon code. En tant qu'apprenant C ++, comme option pour utiliser swap (), je demande humblement des commentaires.
void reverse(char* str) {
int length = strlen(str);
char* str_head = str;
char* str_tail = &str[length-1];
while (str_head < str_tail)
swap(*str_head++, *str_tail--);
}
Si vous utilisez ATL / MFC CString
, appelez simplement CString::MakeReverse()
.
Encore un autre:
#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");
}
Ma réponse serait similaire à la plupart d'entre eux, mais veuillez trouver mon code ici.
//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;
}
Je pense qu'il existe une autre façon d'inverser la chaîne. obtenir l'entrée de l'utilisateur et l'inverser.
void Rev() {
char ch;
cin.get(ch);
if(ch != '\n') {
Rev();
cout.put(ch);
}
}
Si vous n'avez pas besoin de le stocker, vous pouvez réduire le temps passé comme ceci:
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");
}
Voici mon point de vue sur C. Je l'ai fait pour la pratique et j'ai essayé d'être aussi concis que possible! Vous entrez une chaîne via la ligne de commande, c'est-à-dire ./program_name "enter string here"
#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);
}
Mais je pense que l'algorithme d'échange XOR est le meilleur ...
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()
dans la condition de boucle et dans le corps de la boucle peut ne pas être optimisé.
Voici le moyen le plus propre, le plus sûr et le plus simple d'inverser une chaîne en C ++ (à mon avis):
#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);
}
Une alternative est d'utiliser std::swap
, mais j'aime définir mes propres fonctions - c'est un exercice intéressant et vous n'avez besoin de include
rien de plus.
#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;
}
C'est du code optimisé en langage C pour inverser une chaîne ... Et c'est simple; utilisez simplement un simple pointeur pour faire le travail ...