Проверить, существует ли и где подстрока

Я видел этот вопрос, но ни один из ответов не был таким, каким Я искал. Я пробовал strstr, но он возвращает указатель вместо целочисленного индекса.

Мне нужно найти, содержит ли строка a строку b, и если да, то где она находится, вроде индекса, возвращаемого strcmp. Есть ли функция или простой способ сделать это в C?

Например, если a — это «foobar», а b — это «bar», то эта функция/метод вернет 3, потому что «bar» находится в индексе 3 «foobar».

Любая помощь приветствуется!


person MD XF    schedule 04.11.2016    source источник
comment
strstr делает именно это - если он не работает так, как вы ожидаете, вы должны опубликовать свой код здесь и объяснить проблему, связанную с поведением strstr в нем.   -  person DUman    schedule 04.11.2016
comment
"bar" находится по индексу 3 в "foobar", а не 2.   -  person mch    schedule 04.11.2016


Ответы (3)


Вы можете использовать для этого strstr вместе с некоторой арифметикой указателя.

char *result = strstr(a, b);
if (result != NULL) {
    printf("index = %tu\n", result - a);
}

Здесь result указывает на определенное количество байтов впереди a. Итак, если вы вычитаете два, это ваш индекс.

person dbush    schedule 04.11.2016
comment
Правильный. Обратите внимание, что библиотека Microsoft C и некоторые другие могут не поддерживать спецификатор преобразования %lu. В таких системах используйте printf("index = %llu\n", (unsigned long long)result - a); - person chqrlie; 05.11.2016

Вы можете легко преобразовать возвращенный указатель в индекс, вычитая указатель на начало строки, a:

char *p = strstr(a, b);
int i = p ? p - a : -1;

(Кроме того, strcmp не возвращает индекс.)

