Dizeleri düzgün bir şekilde nasıl karşılaştırırım?


183

Bir kullanıcının bir sözcük veya karakter girmesine, saklamasına ve daha sonra kullanıcı programdan çıkıp tekrar yazana kadar yazdırmasına izin vermek için bir program almaya çalışıyorum. Kodum şöyle görünüyor:

#include <stdio.h>

int main()
{
    char input[40];
    char check[40];
    int i=0;
    printf("Hello!\nPlease enter a word or character:\n");
    gets(input);
    printf("I will now repeat this until you type it back to me.\n");

    while (check != input)
    {
        printf("%s\n", input);
        gets(check); 
    }

    printf("Good bye!");


    return 0;
}

Sorun, kullanıcı tarafından girdi (kontrol) orijinal (giriş) eşleşse bile, girdi dizesinin yazdırmasını almaya devam olmasıdır. İkisini yanlış karşılaştırıyor muyum?


13
gets( )standarttan çıkarıldı. fgets( )Bunun yerine kullanın .
Edward Karak

1
Girdileri eşit olduğunda neden sıfır döndüren bu cevabın , eşitlik, eşitsizlik, daha küçük, daha büyük, daha küçük veya eşit ve daha büyük veya eşit için dizelerin nasıl karşılaştırılacağını açıkladığını unutmayın. Tüm dize karşılaştırmaları eşitlik için değildir. Büyük / küçük harfe duyarlı karşılaştırmalar yine farklıdır; diğer özel karşılaştırmalar (örneğin sözlük sırası) daha özel karşılaştırıcılar gerektirir ve daha karmaşık karşılaştırmalar için regex'ler vardır. strcmp()
Jonathan Leffler



Bu soru iyidir, ancak kullanımı gets()hiç de kolay değildir. Ayrıca C11'den beri standarttan çıkarılmıştır -> Lütfen Okuyun Alım işlevi kullanılmaması için neden bu kadar tehlikeli?
RobertS,

Yanıtlar:


276

Sen (yararlı) kullanarak dizeleri karşılaştırmak olamaz !=ya ==Kullanmak gerekir strcmp:

while (strcmp(check,input) != 0)

Bunun nedeni ise !=ve ==sadece bu dizeleri taban adreslerini karşılaştırır. Dizgilerin içeriği değil.


10
Java ile aynı, sadece adres ile karşılaştırmak olabilir.
Telerik

29
Yazmak while (strcmp(check, input))yeterlidir ve iyi uygulama olarak kabul edilir.
Shiva

daha fazla bilgi ... codificare.in/codes/c/...
chanu Panwar

7
Strncmp kullanmak daha güvenlidir! Arabellek taşması istemiyorum!
Floam

@Floam Eğer gerçekten dizeleriniz yoksa, bilinen uzunlukta sıfır olmayan karakterlerin sıfır dolgulu dizileri varsa, bu doğru büyüm olacaktır. Ama bu tamamen farklı bir şey!
Tekilleştirici

33

Tamam birkaç şey: getsgüvensizdir ve fgets(input, sizeof(input), stdin)bir arabellek taşması almamanız için değiştirilmelidir.

Ardından, dizeleri karşılaştırmak için strcmp, 0 döndürme değerinin iki dizenin eşleştiğini gösterdiğini kullanmalısınız. Eşitlik işleçlerini (yani. !=) Kullanmak, iki dizenin adresini, chariçindeki bireylerin aksine karşılaştırır .

Ayrıca, bu örnekte bir soruna neden olmayacak olsa da fgets, yeni satır karakterini '\n'arabelleklerde de sakladığını ; gets()değil. Kullanıcı girişini, hiçbir zaman eşleşmeyecek fgets()şekilde bir dize değişmeziyle "abc"karşılaştırdıysanız (arabellek sığmayacak kadar küçük olmadığı sürece '\n').


Lütfen "\ n" ve string literal ilişkisini / sorununu netleştirebilir misiniz? Diğer tüm dosya ile bir dosyanın dizeleri (satır) karşılaştırma eşit sonuç almıyorum.
beceriksiz

