Jak poprawnie porównać ciągi?

182

Usiłuję uzyskać program, który pozwoli użytkownikowi wprowadzić słowo lub znak, zapisać je, a następnie wydrukować, dopóki użytkownik nie wpisze go ponownie, wychodząc z programu. Mój kod wygląda następująco:

#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;
}

Problem polega na tym, że ciągle otrzymuję wydruk ciągu wejściowego, nawet jeśli dane wejściowe użytkownika (sprawdź) są zgodne z oryginałem (dane wejściowe). Czy źle je porównuję?

nmagerko
źródło
13
gets( )został usunięty ze standardu. Użyj fgets( )zamiast tego.
Edward Karak
1
Zauważ, że ta odpowiedź na pytanie, dlaczego strcmp()zwraca zero, gdy jego dane wejściowe są równe, wyjaśnia, jak porównać ciągi dla równości, nierówności, mniejszej, większej niż, mniejszej lub równej i większej niż lub równej. Nie wszystkie porównania ciągów służą równości. Porównywania wielkości liter są znowu różne; inne specjalne porównania (na przykład kolejność słowników) wymagają bardziej wyspecjalizowanych komparatorów, a istnieją jeszcze wyrażenia regularne dla bardziej złożonych porównań.
Jonathan Leffler
Zauważ też, że istnieje zasadniczo zduplikowane pytanie Jak sprawdzić, czy wartość pasuje do ciągu, o który pytano wiele lat wcześniej.
Jonathan Leffler,
Czy to odpowiada na twoje pytanie? Jak sprawdzić, czy wartość pasuje do ciągu
Andreas
To pytanie jest dobre, ale nie można go użyć gets(). Został również usunięty ze standardu od C11 -> Przeczytaj, dlaczego funkcja gets jest tak niebezpieczna, że ​​nie należy jej używać?
RobertS wspiera Monikę Cellio

Odpowiedzi:

275

Nie możesz (pożytecznie) porównywać ciągów za pomocą !=lub ==, musisz użyć strcmp:

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

Powodem tego jest to, że !=i ==porówna tylko adresy podstawowe tych ciągów. Nie zawartość samych łańcuchów.

Tajemniczy
źródło
10
to samo w java, które może po prostu porównać z adresem.
Telerik
29
Pisanie while (strcmp(check, input))jest wystarczające i jest uważane za dobrą praktykę.
Shiva
wiedzieć więcej ... codificare.in/codes/c/...
Chanu Panwar
7
Bezpieczniej jest używać strncmp! Nie chcę przepełnienia bufora!
Floam,
@ Floam Jeśli tak naprawdę nie masz ciągów, ale wypełnione zerami sekwencje niezerowych znaków o znanej długości, na pewno byłoby to właściwe inkantacja. Ale to coś zupełnie innego!
Deduplicator
33

Ok kilka rzeczy: getsjest niebezpieczny i należy go zastąpić fgets(input, sizeof(input), stdin), aby uniknąć przepełnienia bufora.

Następnie, aby porównać ciągi, musisz użyć strcmp, gdzie zwracana wartość 0 wskazuje, że oba ciągi są zgodne. Użycie operatorów równości (tj. !=) Porównuje adres dwóch ciągów, w przeciwieństwie do poszczególnych znaków charw nich zawartych .

Zwróć też uwagę, że chociaż w tym przykładzie nie spowoduje problemu, fgetszapisuje znak nowej linii, również '\n'w buforach; gets()nie. Jeśli porównasz dane wejściowe użytkownika z fgets()dosłownym ciągiem "abc", który nigdy by się nie zgadzał (chyba że bufor '\n'byłby zbyt mały, aby nie mieścił się w nim).

AusCBloke
źródło
Czy możesz wyjaśnić związek / problem „\ n” i literału łańcuchowego? Otrzymuję nierówny wynik w porównaniu ciągów (linii) pliku z innym całym plikiem.
niekompetentny
@incompetent - jeśli czytasz wiersz z pliku za pomocą fgets(), łańcuch może być, "abc\n"ponieważ fgets()zachowuje nowy wiersz. Jeśli porównasz to z "abc", otrzymasz „nie równy” z powodu różnicy między zakończeniem bajtu zerowego "abc"a znakiem nowej linii w odczytanych danych. Musisz zapełnić nową linię. Niezawodny sposób na wykonanie jednej linii, buffer[strcspn(buffer, "\n")] = '\0';który ma tę zaletę, że działa poprawnie niezależnie od tego, czy w buforze są jakieś dane, czy też dane te kończą się nową linią, czy nie. Inne sposoby zatykania nowej linii łatwo psują się.
Jonathan Leffler
Ta odpowiedź dokładnie rozwiązuje problemy z kodem, podczas gdy najbardziej pozytywna i akceptowana odpowiedź obejmuje tylko odpowiedź na tytuł pytania. Zwłaszcza, że ​​ostatni akapit jest super. +1
RobertS obsługuje Monikę Cellio
11

Zastosowanie strcmp.

To jest w string.hbibliotece i jest bardzo popularne. strcmpzwraca 0, jeśli łańcuchy są równe. Zobacz to, aby uzyskać lepsze wyjaśnienie strcmpzwrotów.

