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

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

#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);   /* obsolete function: do not use!! */
    printf("I will now repeat this until you type it back to me.\n");

    while (check != input)
    {
        printf("%s\n", input);
        gets(check);   /* obsolete function: do not use!! */
    }

    printf("Good bye!");
    

    return 0;
}

Проблема в том, что я продолжаю получать печать входной строки, даже если ввод пользователем (проверка) совпадает с исходным (вводом). Я неправильно сравниваю эти два?


person nmagerko    schedule 04.11.2011    source источник
comment
gets( ) был удален из стандарта. Вместо этого используйте fgets( ).   -  person Edward Karak    schedule 29.01.2015
comment
Обратите внимание, что этот ответ на Почему strcmp() возвращает ноль, когда его входные данные равны, объясняет, как сравнивать строки на равенство, неравенство, меньше, больше, меньше или равно и больше или равно. Не все сравнения строк предназначены для равенства. Сравнение с учетом регистра снова отличается; другие специальные сравнения (например, порядок словаря) требуют более специализированных компараторов, и есть регулярные выражения для еще более сложных сравнений.   -  person Jonathan Leffler    schedule 23.11.2016
comment
Также обратите внимание, что существует практически повторяющийся вопрос Как сделать Я проверяю, соответствует ли значение строке, заданной за несколько лет до этого.   -  person Jonathan Leffler    schedule 10.02.2017


Ответы (10)


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

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

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

person Mysticial    schedule 04.11.2011
comment
то же самое в java, может просто сравнить с адресом. - person Telerik; 06.09.2014
comment
Написание while (strcmp(check, input)) достаточно и считается хорошей практикой. - person Shiva; 28.06.2015
comment
узнать больше ... codificare.in/codes/ c / - person chanu panwar; 25.06.2016
comment
Безопаснее использовать strncmp! Не хочу переполнения буфера! - person Floam; 10.11.2017
comment
@Floam Если у вас на самом деле нет строк, а есть дополненные нулями последовательности ненулевых символов известной длины, конечно, это было бы правильным заклинанием. Но это совсем другое! - person Deduplicator; 02.02.2019
comment
@craigB Извини, что лопнул твой пузырь, но strcmp() говорит, что "abcde" больше, чем "abc", а не равно ему. - person Deduplicator; 11.09.2020

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

Затем, чтобы сравнить строки, вы должны использовать strcmp, где возвращаемое значение 0 указывает, что две строки совпадают. Использование операторов равенства (например, !=) сравнивает адреса двух строк, в отличие от отдельных char внутри них.

Также обратите внимание, что, хотя в этом примере это не вызовет проблем, fgets сохраняет символ новой строки, '\n' также в буферах; gets() нет. Если вы сравните ввод пользователя из fgets() со строковым литералом, например "abc", он никогда не будет совпадать (если только буфер не был слишком маленьким, чтобы '\n' в него не поместился).

person AusCBloke    schedule 04.11.2011
comment
Не могли бы вы прояснить отношения / проблему \ n и строкового литерала? Я получаю неравный результат при сравнении строк (строк) файла с другим целым файлом. - person incompetent; 17.07.2019
comment
@incompetent - если вы читаете строку из файла с fgets(), тогда строка может быть "abc\n", потому что fgets() сохраняет новую строку. Если вы сравните это с "abc", вы получите «не равно» из-за разницы между нулевым байтом, завершающим "abc", и новой строкой в ​​прочитанных данных. Итак, вам нужно удалить новую строку. Надежный однострочный способ сделать это - buffer[strcspn(buffer, "\n")] = '\0';, достоинством которого является правильная работа независимо от того, есть ли какие-либо данные в буфере или заканчиваются ли эти данные символом новой строки или нет. Другие способы замены новой строки легко приводят к сбою. - person Jonathan Leffler; 16.01.2020
comment
Этот ответ точно затрагивает проблемы кода, в то время как наиболее одобренный и принятый ответ касается только ответа на заголовок вопроса. Особо отмечу последний абзац супер. +1 - person RobertS supports Monica Cellio; 30.05.2020

