¿Cómo invierte una cadena en C o C ++ sin requerir un búfer separado para contener la cadena invertida?
¿Cómo invierte una cadena en C o C ++ sin requerir un búfer separado para contener la cadena invertida?
Respuestas:
El algoritmo estándar es usar punteros al inicio / final, y caminar hacia adentro hasta que se encuentren o se crucen en el medio. Intercambia a medida que avanzas.
Cadena ASCII inversa, es decir, una matriz terminada en 0 donde cada carácter cabe en 1 char
. (U otros conjuntos de caracteres no 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;
}
El mismo algoritmo funciona para matrices de enteros con longitud conocida, solo use en tail = start + length - 1
lugar del ciclo de búsqueda final.
(Nota del editor: esta respuesta originalmente también usó XOR-swap para esta versión simple. Se corrigió para el beneficio de los futuros lectores de esta pregunta popular. XOR-swap no es muy recomendable ; difícil de leer y hacer que su código se compile de manera menos eficiente. Usted puede ver en el explorador del compilador Godbolt lo mucho más complicado que es el cuerpo del bucle asm cuando se compila xor-swap para x86-64 con gcc -O3).
(Esto es algo de intercambio XOR. Tenga en cuenta que debe evitar el intercambio con uno mismo, porque si *p
y *q
es la misma ubicación, lo pondrá a cero con un ^ a == 0. El intercambio XOR depende de tener dos ubicaciones distintas, usándolos cada uno como almacenamiento temporal).
Nota del editor: puede reemplazar SWP con una función en línea segura utilizando una 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;
}
Ejemplos:
$ ./strrev Räksmörgås ░▒▓○◔◑◕●
░▒▓○◔◑◕● ●◕◑◔○▓▒░
Räksmörgås sågrömskäR
./strrev verrts/.
#include <algorithm>
std::reverse(str.begin(), str.end());
Esta es la forma más simple en C ++.
Leer Kernighan y 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
debe declararse en forma de matriz. En otras palabras, en char s[] = "this is ok"
lugar de char *s="cannot do this"
porque esto último resulta en una constante de cadena que no se puede modificar
Invierta una cadena en su lugar (visualización):
C no malvada, suponiendo el caso común donde la cadena es una char
matriz terminada en nulo :
#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
, esta solución se ve mejor. +1
!(*str)
sin embargo.
strchr()
buscando '\0'
. Pensé en ambos. No es necesario un bucle.
Utiliza el std::reverse
algoritmo de la biblioteca estándar de C ++.
Ha pasado un tiempo y no recuerdo qué libro me enseñó este algoritmo, pero pensé que era bastante ingenioso y fácil de entender:
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
pensamiento.
Utilice el método std :: reverse de STL :
std::reverse(str.begin(), str.end());
Usted tendrá que incluir la librería "algoritmo", #include<algorithm>
.
Tenga en cuenta que la belleza de std :: reverse es que funciona con char *
cadenas y std::wstring
s tan bien como std::string
s
void strrev(char *str)
{
if (str == NULL)
return;
std::reverse(str, str + strlen(str));
}
*
el tipo y lo pone por el nombre. Maravilloso. Admito que soy algo purista, pero me estremezco cada vez que veo un código similar char* str;
.
Si está buscando revertir los búferes terminados en NULL, la mayoría de las soluciones publicadas aquí están bien. Pero, como Tim Farley ya señaló, estos algoritmos funcionarán solo si es válido suponer que una cadena es semánticamente una matriz de bytes (es decir, cadenas de un solo byte), lo cual es una suposición incorrecta, creo.
Tomemos, por ejemplo, la cadena "año".
Los puntos de código Unicode son 0x61, 0xf1, 0x6f.
Considere algunas de las codificaciones más utilizadas:
Latin1 / iso-8859-1 (codificación de un solo byte, 1 carácter es 1 byte y viceversa):
Original:
0x61, 0xf1, 0x6f, 0x00
Contrarrestar:
0x6f, 0xf1, 0x61, 0x00
El resultado está bien
UTF-8:
Original:
0x61, 0xc3, 0xb1, 0x6f, 0x00
Contrarrestar:
0x6f, 0xb1, 0xc3, 0x61, 0x00
El resultado es galimatías y una secuencia ilegal UTF-8
UTF-16 Big Endian:
Original:
0x00, 0x61, 0x00, 0xf1, 0x00, 0x6f, 0x00, 0x00
El primer byte se tratará como un terminador NUL. No se realizará ninguna inversión.
UTF-16 Little Endian:
Original:
0x61, 0x00, 0xf1, 0x00, 0x6f, 0x00, 0x00, 0x00
El segundo byte se tratará como un terminador NUL. El resultado será 0x61, 0x00, una cadena que contiene el carácter 'a'.
En aras de la exhaustividad, debe señalarse que hay representaciones de cadenas en varias plataformas en las que el número de bytes por carácter varía según el carácter. Los programadores de la vieja escuela se referirían a esto como DBCS (Conjunto de caracteres de doble byte) . Los programadores modernos suelen encontrar esto en UTF-8 (así como en UTF-16 y otros). También hay otras codificaciones de este tipo.
¡En cualquiera de estos esquemas de codificación de ancho variable, los algoritmos simples publicados aquí ( malvados , no malvados u otros ) no funcionarían correctamente! De hecho, incluso podrían hacer que la cadena se vuelva ilegible o incluso una cadena ilegal en ese esquema de codificación. Vea la respuesta de Juan Pablo Califano para algunos buenos ejemplos.
std :: reverse () podría funcionar en este caso, siempre y cuando la implementación de su plataforma de la Biblioteca estándar de C ++ (en particular, los iteradores de cadenas) lo tenga en cuenta correctamente.
Otra forma de C ++ (aunque probablemente usaría std :: reverse () mismo :) como más expresivo y más rápido)
str = std::string(str.rbegin(), str.rend());
La forma C (más o menos :)) y, por favor, tenga cuidado con el truco XOR para el intercambio, los compiladores a veces no pueden optimizar eso.
En tal caso, suele ser mucho más 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
encontrar el final de la cadena, si es potencialmente larga. Una buena implementación de la biblioteca utilizará vectores SIMD para buscar más rápido que 1 byte por iteración. Pero para cadenas muy cortas, while (*++end);
se realizará antes de que se inicie una búsqueda de función de biblioteca.
_mm_shuffle_epi8
(PSHUFB) puede invertir el orden de un vector 16B, dado el vector de control aleatorio correcto. Probablemente puede ejecutarse a una velocidad casi de memoria con una optimización cuidadosa, especialmente con AVX2.
char* beg = s-1
tiene un comportamiento indefinido (al menos si s
apunta al primer elemento de una matriz, que es el caso más común). while (*++end);
tiene un comportamiento indefinido si s
es una cadena vacía.
s-1
tiene un comportamiento definido, incluso si s
apunta al primer elemento de una matriz, por lo que es usted quien debería poder citar el estándar como soporte.
#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;
}
Este código produce esta salida:
gnitset
a
cba
En caso de que esté utilizando GLib, tiene dos funciones para eso, g_strreverse () y g_utf8_strreverse ()
Me gusta la respuesta de Evgeny K&R. Sin embargo, es bueno ver una versión con punteros. De lo contrario, es esencialmente lo mismo:
#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;
}
Función recursiva para invertir una cadena en su lugar (sin búfer adicional, malloc).
Código corto y sexy. Mal, mal uso de la pila.
#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
en la pila no cuenta como "en su lugar". Especialmente no cuando estás presionando 4 * 8B por personaje (en una máquina de 64 bits: 3 args + una dirección de retorno).
Comparte mi código Como estudiante de C ++, como una opción para usar swap (), estoy pidiendo humildemente comentarios.
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 está utilizando ATL / MFC CString
, simplemente llame CString::MakeReverse()
.
Aún otra:
#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");
}
Mi respuesta sería similar a la mayoría de ellos, pero encuentre mi código aquí.
//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;
}
Creo que hay otra forma de invertir la cadena. obtener la entrada del usuario y revertirla.
void Rev() {
char ch;
cin.get(ch);
if(ch != '\n') {
Rev();
cout.put(ch);
}
}
Si no necesita almacenarlo, puede reducir el tiempo que pasa así:
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");
}
Aquí está mi opinión en C. ¡Lo hice para practicar y traté de ser lo más conciso posible! Usted ingresa una cadena a través de la línea de comando, es decir ./program_name "ingrese la cadena aquí"
#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);
}
Pero creo que el algoritmo de intercambio XOR es el mejor ...
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()
en la condición de bucle y en el cuerpo del bucle podría no optimizarse.
Aquí está la forma más limpia, segura y fácil de revertir una cadena en C ++ (en mi opinión):
#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);
}
Una alternativa es usarla std::swap
, pero me gusta definir mis propias funciones: es un ejercicio interesante y no necesita include
nada más.
#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;
}
Este es un código optimizado en el lenguaje C para invertir una cadena ... Y es simple; solo use un puntero simple para hacer el trabajo ...