C strcmp ведет себя странно

Хорошо, я не уверен, что это я или что-то еще, но я действительно запутался.

Я пытаюсь найти первое вхождение строки внутри другой строки (так же, как InString() из C++), но из C и делаю это с помощью strcmp().

У меня есть два массива char, string[] и toFind[], и я просматриваю их в двух циклах for(), сравнивая каждый char с strcmp().

Вот код:

int inString(char string[], char toFind[]){

int i_toFind, i_string, check = 0, start = -1;

for(i_toFind = 0; i_toFind < getLength(toFind)-1; i_toFind++){

    for(i_string = 0; i_string < getLength(string)-1; i_string++){

        if(strcmp(&string[i_string], &toFind[i_toFind])==0){

            printf("%i & %i == %i\n", string[i_string], toFind[i_toFind], strcmp(&string[i_string], &toFind[i_toFind]));

            if(start == -1){
                start = i_string;
            }

            check++;
            i_toFind++;

            if(check == getLength(toFind)-1){
                return start;
            }

        }
        else{

            printf("%i & %i == %i\n", string[i_string], toFind[i_toFind], strcmp(&string[i_string], &toFind[i_toFind]));

            check = 0;
            start = -1;

        }

    }

}

return -1;

}

Теперь это работает, например, для этих значений:

string[] = "hello my friend"
toFind[] = "friend"

result:

104 & 102 == 2
101 & 102 == -1
108 & 102 == 6
108 & 102 == 6
111 & 102 == 9
32 & 102 == -70
109 & 102 == 7
121 & 102 == 19
32 & 102 == -70
102 & 102 == 0
114 & 114 == 0
105 & 105 == 0
101 & 101 == 0
110 & 110 == 0
100 & 100 == 0

Но для этого не работает:

string[] = "friday friend comes"
toFind[] = "friend"

result:

102 & 102 == -1
114 & 102 == 12
105 & 102 == 3
100 & 102 == -2
97 & 102 == -5
121 & 102 == 19
32 & 102 == -70
102 & 102 == 22
114 & 102 == 12
105 & 102 == 3
101 & 102 == -1
110 & 102 == 8
100 & 102 == -2
32 & 102 == -70
99 & 102 == -3
111 & 102 == 9
109 & 102 == 7
101 & 102 == -1
115 & 102 == 13
102 & 114 == -12
114 & 114 == -1
105 & 114 == -9
...

Интересная часть заключается в следующем:

102 & 102 == -1
114 & 114 == -1

Не должно ли это быть 0 при равенстве? Или что мне здесь не хватает? Функция, которую я написал, кажется, терпит неудачу только в том случае, если слово, которое я ищу, НЕ является последним в строке [].

Надеюсь, кто-то действительно найдет мою ошибку.. Спасибо!

ОБНОВИТЬ:

Код, в котором я запускаю InString(), состоит только из одной строки:

printf("Beginn: %i\n", inString(string, substring));

ОБНОВЛЕНИЕ 2:

Вот простой пример проблемы:

int inString(char string[], char toFind[]){

    const char *pointer_toStart = strstr(string, toFind);

    return pointer_toStart ? pointer_toStart - string : -1;

}

int main(int argc, const char * argv[]) {

    char string[300], substring[300];

    printf("String: ");

    fgets(&test, 20, stdin); // To capture the one '\n' inside the buffer (just ignore this line)
    fgets(string, 300, stdin);

    printf("toFind: ");

    fgets(substring, 300, stdin);

    printf("Beginn: %i\n", inString(string, substring));

}

person user1641602    schedule 22.11.2015    source источник
comment
Вы не можете использовать strcmp для сравнения отдельных символов.   -  person Oliver Charlesworth    schedule 22.11.2015
comment
Поскольку вы используете строковые функции c, почему бы не заменить getLength на strlen?   -  person Alexguitar    schedule 22.11.2015
comment
strstr() можно использовать.   -  person balabhi    schedule 22.11.2015
comment
Вы уверены, что не хотите strstr()? Если вы пытаетесь реализовать strstr(), неясно, является ли strcmp() полезной функцией; вы могли бы использовать strncmp(), но даже это, скорее всего, будет проблематичным, чем выполнение работы с символами, а не со строками.   -  person Jonathan Leffler    schedule 22.11.2015
comment
getLength() использует strlen, так что в основном это то же самое. Я не использую strstr(), потому что это дает мне указатель, верно? Как мне получить из этого начальную позицию искомой строки?   -  person user1641602    schedule 22.11.2015
comment
Указатель, возвращаемый strstr(), является указателем на начало первого вхождения строки «игла» в строку «стог сена» или нулевым указателем, если игла не найдена в стоге сена. Если вам нужно смещение до начала строки, то retval - start дает вам нужное число.   -  person Jonathan Leffler    schedule 22.11.2015
comment
Да, я знаю, что это указатель на искомую строку, но как именно мне преобразовать указатель char * в int, сообщающий мне начальный индекс?   -  person user1641602    schedule 22.11.2015
comment
Это не неправильное поведение, просто неправильное использование   -  person Michi    schedule 22.11.2015
comment
сори, временный мозговой провал...   -  person alk    schedule 22.11.2015


