Как перевернуть строку в C или C ++, не требуя отдельного буфера для хранения перевернутой строки?
Как перевернуть строку в C или C ++, не требуя отдельного буфера для хранения перевернутой строки?
Ответы:
Стандартный алгоритм состоит в том, чтобы использовать указатели на начало / конец и перемещать их внутрь, пока они не встретятся или не пересекаются в середине. Поменяйтесь, как вы идете.
Обратная строка ASCII, то есть массив с нулем в конце, где каждый символ помещается в 1 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 настоятельно не рекомендуется ; его трудно читать, а код компилируется менее эффективно. в проводнике компилятора Godbolt можно увидеть, насколько сложнее тело цикла asm, когда xor-swap скомпилирован для x86-64 с помощью gcc -O3.)
(Это вещь XOR-swap. Обратите внимание, что вы должны избегать обмена с self, потому что если *p
и если *q
это одно и то же место, вы обнуляете его с помощью ^ a == 0. XOR-swap зависит от наличия двух разных местоположений, используя их каждый как временное хранилище.)
Примечание редактора: вы можете заменить SWP на безопасную встроенную функцию, используя переменную 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;
}
Примеры:
$ ./strrev Räksmörgås ░▒▓○◔◑◕●
░▒▓○◔◑◕● ●◕◑◔○▓▒░
Räksmörgås sågrömskäR
./strrev verrts/.
#include <algorithm>
std::reverse(str.begin(), str.end());
Это самый простой способ в C ++.
Читайте Керниган и Ричи
#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
должна быть объявлена в виде массива. Другими словами, char s[] = "this is ok"
а не char *s="cannot do this"
потому, что последнее приводит к строковой константе, которая не может быть изменена
Переверните строку на месте (визуализация):
Не злой 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--;
}
}
int temp
, это решение выглядит лучше всего. +1
!(*str)
хотя.
strchr()
ищу '\0'
. Я думал об обоих. Не нужно для петли.
Вы используете std::reverse
алгоритм из стандартной библиотеки 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);
size_t
хоть.
Используйте метод std :: reverse из STL :
std::reverse(str.begin(), str.end());
Вы должны включить «алгоритм» библиотеку #include<algorithm>
.
Обратите внимание, что прелесть std :: reverse заключается в том, что он работает со char *
строками и std::wstring
s так же хорошо, как и std::string
s
void strrev(char *str)
{
if (str == NULL)
return;
std::reverse(str, str + strlen(str));
}
*
тип по типу, а скорее ставит его по имени. Чудесно. Я признаю , что я что - то пурист , но меня передергивает каждый раз , когда я вижу подобный код char* str;
.
Если вы ищете реверсивные буферы с терминацией NULL, большинство решений, размещенных здесь, в порядке. Но, как уже указывал Тим Фарли, эти алгоритмы будут работать, только если допустимо предположить, что строка является семантически массивом байтов (то есть однобайтовых строк), что, как мне кажется, является неверным предположением.
Взять, к примеру, строку «año» (год по-испански).
Кодовые точки Unicode: 0x61, 0xf1, 0x6f.
Рассмотрим некоторые из наиболее часто используемых кодировок:
Latin1 / iso-8859-1 (однобайтовая кодировка, 1 символ - 1 байт и наоборот):
Оригинал:
0x61, 0xf1, 0x6f, 0x00
Обеспечить регресс:
0x6f, 0xf1, 0x61, 0x00
Результат в порядке
UTF-8:
Оригинал:
0x61, 0xc3, 0xb1, 0x6f, 0x00
Обеспечить регресс:
0x6f, 0xb1, 0xc3, 0x61, 0x00
Результат - бред и недопустимая последовательность UTF-8
UTF-16 Big Endian:
Оригинал:
0x00, 0x61, 0x00, 0xf1, 0x00, 0x6f, 0x00, 0x00
Первый байт будет рассматриваться как NUL-терминатор. Реверс не будет.
UTF-16 Little Endian:
Оригинал:
0x61, 0x00, 0xf1, 0x00, 0x6f, 0x00, 0x00, 0x00
Второй байт будет рассматриваться как NUL-терминатор. Результат будет 0x61, 0x00, строка, содержащая символ «a».
В интересах полноты следует указать, что существуют представления строк на различных платформах, в которых число байтов на символ изменяется в зависимости от символа. Программисты старой школы называют это DBCS (двухбайтовый набор символов) . Современные программисты чаще сталкиваются с этим в UTF-8 (а также в UTF-16 и других). Есть и другие подобные кодировки.
В любой из этих схем кодирования с переменной шириной простые выложенные здесь алгоритмы ( злые , не злые или иные ) не будут работать правильно вообще! Фактически, они могут даже привести к тому, что строка станет неразборчивой или даже недопустимой строкой в этой схеме кодирования. Посмотрите ответ Хуана Пабло Калифано для некоторых хороших примеров.
std :: reverse () потенциально все равно будет работать в этом случае, если реализация Стандартной библиотеки C ++ (в частности, итераторы строк) на вашей платформе должным образом учитывает это.
Другой способ C ++ (хотя я бы, вероятно, сам использовал std :: reverse () :) как более выразительный и быстрый)
str = std::string(str.rbegin(), str.rend());
Способ C (более или менее :)) и, пожалуйста, будьте осторожны с трюком 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
strlen
чтобы найти конец строки, если он потенциально длинный. Хорошая реализация библиотеки будет использовать векторы SIMD для поиска быстрее, чем 1 байт за итерацию. Но для очень коротких строк while (*++end);
это будет сделано до того, как вызов библиотечной функции начнет поиск.
_mm_shuffle_epi8
(PSHUFB) может изменить порядок вектора 16B, учитывая правильный вектор управления тасованием. Вероятно, он может работать почти на скорости memcpy с некоторой тщательной оптимизацией, особенно с AVX2.
char* beg = s-1
имеет неопределенное поведение (по крайней мере, если s
указывает на первый элемент массива, что является наиболее распространенным случаем). while (*++end);
имеет неопределенное поведение, если s
является пустой строкой.
s-1
которое определило поведение, даже если оно s
указывает на первый элемент массива, так что именно вы должны иметь возможность ссылаться на стандарт в поддержку.
#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
Если вы используете GLib, для этого есть две функции: g_strreverse () и g_utf8_strreverse ()
Мне нравится ответ Евгения 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;
}
Рекурсивная функция, чтобы перевернуть строку на месте (без дополнительного буфера, 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;
}
char
в стек не считается «на месте». Особенно, когда вы нажимаете 4 * 8B на символ (на 64-битной машине: 3 аргумента + обратный адрес).
Поделитесь моим кодом. Как ученик C ++, как вариант использования swap (), я скромно спрашиваю комментарии.
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--);
}
Если вы используете ATL / MFC CString
, просто позвонить CString::MakeReverse()
.
Еще один:
#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");
}
Мой ответ будет похож на большинство из них, но, пожалуйста, найдите мой код здесь.
//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;
}
Я думаю, что есть другой способ перевернуть строку. получить ввод от пользователя и отменить его.
void Rev() {
char ch;
cin.get(ch);
if(ch != '\n') {
Rev();
cout.put(ch);
}
}
Если вам не нужно хранить его, вы можете сократить время, проведенное следующим образом:
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. Сделал это для практики и постарался быть максимально кратким! Вы вводите строку через командную строку, т.е. ./program_name «введите строку здесь»
#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);
}
Но я думаю, что алгоритм обмена XOR является лучшим ...
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()
в условии цикла и в теле цикла может не оптимизироваться.
Вот самый чистый, безопасный и простой способ перевернуть строку в C ++ (на мой взгляд):
#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);
}
Альтернативой является использование std::swap
, но мне нравится определять свои собственные функции - это интересное упражнение, и вам не нужно include
ничего лишнего.
#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 для обращения строки ... И это просто; просто используйте простой указатель, чтобы сделать работу ...