Есть ли что-то подобное startsWith(str_a, str_b)
в стандартной библиотеке Си?
Он должен принимать указатели на две строки, которые заканчиваются нулевыми байтами, и сообщать мне, появляется ли первая также полностью в начале второй.
Примеры:
"abc", "abcdef" -> true
"abcdef", "abc" -> false
"abd", "abdcef" -> true
"abc", "abc" -> true
c
string
comparison
startswith
thejh
источник
источник
Ответы:
По-видимому, для этого нет стандартной функции C. Так:
bool startsWith(const char *pre, const char *str) { size_t lenpre = strlen(pre), lenstr = strlen(str); return lenstr < lenpre ? false : memcmp(pre, str, lenpre) == 0; }
Обратите внимание, что приведенное выше красиво и ясно, но если вы делаете это в тесном цикле или работаете с очень большими строками, это не обеспечивает наилучшей производительности, поскольку оно сканирует всю длину обеих строк впереди (
strlen
). Такие решения, как wj32 или Christoph, могут предложить лучшую производительность (хотя этот комментарий о векторизации выходит за рамки моего понимания C). Также обратите внимание на решение Фреда Фу, которое избегаетstrlen
onstr
(он прав, в этом нет необходимости, если вы используетеstrncmp
вместоmemcmp
). Имеет значение только для (очень) больших струн или многократного использования в узких петлях, но когда это имеет значение, это имеет значение.источник
memcmp
наstrncmp
здесь , и это быстрее. UB нет, потому что известно, что обе строки содержат как минимумlenpre
байты.strncmp
проверяет каждый байт обеих строк на NUL, ноstrlen
вызовы уже гарантировали, что их нет. (Но у него все еще есть удар по производительности, о котором вы упомянули, когдаpre
илиstr
дольше, чем фактическая обычная начальная последовательность.)memcmp
выше не соответствовало бы другому ответу здесь, я пошел дальше и изменил его в ответе.strlen
иmemcmp
может быть реализовано с очень быстрыми аппаратными инструкциями, аstrlen
s может помещать строки в кеш, избегая двойного попадания в память. На таких машинахstrncmp
можно было бы реализовать как дваstrlen
s и a,memcmp
как это, но это было бы рискованно для автора библиотеки, так как это могло бы занять гораздо больше времени для длинных строк с короткими общими префиксами. Здесь это попадание явное, иstrlen
s выполняются только один раз каждый (strlen
+ Фред Фуstrncmp
сделал бы 3).Для этого нет стандартной функции, но вы можете определить
bool prefix(const char *pre, const char *str) { return strncmp(pre, str, strlen(pre)) == 0; }
Нам не нужно беспокоиться о
str
том, чтобы быть короче,pre
потому что согласно стандарту C (7.21.4.4/2):источник
strncmp
.strncmp
иstrlen
не называется strncmp.Я бы, наверное, пошел
strncmp()
, но просто для удовольствия, сырую реализацию:_Bool starts_with(const char *restrict string, const char *restrict prefix) { while(*prefix) { if(*prefix++ != *string++) return 0; } return 1; }
источник
strncmp
, если ваш компилятор действительно хорош в векторизации, потому что авторы glibc уверены :-)Я не специалист по написанию элегантного кода, но ...
int prefix(const char *pre, const char *str) { char cp; char cs; if (!*pre) return 1; while ((cp = *pre++) && (cs = *str++)) { if (cp != cs) return 0; } if (!cs) return 0; return 1; }
источник
Используйте
strstr()
функцию.Stra == strstr(stra, strb)
источник
Оптимизировано (v.2. - исправлено):
uint32 startsWith( const void* prefix_, const void* str_ ) { uint8 _cp, _cs; const uint8* _pr = (uint8*) prefix_; const uint8* _str = (uint8*) str_; while ( ( _cs = *_str++ ) & ( _cp = *_pr++ ) ) { if ( _cp != _cs ) return 0; } return !_cp; }
источник
startsWith("\2", "\1")
возвращает 1,startsWith("\1", "\1")
также возвращает 1Поскольку я запустил принятую версию и у меня возникла проблема с очень длинной строкой, мне пришлось добавить следующую логику:
bool longEnough(const char *str, int min_length) { int length = 0; while (str[length] && length < min_length) length++; if (length == min_length) return true; return false; } bool startsWith(const char *pre, const char *str) { size_t lenpre = strlen(pre); return longEnough(str, lenpre) ? strncmp(str, pre, lenpre) == 0 : false; }
источник
Или комбинация двух подходов:
_Bool starts_with(const char *restrict string, const char *restrict prefix) { char * const restrict prefix_end = prefix + 13; while (1) { if ( 0 == *prefix ) return 1; if ( *prefix++ != *string++) return 0; if ( prefix_end <= prefix ) return 0 == strncmp(prefix, string, strlen(prefix)); } }
РЕДАКТИРОВАТЬ: приведенный ниже код НЕ работает, потому что, если strncmp возвращает 0, неизвестно, был ли достигнут завершающий 0 или длина (block_size).
Дополнительная идея - сравнивать по блокам. Если блок не равен, сравните этот блок с исходной функцией:
_Bool starts_with_big(const char *restrict string, const char *restrict prefix) { size_t block_size = 64; while (1) { if ( 0 != strncmp( string, prefix, block_size ) ) return starts_with( string, prefix); string += block_size; prefix += block_size; if ( block_size < 4096 ) block_size *= 2; } }
Константы
13
,64
,4096
, а также потенциированиеblock_size
лишь догадки. Его нужно будет выбрать для используемых входных данных и оборудования.источник
block_size
увеличение должно происходить после увеличения указателя. Теперь исправлено.