Як правильно порівнювати рядки?


182

Я намагаюся отримати програму, щоб дозволити користувачеві ввести слово чи символ, зберегти його та потім надрукувати, поки користувач не введе його знову, вийшовши з програми. Мій код виглядає приблизно так:

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

Проблема полягає в тому, що я продовжую отримувати друк вхідного рядка, навіть коли введення користувачем (перевірка) відповідає оригіналу (вводу). Чи порівнюю я ці два?


13
gets( )було видалено зі стандарту. Використовуйте fgets( )замість цього.
Едвард Карак

1
Зауважте, що ця відповідь на питання " Чому strcmp()повертає нуль, коли його вхідні дані рівні" пояснює, як порівнювати рядки для рівності, нерівності, меншої, більшої, меншої або однакової та більшої або рівної. Не всі порівняння рядків є для рівності. Порівняння, що враховують регістр, знову відрізняються; інші спеціальні порівняння (порядок словника, наприклад) вимагають більш спеціалізованих компараторів, а для ще складніших порівнянь існують регулярні виразки.
Джонатан Леффлер

Зауважте також, що існує по суті повторне запитання. Як перевірити, чи відповідає значення рядку , заданому роками до цього.
Джонатан Леффлер

Чи відповідає це на ваше запитання? Як перевірити, чи відповідає значення рядку
Андреас

Це питання є гарним, але використання не gets()є. Він також був видалений зі стандарту з C11 -> Будь ласка, прочитайте Чому функція get настільки небезпечна, що її не слід використовувати?
RobertS підтримує Моніку Селліо

Відповіді:


275

Ви не можете (корисно) порівнювати рядки , використовуючи !=або ==, вам потрібно використовувати strcmp:

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

Причина цього полягає в тому, що !=і ==лише порівнюватимуть базові адреси цих рядків. Не вміст самих рядків.


10
те саме в java, яке може просто порівнятись з адресою.
Телерик

29
Писання while (strcmp(check, input))достатньо і вважається хорошою практикою.
Шива

дізнатися більше ... codificare.in/codes/c / ...
Chanu Panwar

7
Безпечніше використовувати strncmp! Не потрібно переповнювати буфер!
Поплавок

@Floam Якщо ви насправді не маєте рядків, але послідовностей з нульовими знаками ненульових символів відомої довжини, впевнено, це було б правильним закликом. Але це щось зовсім інше!
Deduplicator

33

Добре кілька речей: getsнебезпечно і його слід замінити fgets(input, sizeof(input), stdin)таким чином, щоб у вас не було переповнення буфера.

Далі, для порівняння рядків, ви повинні використовувати strcmp, де значення повернення 0 означає, що обидва рядки відповідають. Використання операторів рівності (тобто !=) порівнює адресу двох рядків на відміну від окремих chars всередині них.

А також зауважте, що, хоча в цьому прикладі це не спричинить проблеми, він fgetsзберігає символ нового рядка і '\n'в буферах; gets()не. Якщо ви порівняли вхід користувача з fgets()лінійним рядком, таким як "abc"він ніколи не збігався (якщо тільки буфер не був занадто малим, щоб '\n'він не вміщувався в ньому).


Чи можете ви уточнити зв'язок / проблему "\ n" та літерального рядка? Я отримую не рівний результат у порівнянні рядків (рядків) файлу з іншими цілими файлами.
некомпетентний

@incompetent - якщо ви читаєте рядок з файлу з fgets(), то рядок може бути "abc\n"тому, що fgets()зберігає новий рядок. Якщо порівнювати це з "abc", ви отримаєте "не рівне" через різницю між завершенням нульового байта "abc"та новим рядком у прочитаних даних. Отже, вам доведеться запаяти новий рядок. Надійний однолінійний спосіб зробити це тим, buffer[strcspn(buffer, "\n")] = '\0';що заслуговує на правильну роботу незалежно від того, чи є якісь дані в буфері, чи закінчуються ці дані новим рядком чи ні. Інші способи переключення нового рядка легко виходять з ладу.
Джонатан Леффлер

Ця відповідь точно вирішує питання коду, тоді як найбільш схвалена та прийнята відповідь стосується лише відповіді на назву питання. Особливо згадати останній абзац - це супер. +1
RobertS підтримує Моніку Селліо

11

Використовуйте strcmp.

Це є в string.hбібліотеці і користується великою популярністю. strcmpповернути 0, якщо рядки рівні. Дивіться це для кращого пояснення того, що strcmpповертається.