Используйте strcmp.

Он находится в string.h библиотеке и очень популярен. strcmp возвращает 0, если строки равны. См. this для лучшего объяснения того, что возвращает strcmp .

В основном вам нужно сделать:

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

or

while (!strcmp(check,input))

or

while (strcmp(check,input))

Вы можете проверить это, руководство по strcmp.

person Ashish Ahuja    schedule 13.02.2016

Вы не можете напрямую сравнивать массивы таким образом

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;
}
person mugetsu    schedule 06.04.2015
comment
Не могли бы вы подробнее рассказать о своем решении? - person abarisone; 06.04.2015
comment
да, это замена функции strcmp и solition без использования заголовка string.h @Jongware - person mugetsu; 06.04.2015
comment
Это не работает. Когда checker находит '\0' в одной из строк, он не проверяет другую строку на наличие '\0'. Функция возвращает 1 (равно), даже если одна строка является префиксом другой (например, "foo" и "foobar"). - person lukasrozs; 06.10.2017
comment
Я бы использовал || вместо &&. - person lukasrozs; 06.10.2017

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

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

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

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

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

Вы думали, что ваша программа сравнивает 1 с 7, но это не так. Он сравнивал 0x4000 с 0x4004. Так что же происходит в такой ситуации?

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

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

person thb    schedule 01.02.2019

Всякий раз, когда вы пытаетесь сравнить строки, сравнивайте их по каждому символу. Для этого вы можете использовать встроенную строковую функцию 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");  
} 
person Vishwanath Tallalli    schedule 03.03.2016

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

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 может стать строкой.

В большинстве случаев, когда в выражении используется массив, он преобразуется в адрес его 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() функции

person chux - Reinstate Monica    schedule 10.01.2019

Вам нужно использовать strcmp() и вам нужно #include <string.h>

Операторы != и == сравнивают только базовые адреса этих строк. Не содержимое строк

while (strcmp(check, input))

Пример кода:

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

int main()
{
    char input[40];
    char check[40] = "end\n"; //dont forget to check for \n

    while ( strcmp(check, input) ) //strcmp returns 0 if equal
    {
        printf("Please enter a name: \n");
        fgets(input, sizeof(input), stdin);
        printf("My name is: %s\n", input);
    }

    printf("Good bye!");
    return 0;
}

Примечание 1: gets() небезопасно. Вместо этого используйте fgets()

Примечание 2: при использовании fgets() вам также необходимо проверить '\n' символ новой строки

person mustafa candan    schedule 05.04.2021

Ты сможешь:

Используйте strcmp() из string.h, что является более простой версией

Или, если вы хотите свернуть свой собственный, вы можете использовать что-то вроде этого:

int strcmp(char *s1, char *s2)
{
    int i;
    while(s1[i] != '\0' && s2[i] != '\0')
    {
        if(s1[i] != s2[i])
        {
            return 1;
        }
        i++;
    }
    return 0;
}

Я бы использовал strcmp() следующим образом:

while(strcmp(check, input))
{
    // code here
}
person Anic17    schedule 18.07.2021
comment
Вы, вероятно, хотели поставить return 0; в конце функции strcmp - person Harrison; 20.07.2021
comment
Это не нужно, но да, это хорошая практика - person Anic17; 20.07.2021

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

Это очень простое решение, в котором вы получите желаемый результат.

person Rupani T D    schedule 23.03.2018
comment
gets(); не является частью стандарта C, начиная с C11. - person chux - Reinstate Monica; 10.01.2019
comment
strcmp(s1,s2) - это UB, поскольку s2 содержимое сначала не указывается. - person chux - Reinstate Monica; 10.01.2019
comment
Было бы здорово, если бы вы также могли предоставить вывод этого фрагмента в той или иной форме. - person not2qubit; 10.07.2019