@incompetent - Eğer bir dosyadan bir satır okursanız fgets(), o zaman dize olabilir "abc\n"çünkü fgets()yeni satırı tutar. Bunu ile karşılaştırırsanız, "abc"boş bir bayt sonlandırması "abc"ile okunan verilerdeki yeni satır arasındaki farktan dolayı 'eşit olmaz' elde edersiniz . Yani, satırsonu zap gerekir. Bunu yapmanın güvenilir tek satırlık yolu buffer[strcspn(buffer, "\n")] = '\0';, arabellekte herhangi bir veri olup olmadığına veya bu verilerin bir satırsonu ile bitip bitmeyeceğine bakılmaksızın doğru çalışma avantajına sahiptir. Newline'ı zappingin diğer yolları kolayca çökebilir.
Jonathan Leffler

Bu cevap, kodun sorunlarını doğru bir şekilde ele alırken, en çok oylanan ve kabul edilen cevap sadece sorunun başlığını cevaplamayı kapsar. Özellikle son paragraftan bahsetmek süper. +1
RobertS,

11

Kullanın strcmp.

Bu string.hkütüphanede ve çok popüler. strcmpdizeler eşitse 0 döndür. Bkz bu ne bir iyi açıklama için strcmpgetiriler.

Temel olarak, yapmanız gerekenler:

while (strcmp(check,input) != 0)

veya

while (!strcmp(check,input))

veya

while (strcmp(check,input))

Kontrol edebilirsiniz bu , bir öğretici strcmp.


7

Dizileri doğrudan bu şekilde karşılaştıramazsınız

array1==array2

Onları char-by-char ile karşılaştırmalısınız; bunun için bir işlev kullanabilir ve bir boolean (True: 1, False: 0) değeri döndürebilirsiniz. Sonra while döngüsünün test koşullarında kullanabilirsiniz.

Bunu dene:

#include <stdio.h>
int checker(char input[],char check[]);
int main()
{
    char input[40];
    char check[40];
    int i=0;
    printf("Hello!\nPlease enter a word or character:\n");
    scanf("%s",input);
    printf("I will now repeat this until you type it back to me.\n");
    scanf("%s",check);

    while (!checker(input,check))
    {
        printf("%s\n", input);
        scanf("%s",check);
    }

    printf("Good bye!");

    return 0;
}

int checker(char input[],char check[])
{
    int i,result=1;
    for(i=0; input[i]!='\0' || check[i]!='\0'; i++) {
        if(input[i] != check[i]) {
            result=0;
            break;
        }
    }
    return result;
}

1
Çözümünüz hakkında daha fazla ayrıntı ekleyebilir misiniz?
abarisone

evet, string.h başlık kullanmadan strcmp işlevi ve çözümünün yerini alır @Jongware
mugetsu

2
Bu çalışmıyor. Ne zaman checkerbulur '\0'dizeleri birinde, onun için başka dize kontrol etmez '\0'. 1Bir dize yalnızca diğerinin öneki olsa bile (örneğin, "foo"ve "foobar") işlev ("eşit") döndürür .
Lukasrozs

1
Bunun ||yerine kullanırdım &&.
Lukasrozs

3

İşaretçi kavramına hoş geldiniz . Başlangıç ​​programcılarının nesiller kavramı zor bulmuşlardır, ancak yetkili bir programcıya dönüşmek istiyorsanız, sonunda bu kavramda ustalaşmalısınız - ve dahası, zaten doğru soruyu soruyorsunuz. Bu iyi.

Bir adresin ne olduğu size açık mı? Bu şemaya bakın:

----------     ----------
| 0x4000 |     | 0x4004 |
|    1   |     |    7   |
----------     ----------

Diyagramda, tamsayı 1, bellekte 0x4000 adresinde saklanır . Neden bir adreste? Çünkü hafıza büyüktür ve bir kentin büyük olduğu ve birçok aileyi barındırabildiği gibi birçok tamsayı depolayabilir. Her bir aile bir evde bulunduğu için her tamsayı bir bellek konumunda saklanır. Her ev bir adresle tanımlandığından, her bellek konumu bir adresle tanımlanır.

Diyagramdaki iki kutu iki ayrı bellek konumunu temsil eder. Onları evmiş gibi düşünebilirsiniz. Tamsayı 1, 0x4000 adresindeki bellek konumunda bulunur ("4000 Elm St." deyin). 7 tamsayısı 0x4004 adresindeki bellek konumunda bulunur ("4004 Elm St." deyin).