person melpomene    schedule 04.11.2016
comment
Разве strcmp не возвращает первую точку, в которой первый аргумент отличается от второго? - person MD XF; 04.11.2016
comment
@MD XF - нет. strcmp() может только определить характер различия (один лексикографически меньше, равен или больше другого). Он не возвращает никакой информации о том, где находится разница. - person Peter; 05.11.2016
comment
@melpomene - i было бы лучше с типом ptrdiff_t. Это результат вычитания указателей, и результат не обязательно может быть сохранен в int. В остальном с вашим подходом все в порядке — если strstr(a,b), если не NULL, не может дать результат меньше a, поэтому вычитание p-a не даст отрицательного значения. - person Peter; 05.11.2016
comment
@MDXF - strcmp() сравнивает строки в лексическом порядке. Рассмотрим ваш пример foobar и bar. Он сравнивает f и b и возвращает 4, потому что b на 4 порядка опережает f. Если вы сравните foobar и Foobar, он вернет 32, потому что f и F разделены на 32 порядка. Если первый байт равен, он продолжит сравнение следующих байтов, пока не найдет разницу или конец любой строки. - person alvits; 05.11.2016
comment
@melpomene /tmp/test foobar Foobar - результат: The difference between foobar and Foobar is 32. Вот еще The difference between foobar and bar is 4. Заявление: printf("The difference between %s and %s is %d\n", argv[1], argv[2], strcmp(argv[1], argv[2]));. Так что же не так? Хочешь объяснить? Если вы сомневаетесь, протестируйте код. - person alvits; 05.11.2016
comment
@melpomene programiz.com/c-programming/library-function/ string.h/strcmp - Первый несопоставленный символ между строками str1 и str2 является третьим символом. Значение ASCII "c" равно 99, а значение ASCII "C" равно 67. Таким образом, при сравнении строк str1 и str2 возвращается значение 32. - person alvits; 05.11.2016
comment
@alvits: Учебник от programiz.com вводит в заблуждение: значение 32 может быть любым значением int, большим, чем 0. Ничего нельзя предположить о возвращаемом значении strcmp(a,b);, кроме того факта, что оно может быть 0, если строки одинаковы, отрицательным, если a лексикографически меньше, чем b, и положительным в противном случае. - person chqrlie; 05.11.2016
comment
@chqrlie — вот исходный код NetBSD для strcmp() ftp.netbsd.org/pub/NetBSD/NetBSD-current/src/common/lib/libc/ и Microsoft research.microsoft.com/en-us/um/redmond/projects/invisible/src/. В обоих случаях они возвращают разницу между отличающимися символами. Вы все еще можете спорить, но все источники показывают, что они возвращают разницу символа, который отличается. - person alvits; 05.11.2016
comment
@chqrlie - а вот из GNU ask.systutorials.com/71 / как опубликовано на systutorials.com - person alvits; 05.11.2016
comment
@alvits: Прочтите описание на programiz.com, C Standard, cppreference... Все скажут вам именно то, что я написал в своем комментарии. Тот факт, что несколько реализаций strcmp() возвращают разницу несоответствующих символов, не имеет значения, это просто эффективный способ реализации strcmp. Если ваш код полагается на это, это подделка. Сравнивать возвращаемое значение strcmp() с чем-то еще, кроме 0, бессмысленно. - person chqrlie; 05.11.2016
comment
@chqrlie - <я>. Сравнивать возвращаемое strcmp() значение с чем-то другим, кроме 0, бессмысленно — я не спорю с этим. Вы абсолютно правы. Я защищаю свой предыдущий комментарий на основе реальных реализаций в glibc относительно первого комментария MD XF в этом ответе. - person alvits; 05.11.2016
comment
@chqrlie - Учебник от programiz.com вводит в заблуждение: значение 32 может быть любым значением int больше 0 - исходный код, который я разместил, предназначен для резервного копирования моего утверждения и опровержения того, что возвращаемое значение не является просто любое целое число. - person alvits; 05.11.2016
comment
@alvits: проблема с вашей формулировкой в ​​том, что она может заставить других программистов поверить, что возвращаемое значение представляет собой разницу несоответствующих символов. Учитывая исходное заблуждение ОП о strcmp(), важно быть кратким и недвусмысленным. Кстати, вы заметили ошибку в реализации MSVC? - person chqrlie; 05.11.2016
comment
@alvits: этот комментарий неверен и вводит в заблуждение: strcmp() сравнивает строки в лексическом порядке. Рассмотрим ваш пример foobar и bar. Он сравнит f и b и вернет 4, потому что b на 4 порядка опережает f. Если вы сравните foobar и Foobar, он вернет 32, потому что f и F отличаются друг от друга на 32 порядка. Если первый байт равен, он продолжит сравнение следующих байтов, пока не найдет разницу или конец любой из строк - person chqrlie; 05.11.2016
comment
@chqrlie - это утверждение должно объяснить MD XF, почему он видит возвращаемые значения, которые, по его мнению, могли быть местом несоответствия. Он основан на текущей реализации glibc в GNU и NetBSD и пакете Microsoft. Если вы найдете лучшую формулировку для описания текущей реализации, сообщите мне, и я перефразирую. - person alvits; 05.11.2016
comment
@chqrlie - вы все еще можете утверждать, что текущая реализация не является стандартной и может измениться в будущем. Я согласен с тем, что никто не должен полагаться на значение, кроме сравнения с 0 меньше 0 или больше 0, что будет доказательством в будущем. - person alvits; 05.11.2016
comment
Комментарий в исходном коде MSVC довольно ясен: Сравните строки, на которые указывает pSrt1, pSrt2. Возвращает 0, если то же самое, ‹ 0, если pStr1 меньше, чем pStr2, > 0 в противном случае. Объяснение OP того, как конкретная библиотека вычисляет разные возвращаемые значения, контрпродуктивно: многие случайные читатели поверят, что вы описываете то, что она должна делать. вместо того, что он делает. Такие заблуждения трудно развеять. - person chqrlie; 05.11.2016

Ну вот:

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

int main()
{
    char str1[]="foobar";
    char str2[]="bar";
    char *ptr;
    if((ptr = strstr(str1, str2)) != NULL) {
        printf("Start offset: %td\n", ptr - str1);
    }

    return 0;
}
person Kamyar Souri    schedule 04.11.2016
comment
Тестирование не спасет от UB. - person melpomene; 05.11.2016
comment
Насколько я понимаю, ptrdiff_t - это подписанное целое число, которое ожидает %d в printf. Это правда? - person Kamyar Souri; 05.11.2016
comment
Нет: ptrdiff_t — это тип со знаком, который может быть int или больше. В 64-разрядной версии Linux это long, в 64-разрядной версии Windows это long long. Либо используйте printf("Start offset: %td\n", ptr - str1);, либо printf("Start offset: %lld\n", (long long)(ptr - str1)); - person chqrlie; 05.11.2016