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ę?


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.


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


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.


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

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.


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");  
} 

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


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


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,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.