Programınızın 1'i 7 ile karşılaştırdığını düşündünüz, ama değildi. 0x4000 ile 0x4004'ü karşılaştırıyordu. Peki bu duruma sahip olduğunuzda ne olur?

----------     ----------
| 0x4000 |     | 0x4004 |
|    1   |     |    1   |
----------     ----------

İki tamsayı aynıdır, ancak adresler farklıdır. Programınız adresleri karşılaştırır.


2

Dizeleri her karşılaştırmaya çalıştığınızda, bunları her karakterle karşılaştırın. Bunun için strcmp (input1, input2) adlı yerleşik dize işlevini kullanabilirsiniz; ve şu başlık dosyasını kullanmalısınız:#include<string.h>

Bu kodu deneyin:

#include<stdio.h> 
#include<stdlib.h> 
#include<string.h>  

int main() 
{ 
    char s[]="STACKOVERFLOW";
    char s1[200];
    printf("Enter the string to be checked\n");//enter the input string
    scanf("%s",s1);
    if(strcmp(s,s1)==0)//compare both the strings  
    {
        printf("Both the Strings match\n"); 
    } 
    else
    {
        printf("Entered String does not match\n");  
    } 
    system("pause");  
} 

0

Dizeleri düzgün bir şekilde nasıl karşılaştırırım?

char input[40];
char check[40];
strcpy(input, "Hello"); // input assigned somehow
strcpy(check, "Hello"); // check assigned somehow

// insufficient
while (check != input)

// good
while (strcmp(check, input) != 0)
// or 
while (strcmp(check, input))

Neden check != inputyeterli olmadığını görmek için daha derine inelim .

C dilinde dize , standart bir kitaplık belirtimidir.

Bir dizi birinci boş karakter içeren bir bitişik sona erdirildi karakter dizisi ve bir.
C11 §7.1.1 1

inputYukarıdaki bir dize değildir . inputolan char dizisi 40 .

İçeriği inputbir dize olabilir .

Çoğu durumda, bir ifadede bir dizi kullanıldığında, bu öğe birinci öğesinin adresine dönüştürülür.

Aşağıdaki checkve inputilk elemanın ilgili adreslerine dönüştürülür , daha sonra bu adresler karşılaştırılır.

check != input   // Compare addresses, not the contents of what addresses reference

Dizeleri karşılaştırmak için bu adresleri kullanmalı ve daha sonra işaret ettikleri verilere bakmalıyız.
strcmp()işi yapar . §7.23.4.2

int strcmp(const char *s1, const char *s2);

strcmpFonksiyon tarafından işaret dize karşılaştırır s1dize tarafından işaret etmeks2 .

strcmpDaha büyük bir tam sayı işlevi döndürüldüğünde, e eşit ya da daha az bir sıfır, karakteri ile işaret buna uygun olarak s1daha az karakteri ile işaret oranına eşit ya da daha büyüktür s2.

Kodlar yalnızca aynı veriye sahipse kod bulmakla kalmaz, aynı zamanda farklı olduklarında hangisinin daha büyük / az olduğunu da bulabilir.

Dize farklı olduğunda aşağıdaki doğrudur.

strcmp(check, input) != 0

Bilgi için bkz. Kendi strcmp()işlevimi oluşturma


-2
    #include<stdio.h>
    #include<string.h>
    int main()
    {
        char s1[50],s2[50];
        printf("Enter the character of strings: ");
        gets(s1);
        printf("\nEnter different character of string to repeat: \n");
        while(strcmp(s1,s2))
        {
            printf("%s\n",s1);
            gets(s2);
        }
        return 0;
    }

Bu, çıktınızı istediğiniz gibi alacağınız çok basit bir çözümdür.


2
gets();C11'den beri standart C'nin bir parçası değildir.
chux - Monica'yı

2
strcmp(s1,s2)s2içeriği ilk başta belirtilmediğinden UB'dir .
chux - Monica'yı

Bu snippet'in çıktısını bir şekilde veya başka bir şekilde vermeniz harika olurdu.
not2qubit
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.