Ters çevrilmiş dizeyi tutmak için ayrı bir arabellek gerekmeden C veya C ++ 'da bir dizeyi nasıl ters çevirirsiniz?
Ters çevrilmiş dizeyi tutmak için ayrı bir arabellek gerekmeden C veya C ++ 'da bir dizeyi nasıl ters çevirirsiniz?
Yanıtlar:
Standart algoritma, başlangıç / bitiş işaretçileri kullanmak ve ortada buluşuncaya veya geçene kadar onları içeriye doğru yürümektir. Kullandıkça değiştirin.
Ters ASCII dizesi, yani her karakterin 1'e sığdığı 0 sonlu bir dizi char
. (Veya diğer çok baytlı olmayan karakter kümeleri).
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;
}
Aynı algoritma, bilinen uzunluğa sahip tamsayı dizileri için çalışır, sadece tail = start + length - 1
son bulma döngüsü yerine kullanın .
(Editörün notu: Bu cevap başlangıçta bu basit sürüm için XOR-takasını da kullandı. Bu popüler sorunun gelecekteki okuyucularının yararı için düzeltildi. XOR-takas şiddetle tavsiye edilmez ; okunması zor ve kodunuzu daha az verimli bir şekilde derlemenizi sağlar. görebileceğiniz Godbolt derleyici kaşif üzerinde xor-takas gcc -O3 ile x86-64 için derlenmiş asm döngü gövdesini komplike ne kadar daha fazladır.)
(Bu XOR-takas olayıdır. Benimle takas etmekten kaçınmanız gerektiğine dikkat edin , çünkü eğer *p
ve*q
konumları aynı bir == 0. XOR-takas iki ayrı yerleri olan bağlı olan bir ^ ile sıfır olacak, her birini geçici depolama alanı olarak kullanarak.)
Editörün notu: SWP'yi bir tmp değişkeni kullanarak güvenli bir satır içi işleviyle değiştirebilirsiniz.
#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;
}
Örnekler:
$ ./strrev Räksmörgås ░▒▓○◔◑◕●
░▒▓○◔◑◕● ●◕◑◔○▓▒░
Räksmörgås sågrömskäR
./strrev verrts/.
#include <algorithm>
std::reverse(str.begin(), str.end());
Bu C ++ 'daki en basit yoldur.
Kernighan ve Ritchie'yi okuyun
#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
bir dizi biçiminde bildirilmesi gerektiğini belirtmek önemlidir . Başka bir deyişle, char s[] = "this is ok"
yerine char *s="cannot do this"
değiştirilemez bir dize sabiti içinde son sonuçlara çünkü
Bir dizgiyi ters çevirin (görselleştirme):
Dizenin boş sonlandırılmış bir char
dizi olduğu yaygın durum varsayılırsa kötü olmayan C :
#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
, bu çözüm en iyi görünüyor. +1
!(*str)
olsa da kaldırmak .
strchr()
arıyor '\0'
. İkisini de düşündüm. Döngü gerekmez.
std::reverse
C ++ Standart Kitaplığı'ndan algoritma kullanın .
Bir süre oldu ve hangi kitabın bana bu algoritmayı öğrettiğini hatırlamıyorum, ama bunun oldukça ustaca ve anlaşılması kolay olduğunu düşündüm:
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
olsa gerekir.
Kullanım std :: ters yönteminden STL :
std::reverse(str.begin(), str.end());
"Algoritma" kütüphanesini eklemeniz gerekecek #include<algorithm>
,.
Std :: reverse'ın güzelliğinin, char *
dizelerin ve std::wstring
s'nin yanı sıra std::string
s
void strrev(char *str)
{
if (str == NULL)
return;
std::reverse(str, str + strlen(str));
}
*
tipine göre koymayan ve adıyla koyan C ++ kullandığını görmek güzel, güzel, kesinlikle güzel bir şey . Olağanüstü. Ben saf bir şey olduğumu itiraf ediyorum ama her kod gördüğümde küfrediyorum char* str;
.
NULL sonlandırılmış arabellekleri tersine çevirmek istiyorsanız, burada yayınlanan çözümlerin çoğu TAMAM. Ancak, Tim Farley'in daha önce işaret ettiği gibi, bu algoritmalar sadece bir dizenin anlamsal olarak bir bayt dizisi (yani tek bayt dizeler) olduğunu varsaymak için geçerliyse işe yarayacaktır, bence yanlış.
Örneğin, "año" dizesini (İspanyolca yıl) ele alalım.
Unicode kod noktaları 0x61, 0xf1, 0x6f'dir.
En çok kullanılan kodlamalardan bazılarını düşünün:
Latin1 / iso-8859-1 (tek bayt kodlama, 1 karakter 1 bayt ve tam tersi):
Orijinal:
0x61, 0xf1, 0x6f, 0x00
Tersine çevirmek:
0x6f, 0xf1, 0x61, 0x00
Sonuç iyi
UTF-8:
Orijinal:
0x61, 0xc3, 0xb1, 0x6f, 0x00
Tersine çevirmek:
0x6f, 0xb1, 0xc3, 0x61, 0x00
Sonuç anlamsız ve geçersiz bir UTF-8 dizisidir
UTF-16 Büyük Endian:
Orijinal:
0x00, 0x61, 0x00, 0xf1, 0x00, 0x6f, 0x00, 0x00
İlk bayt, bir NUL sonlandırıcı olarak kabul edilecektir. Tersine çevirme gerçekleşmez.
UTF-16 Küçük Endian:
Orijinal:
0x61, 0x00, 0xf1, 0x00, 0x6f, 0x00, 0x00, 0x00
İkinci bayt, bir NUL sonlandırıcı olarak işlem görecektir. Sonuç, 'a' karakterini içeren bir dize olan 0x61, 0x00 olur.
Bütünlük açısından, karakter başına bayt sayısının karaktere bağlı olarak değiştiği çeşitli platformlarda dizelerin temsili olduğuna dikkat edilmelidir . Eski okul programcıları buna DBCS (Çift Bayt Karakter Kümesi) denir . Modern programcılar bununla birlikte UTF-8'de ( UTF-16 ve diğerleri) daha sık karşılaşırlar . Bunun gibi başka kodlamalar da vardır.
Bu değişken genişlik kodlama şemalarının herhangi birinde, burada yayınlanan basit algoritmalar ( kötü , kötü olmayan veya başka şekilde ) doğru şekilde çalışmaz! Aslında, dizenin okunaksız olmasına ve hatta kodlama şemasında yasadışı bir dizeye bile neden olabilirler. Bazı iyi örnekler için Juan Pablo Califano'nun cevabına bakınız .
std :: reverse (), platformunuzun Standart C ++ Kitaplığı'nı (özellikle dize yineleyicileri) düzgün bir şekilde hesaba kattığı sürece bu durumda potansiyel olarak hala çalışır.
Başka bir C ++ yolu (muhtemelen daha anlamlı ve daha hızlı olarak std :: reverse () kendimi :) kullanmak istiyorsunuz)
str = std::string(str.rbegin(), str.rend());
C yolu (az ya da çok :)) ve lütfen, XOR takas için hile konusunda dikkatli olun, derleyiciler bazen bunu optimize edemez.
Bu durumda genellikle çok daha yavaştır.
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
Eğer potansiyel olarak uzunsa, ipin sonunu bulmak için kullanırdım . İyi bir kütüphane uygulaması, yineleme başına 1 bayttan daha hızlı arama yapmak için SIMD vektörlerini kullanır. Ancak çok kısa dizeler için, while (*++end);
bir kütüphane fonksiyon çağrısı aramaya başlamadan önce yapılacaktır.
_mm_shuffle_epi8
x86'da , (PSHUFB), sağ karıştırma kontrol vektörü verildiğinde bir 16B vektörünün sırasını tersine çevirebilir. Özellikle AVX2 ile bazı dikkatli optimizasyonlarla muhtemelen neredeyse memcpy hızında çalışabilir.
char* beg = s-1
tanımlanmamış bir davranışı vardır (en azından s
bir dizinin en yaygın örneği olan ilk öğesini işaret ediyorsa ). boş bir dize while (*++end);
ise tanımsız davranışa sahiptir s
.
s-1
etse bile, davranışı tanımlayan bir iddiada s
bulunuyorsunuz, bu yüzden standardı destekleyecek olan sizsiniz.
#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;
}
Bu kod bu çıktıyı üretir:
gnitset
a
cba
GLib kullanıyorsanız, bunun için iki işlevi vardır, g_strreverse () ve g_utf8_strreverse ()
Evgeny'nin K&R cevabını seviyorum. Ancak, işaretçiler kullanarak bir sürüm görmek güzel. Aksi takdirde, aslında aynıdı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;
}
Bir dizgiyi yerinde tersine çevirmek için özyinelemeli fonksiyon (ekstra tampon yok, malloc).
Kısa, seksi kod. Kötü, kötü yığın kullanımı.
#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
yığının üzerine itmek "yerinde" olarak sayılmaz. Özellikle karakter başına 4 * 8B bastığınızda değil (64 bit makinede: 3 args + bir dönüş adresi).
Kodumu paylaş. Bir C ++ öğrenicisi olarak, swap () kullanmak için bir seçenek olarak, alçakgönüllü yorum istiyorum.
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 kullanıyorsanız CString
, arayın CString::MakeReverse()
.
Yine bir başka:
#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");
}
Cevabım çoğuna benzer, ancak lütfen kodumu burada bulabilirsiniz.
//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;
}
Dize tersine çevirmek için başka bir yol olduğunu düşünüyorum. girdiyi kullanıcıdan alır ve tersine çevirir.
void Rev() {
char ch;
cin.get(ch);
if(ch != '\n') {
Rev();
cout.put(ch);
}
}
Depolamanıza gerek yoksa, bu şekilde harcanan zamanı azaltabilirsiniz:
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");
}
İşte benim C ile benim almak. Bunu pratik için yaptı ve mümkün olduğunca özlü olmaya çalıştı! Komut satırı üzerinden bir dize girersiniz, yani ./program_name "buraya dizeyi girin"
#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);
}
Ama XOR takas algoritmasının en iyisi olduğunu düşünüyorum ...
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()
Döngü koşulunda ve döngü gövdesinde yapılan bir çağrı optimize edilemeyebilir.
İşte bir dize C ++ (bence) tersine çevirmek için en temiz, en güvenli, en kolay yolu:
#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);
}
Bir alternatif kullanmaktır std::swap
, ancak kendi işlevlerimi tanımlamayı seviyorum - ilginç bir egzersiz ve include
ekstra bir şeye ihtiyacınız yok .
#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;
}
Bu, bir dize tersine çevirmek için C dilinde optimize edilmiş koddur ... Ve basit; işi yapmak için basit bir işaretçi kullanın ...