คุณจะย้อนกลับสตริงใน C หรือ C ++ โดยไม่ต้องใช้บัฟเฟอร์ที่แยกต่างหากเพื่อเก็บสายอักขระที่ตรงกันข้ามได้อย่างไร
คุณจะย้อนกลับสตริงใน C หรือ C ++ โดยไม่ต้องใช้บัฟเฟอร์ที่แยกต่างหากเพื่อเก็บสายอักขระที่ตรงกันข้ามได้อย่างไร
คำตอบ:
อัลกอริธึมมาตรฐานคือการใช้พอยน์เตอร์ไปยังจุดเริ่มต้น / สิ้นสุดและเดินเข้าด้านในจนกระทั่งพบหรือข้ามตรงกลาง สลับที่คุณไป
สตริง ASCII ย้อนกลับเช่นอาร์เรย์ 0 สิ้นสุดที่ทุกเหมาะกับตัวละครใน char
1 (หรือชุดอักขระที่ไม่ใช่มัลติไบต์อื่น ๆ )
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 compiler explorerความซับซ้อนของ asm loop body มากขึ้นเมื่อ xor-swap ถูกคอมไพล์สำหรับ x86-64 ด้วย gcc -O3)
(นี่คือสิ่งที่ XOR-swap โปรดทราบว่าคุณต้องหลีกเลี่ยงการแลกเปลี่ยนด้วยตนเองเพราะถ้า*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 ++
อ่าน Kernighan และ 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
จะต้องประกาศในรูปแบบอาร์เรย์ กล่าวอีกนัยหนึ่งchar s[] = "this is ok"
แทนที่จะchar *s="cannot do this"
เป็นผลลัพธ์หลังในค่าคงที่สตริงซึ่งไม่สามารถแก้ไขได้
ย้อนกลับสตริงในสถานที่ (การสร้างภาพ):
Non-evil C สมมติกรณีทั่วไปที่สตริงเป็นchar
อาร์เรย์ที่สิ้นสุดด้วยค่า 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
วิธีนี้อาจดูดีที่สุด +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 method จาก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 โซลูชันส่วนใหญ่ที่โพสต์ที่นี่ก็โอเค แต่ตามที่ Tim Farley ชี้ให้เห็นแล้วอัลกอริธึมเหล่านี้จะใช้งานได้ก็ต่อเมื่อมันถูกต้องที่จะสมมติว่าสตริงนั้นมีความหมายเป็นอาร์เรย์ของไบต์ (เช่นสตริงไบต์เดียว) ซึ่งเป็นข้อสันนิษฐานที่ผิด
ยกตัวอย่างเช่นสตริง "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-terminator จะไม่มีการย้อนกลับ
UTF-16 Little Endian:
เดิม:
0x61, 0x00, 0xf1, 0x00, 0x6f, 0x00, 0x00, 0x00
ไบต์ที่สองจะถูกใช้เป็น NUL-terminator ผลลัพธ์จะเป็น 0x61, 0x00 ซึ่งเป็นสตริงที่มีอักขระ 'a'
ในความสนใจของความสมบูรณ์มันควรจะชี้ให้เห็นว่ามีการแสดงออกของสตริงบนแพลตฟอร์มต่างๆที่จำนวนไบต์ต่อตัวละครแตกต่างกันไปขึ้นอยู่กับตัวละคร โปรแกรมเมอร์เก่าโรงเรียนจะอ้างถึงนี้เป็นDBCS (Double ชุดอักขระไบต์) โปรแกรมเมอร์สมัยใหม่มักพบสิ่งนี้ในUTF-8 (เช่นเดียวกับUTF-16และอื่น ๆ ) มีการเข้ารหัสอื่น ๆ เช่นกัน
ในใด ๆ ของตัวแปรความกว้างเหล่านี้รูปแบบการเข้ารหัสขั้นตอนวิธีการง่าย ๆ ที่โพสต์ได้ที่นี่ ( ความชั่วร้าย , ไม่ใช่ความชั่วร้ายหรือเป็นอย่างอื่น ) จะไม่ทำงานอย่างถูกต้องที่ทุกคน! ในความเป็นจริงพวกเขาอาจทำให้สายอักขระกลายเป็นอ่านไม่ออกหรือแม้แต่สายอักขระที่ผิดกฎหมายในรูปแบบการเข้ารหัสนั้น ดูคำตอบของ Juan Pablo Califanoสำหรับตัวอย่างที่ดี
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 ของ Evgeny อย่างไรก็ตามมันเป็นเรื่องดีที่ได้เห็นรุ่นที่ใช้ตัวชี้ ไม่เช่นนั้นมันจะเหมือนกัน:
#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 args + a address return)
แบ่งปันรหัสของฉัน ในฐานะผู้เรียน 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");
}
ด้วยแลมบ์ดา C ++:
auto reverse = [](std::string& s) -> std::string {
size_t start = 0, end = s.length() -1;
char temp;
while (start < end) {
temp = s[start];
s[start++] = s[end];
s[end--] = temp;
}
return s;
};
คำตอบของฉันจะคล้ายกับส่วนใหญ่ แต่โปรดหารหัสของฉันที่นี่
//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 สำหรับการย้อนกลับสตริง ... และมันก็เป็นเรื่องง่าย เพียงใช้ตัวชี้แบบง่าย ๆ ในการทำงาน ...