Как правильно разбивать текст на слова с учетом разных разделителей
Что проверяют
Это задача на живое кодирование, и оценивают не знание Split, а то, как вы уточняете требования и закрываете крайние случаи. Следом просят написать тесты или ускорить решение для большого файла.
Как отвечать
Сначала задайте вопросы: что считается словом («C#», «что-то», числа), важен ли регистр, какого размера вход, критичны ли аллокации. Потом решение по нарастающей:
string.Split: для фиксированного набора символов достаточноtext.Split(new[] { ' ', ',', '.', ';', '!', '?' }, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).TrimEntriesпоявился в .NET 5, а безRemoveEmptyEntriesподряд идущие разделители дают пустые элементы;- поиск слов регуляркой: надёжнее описать само слово, а не разделители:
Regex.Matches(text, @"[\p{L}\p{N}]+(?:[-'][\p{L}\p{N}]+)*"). Новый символ-разделитель во входе ничего не сломает. Шаблон лучше объявить через[GeneratedRegex]наpartial-методе (.NET 7+), тогда код регулярки генерируется при компиляции; - без лишних аллокаций:
Regex.EnumerateMatches(.NET 7+) отдаётValueMatchс позицией и длиной, а ручной проход поReadOnlySpan<char>сchar.IsLetterOrDigitдаёт O(n) и ни одной лишней строки, пока слово реально не понадобится.
Проговорите сложность: любой из вариантов линейный по длине текста, разница в аллокациях и константе. И сразу назовите тесты: null, пустая строка, строка из одних разделителей, ведущие и хвостовые пробелы, несколько разделителей подряд, дефис и апостроф внутри слова, «C#».
Где спотыкаются
Берут Regex.Split(text, @"\W+") и считают, что пустых элементов не будет. Если строка начинается или заканчивается разделителем, Regex.Split вернёт пустую строку в начале или в конце, а RemoveEmptyEntries у него нет.
Не замечают, что \W съедает # и +: «C#» превращается в «C», с «C++» то же самое. В .NET \w понимает Unicode, так что кириллица уцелеет, а символы в названиях технологий нет.
Создают new Regex(...) внутри цикла или метода, который зовут на каждую строку. Статические методы Regex кешируют последние шаблоны (по умолчанию 15, Regex.CacheSize), а экземпляр в цикле разбирается заново каждый раз.