Как правильно сравнить строки?


183

Я пытаюсь получить программу, позволяющую пользователю вводить слово или символ, сохранять его, а затем распечатывать, пока пользователь не введет его снова, выйдя из программы. Мой код выглядит так:

#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 -> Пожалуйста, прочтите Почему функция gets настолько опасна, что ее не следует использовать?
— RobertS поддерживает Монику Челлио

Ответы:


276

Вы не можете (полезно) сравнивать строки, используя !=или ==, вам нужно использовать strcmp:

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

Причина этого в том , что !=и ==будет сравнивать только базовые адреса этих строк. Не содержимое самих строк.


10
то же самое в Java, который может просто сравнить с адресом.
— Telerik

29
Письмо while (strcmp(check, input))достаточно и считается хорошей практикой.
— Шива

узнать больше ... codificare.in/codes/c/...
— Chanu Panwar

7
Безопаснее использовать strncmp! Не хочу переполнения буфера!
— Floam

@Floam Если у вас на самом деле нет строк, но есть дополненные нулями последовательности ненулевых символов известной длины, конечно, это было бы правильным заклинанием. Но это нечто совершенно другое!
— дедупликатор

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").
— Лукасроз

1
Я бы использовал ||вместо &&.
— Лукасроз

3

Добро пожаловать в концепцию указателя.Поколения начинающих программистов нашли эту концепцию неуловимой, но если вы хотите стать компетентным программистом, вы должны в конечном итоге овладеть этой концепцией - и, более того, вы уже задаете правильный вопрос. Это хорошо.

Вам понятно, что это за адрес? Смотрите эту диаграмму:

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

На диаграмме целое число 1 хранится в памяти по адресу 0x4000. Почему по адресу? Поскольку память велика и может хранить много целых чисел, так же, как город большой и может вместить много семей. Каждое целое число хранится в ячейке памяти, так как каждая семья проживает в доме. Каждая ячейка памяти идентифицируется по адресу , а каждый дом - по адресу.

Два поля на диаграмме представляют две различные области памяти. Вы можете думать о них, как будто они были домами. Целое число 1 находится в ячейке памяти по адресу 0x4000 (например, «4000 Elm St.»). Целое число 7 находится в ячейке памяти по адресу 0x4004 (например, «4004 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этого недостаточно .

В Си строка является стандартной спецификацией библиотеки.

Строка представляет собой непрерывную последовательность символов , заканчивающихся и в том числе первого нулевого символа.
C11 §7.1.1 1

inputвыше не строка . inputэто массив 40 из полукокса .

Содержимое inputможет стать строкой .

В большинстве случаев, когда массив используется в выражении, он преобразуется в адрес своего 1-го элемента.

Ниже преобразуются 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.