Ответы (2)


Вы не можете использовать strcmp для сравнения частей строк C, вы можете использовать для этого memcmp. Но есть более простой подход к вашему вопросу, используя strstr.

Вот простая реализация inString в соответствии с вашей неявной спецификацией.

#include <string.h>

int inString(const char *string, const char *toFind) {
    const char *p = strstr(string, toFind);
    return p ? p - string : -1;
}

Функция возвращает начальный индекс подстроки, если она найдена, и -1, если не найдена.

С этой реализацией следующий тест правильно печатает 25:

#include <stdio.h>

int main(void) {
    char string[] = "Friede freude Eierkuchen freuen sich freundlich";
    char toFind[] = "freuen";

    printf("inString(\"%s\", \"%s\") -> %d\n", string, toFind, inString(string, toFind));
    return 0;
}

В тестовом коде вы используете fgets для чтения строк из стандартного ввода. Обе строки, скорее всего, будут содержать окончание '\n', и поэтому вы не найдете совпадения для "freuen\n", если только оно не находится в конце string, у которого также есть последнее '\n'. Исправьте это, удалив файл '\n'. Вот простой способ удалить его, который также работает, если его там нет:

string[strcspn(string, "\n")] = '\0';
toFind[strcspn(toFind, "\n")] = '\0';
person chqrlie    schedule 22.11.2015
comment
вау спасибо за ответ! Не могли бы вы объяснить мне, что делает последняя строка (где возвращается значение)? (Никогда не видел этот синтаксис..) - person user1641602; 22.11.2015
comment
Тернарный оператор похож на выражение if/then/else. последнюю строку можно было бы написать так: if (p != NULL) return p - string; else return -1;. Компилятор, вероятно, сгенерирует один и тот же код для обеих версий. - person chqrlie; 22.11.2015
comment
Большое спасибо! к сожалению, это работает не для всех строк: string[] = friede freude eierkuchen freuen sich freundlich | toFind[] = свободный | результат = -1 - person user1641602; 22.11.2015
comment
Тернарный оператор - самый уродливый оператор C :)) - person Michi; 22.11.2015
comment
Что ж, это может быть правдой, но почему strstr() также терпит неудачу точно так же, как моя самореализация? - person user1641602; 22.11.2015
comment
Похоже, что оба метода (мой и strstr()) могут найти искомую строку только тогда, когда она находится в конце всей строки, иначе они всегда возвращают -1. Почему это так? - person user1641602; 22.11.2015
comment
Нисколько. strstr делает именно то, что вы хотите. Проблема в другом. Можете ли вы отредактировать свой вопрос и опубликовать код, где вы звоните inString()? Моя версия возвращает 25. - person chqrlie; 22.11.2015
comment
Я обновил его, или вам нужно больше кода? (например, как я могу ввести два массива?) - person user1641602; 22.11.2015
comment
Можете ли вы опубликовать полный код простого примера, который показывает сбой. Я подозреваю, что определение string и/или toFind не совсем то, что вы подразумеваете в своих комментариях. - person chqrlie; 22.11.2015
comment
Я обновил код, вот так выглядит моя программа (ну, с разбивкой по проблеме). - person user1641602; 22.11.2015
comment
Я был прав, подозревая другую проблему. Я обновил свой ответ. - person chqrlie; 22.11.2015
comment
Всегда эти нулевые терминаторы и новые строки... бросьте меня. Большое спасибо за Вашу помощь! - person user1641602; 22.11.2015

Хорошо, во-первых, большое спасибо chqrlie за то, что так много мне помогли и указали мне правильное направление.

Я попробовал то, что вы упомянули, удалив '\ n' из строки [] и toFind [], но все равно столкнулся с той же проблемой.

Что решило это для меня, так это использование моего метода, опубликованного в начале, но вместо strcmp() с использованием memcmp(). Это сделало это. Итак, вот мой inString(), как он выглядит сейчас:

int inString(char string[], char toFind[]){

     int i_toFind, i_string, check = 0, start = -1;

     for(i_toFind = 0; i_toFind < getLength(toFind)-1; i_toFind++){

       for(i_string = 0; i_string < getLength(string); i_string++){

         if(memcmp(&string[i_string], &toFind[i_toFind], 1)==0){

           if(start == -1){

             start = i_string;

           }

           check++;
           i_toFind++;

           if(check == getLength(toFind)-1){
             return start;
           }

         }
         else{

         i_toFind = i_toFind - check;

         check = 0;
         start = -1;

         }

       }

     }

     return -1;

}

дает мне также 25 на примере, упомянутом выше.

person user1641602    schedule 22.11.2015
comment
Ваша функция все еще слишком сложна. Вместо использования memcmp для сравнения по одному байту за раз, для чего достаточно простого оператора ==, используйте его для сравнения strlen(toFind) байтов в последовательных позициях в string, от 0 до strlen(string)-strlen(toFind) включительно. Сначала вычислите длину строки и используйте один цикл for для тестов, верните первое совпадающее смещение и -1, если все сравнения не пройдены. - person chqrlie; 22.11.2015