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


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.