В основному, ви повинні зробити:

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

або

while (!strcmp(check,input))

або

while (strcmp(check,input))

Ви можете перевірити це , підручник на strcmp.


7

Ви не можете безпосередньо порівнювати масиви

array1==array2

Ви повинні порівнювати їх чар-чар-чар; для цього можна використовувати функцію та повернути бульне значення (True: 1, False: 0). Потім ви можете використовувати його в тестовому стані циклу while.

Спробуйте це:

#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
Не могли б ви додати більше деталей щодо свого рішення?
абарізон

так, це заміна функції strcmp і рішення без використання string.h заголовка @Jongware
mugetsu

2
Це не працює. Коли checkerзнаходить '\0'в одному з рядків, він не перевіряє інший рядок на '\0'. Функція повертається 1("рівна"), навіть якщо одна рядок є лише префіксом іншої (наприклад, "foo"і "foobar").
lukasrozs

1
Я б використав ||замість цього &&.
lukasrozs

3

Ласкаво просимо до концепції вказівника. Покоління початківців програмістів визнали це поняття невловимим, але якщо ви хочете перерости в компетентного програміста, ви, зрештою, повинні освоїти цю концепцію - і більше того, ви вже ставите правильне питання. Добре.

Вам зрозуміло, що таке адреса? Дивіться цю схему:

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

На діаграмі ціле число 1 зберігається в пам'яті за адресою 0x4000. Чому за адресою? Оскільки пам'ять велика і може зберігати безліч цілих чисел, подібно до того, як місто велике і може розміщувати багато сімей. Кожне ціле число зберігається в пам'яті, оскільки кожна сім'я проживає в будинку. Кожне місце пам'яті ідентифікується адресою , так як кожен будинок ідентифікується адресою.

Два поля на діаграмі представляють два різних місця пам'яті. Ви можете думати про них так, ніби це були будинки. Ціле число 1 знаходиться в пам'яті за адресою 0x4000 (подумайте, "4000 Elm St."). Ціле число 7 знаходиться в пам'яті за адресою 0x4004 (подумайте, "4004 St. Elm St.").

Ви думали, що ваша програма порівнює 1 з 7, але це не було. Він порівнював 0x4000 з 0x4004. То що відбувається, коли у вас є така ситуація?

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

Два цілих числа однакові, але адреси відрізняються. Ваша програма порівнює адреси.


2

Кожного разу, коли ви намагаєтеся порівняти рядки, порівнюйте їх щодо кожного символу. Для цього можна використовувати вбудовану функцію рядка під назвою strcmp (input1, input2); і вам слід скористатися файлом заголовка, який називається#include<string.h>

Спробуйте цей код:

#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

Як правильно порівнювати рядки?

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

Давайте копаємо глибше, щоб зрозуміти, чому check != inputцього недостатньо .

У C рядок - це стандартна специфікація бібліотеки.

Рядок являє собою безперервну послідовність символів , що закінчуються і в тому числі першого нульового символу.
C11 §7.1.1 1

inputвище не є рядком . inputце масив 40 знаків .

Вміст inputможе стати рядком .

У більшості випадків, коли масив використовується в виразі, він перетворюється на адресу свого першого елемента.

Нижче звернені checkі inputдо їх відповідними адресами першого елемента, то ці адреси порівнюються.

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

Для порівняння рядків нам потрібно використовувати ці адреси, а потім переглянути дані, на які вони вказують.
strcmp()виконує роботу . §7.23.4.2

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

strcmpФункція порівнює рядок , на яку вказує s1на рядок , на яку вказує s2.

В strcmpфункція повертає ціле число більше, дорівнює або менше , ніж нуль, відповідно , як рядок , на яку вказує s1більше, дорівнює або менше , ніж рядок , на яку вказує s2.

Код може не тільки знайти, якщо рядки мають однакові дані, але й той, який є більшим / меншим, коли вони відрізняються.

Нижче справедливо, коли рядки відрізняються.

strcmp(check, input) != 0

Для ознайомлення читайте в розділі Створення власної strcmp()функції


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

Це дуже просте рішення, при якому ви отримаєте свій результат як хочете.


2
gets();не входить до стандарту C з C11.
chux

2
strcmp(s1,s2)є UB, оскільки s2вміст спочатку не вказується.
chux

Було б чудово, якби ви могли також надати вихід цього фрагмента в тій чи іншій формі.
not2qubit
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.