Zasadniczo musisz zrobić:

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

lub

while (!strcmp(check,input))

lub

while (strcmp(check,input))

Można sprawdzić to , tutorial strcmp.

Ashish Ahuja
źródło
7

Nie można bezpośrednio porównywać tablic w ten sposób

array1==array2

Powinieneś porównać je char-by-char; w tym celu można użyć funkcji i zwrócić wartość logiczną (True: 1, False: 0). Następnie można go użyć w warunkach testowych pętli while.

Spróbuj tego:

#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;
}
mugetsu
źródło
1
Czy możesz dodać więcej szczegółów na temat swojego rozwiązania?
abarisone
tak, to zastępuje funkcję i polecenie strcmp bez użycia string.h header @Jongware
mugetsu
2
To nie działa. Gdy checkerznajdzie '\0'w jednym z ciągów, nie sprawdza innego ciągu '\0'. Funkcja zwraca 1(„równy”), nawet jeśli jeden ciąg znaków jest tylko przedrostkiem drugiego (na przykład "foo"i "foobar").
lukasrozs
1
Użyłbym ||zamiast &&.
lukasrozs
3

Witamy w koncepcji wskaźnika. Wiele pokoleń początkujących programistów uznało tę koncepcję za nieuchwytną, ale jeśli chcesz wyrosnąć na kompetentnego programistę, musisz w końcu opanować tę koncepcję - a ponadto zadajesz już właściwe pytanie. Dobre.

Czy jest dla ciebie jasne, jaki jest adres? Zobacz ten schemat:

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

Na schemacie liczba całkowita 1 jest przechowywana w pamięci pod adresem 0x4000. Dlaczego pod adresem? Ponieważ pamięć jest duża i może przechowywać wiele liczb całkowitych, tak samo jak miasto jest duże i może pomieścić wiele rodzin. Każda liczba całkowita jest przechowywana w miejscu pamięci, ponieważ każda rodzina mieszka w domu. Każda lokalizacja pamięci jest identyfikowana przez adres , ponieważ każdy dom jest identyfikowany przez adres.

Dwa pola na schemacie reprezentują dwie różne lokalizacje pamięci. Możesz myśleć o nich jak o domach. Liczba całkowita 1 znajduje się w lokalizacji pamięci pod adresem 0x4000 (pomyśl „4000 Elm St.”). Liczba całkowita 7 znajduje się w lokalizacji pamięci pod adresem 0x4004 (pomyśl: „4004 Elm St.”).

Myślałeś, że twój program porównuje 1 z 7, ale tak nie było. Porównywał 0x4000 do 0x4004. Co się dzieje, gdy masz taką sytuację?

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

Dwie liczby całkowite są takie same, ale adresy się różnią. Twój program porównuje adresy.

thb
źródło
2

Ilekroć próbujesz porównać ciągi, porównaj je w odniesieniu do każdego znaku. W tym celu można użyć wbudowanej funkcji łańcuchowej o nazwie strcmp (input1, input2); i powinieneś użyć pliku nagłówka o nazwie#include<string.h>

Wypróbuj ten kod:

#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");  
} 
Vishwanath Tallalli
źródło
0

Jak poprawnie porównać ciągi?

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))

Zagłębmy się, aby zobaczyć, dlaczego check != inputto nie wystarczy .

W C łańcuch znaków jest standardową specyfikacją biblioteki.

Ciąg jest ciągła sekwencja znaków zakończone włącznie pierwszego znaku null.
C11 § 7.1.1 1

inputpowyżej nie jest ciągiem . inputjest tablicą 40 znaków .

Zawartość inputmoże stać się ciągiem .

W większości przypadków, gdy tablica jest używana w wyrażeniu, jest konwertowana na adres jej pierwszego elementu.

Poniżej konwertuje checki inputna ich adresy pierwszego elementu, a następnie te adresy są porównywane.

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

Aby porównać ciągi , musimy użyć tych adresów, a następnie spojrzeć na dane, na które wskazują.
strcmp()wykonuje robotę . §7.23.4.2

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

strcmpFunkcja porównuje łańcuch wskazywany przez s1się łańcuch wskazywany przez s2.

Te strcmpfunkcja zwraca większa liczba całkowita, równa lub mniejsza od zera, w związku z łańcuchem, wskazywanego przez s1większa, równa lub mniejsza niż ciąg wskazywanego przez s2.

Kod może nie tylko znaleźć, czy ciągi zawierają te same dane, ale który z nich jest większy / mniej, gdy się różnią.

Poniższe jest prawdziwe, gdy łańcuch różni się.

strcmp(check, input) != 0

Aby uzyskać wgląd, zobacz Tworzenie własnej strcmp()funkcji

chux - Przywróć Monikę
źródło
-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;
    }

To bardzo proste rozwiązanie, w którym uzyskasz swoje wyniki, jak chcesz.

Rupani TD
źródło
2
gets();nie jest częścią standardu C od C11.
chux - Przywróć Monikę
2
strcmp(s1,s2)jest UB, ponieważ s2treść nie jest początkowo określona.
chux - Przywróć Monikę
Byłoby wspaniale, gdybyś mógł podać wyniki tego fragmentu, w takiej czy innej formie.
not2qubit,