Я хочу преобразовать std::string
в нижний регистр. Я знаю о функции tolower()
, однако в прошлом у меня были проблемы с этой функцией, и в любом случае она вряд ли идеальна, так как использование с std::string
требовало бы итерации по каждому символу.
Есть ли альтернатива, которая работает 100% времени?
c++
string
c++-standard-library
tolower
Konrad
источник
источник
Ответы:
Адаптировано из не так часто задаваемых вопросов :
Вы действительно не собираетесь уходить без итерации каждого персонажа. Там нет никакого способа узнать, является ли символ строчными или прописными в противном случае.
Если вы действительно ненавидите
tolower()
, вот специальная альтернатива только для ASCII, которую я не рекомендую вам использовать:Имейте в виду, что
tolower()
можно выполнять замену только одного байта символа, что плохо подходит для многих сценариев, особенно при использовании многобайтовой кодировки, такой как UTF-8.источник
char
к::tolower(int)
.). Вы должны убедиться, что не передаете отрицательное значение.::tolower
может привести к сбою, это UB для ввода не-ASCII.Boost предоставляет строковый алгоритм для этого :
Или для не на месте :
источник
to_lower_copy
ТЛ; др
Используйте библиотеку ICU . Если вы этого не сделаете, ваша процедура преобразования будет молча нарушать случаи, о которых вы, вероятно, даже не подозреваете.
Сначала вы должны ответить на вопрос: какова ваша кодировка
std::string
? Это ISO-8859-1? Или, возможно, ISO-8859-8? Или кодовая страница Windows 1252? Знает ли это то, что вы используете для преобразования прописных букв в строчные? (Или это с треском проваливается для персонажей более0x7f
?)Если вы используете UTF-8 (единственный разумный выбор среди 8-битных кодировок) в
std::string
качестве контейнера, вы уже обманываете себя, полагая, что вы все еще контролируете вещи, потому что вы храните многобайтовую последовательность символов в контейнере который не знает о многобайтовой концепции. Даже.substr()
такая простая вещь, как бомба замедленного действия. (Поскольку разбиение многобайтовой последовательности приведет к недопустимой (под) строке.)И как только вы попробуете что-то подобное
std::toupper( 'ß' )
, в любой кодировке, у вас будут большие проблемы. (Потому что просто невозможно сделать это «правильно» со стандартной библиотекой, которая может доставить только один символ результата, а не"SS"
необходимый здесь.) [1] Другой примерstd::tolower( 'I' )
, который должен давать разные результаты в зависимости от локали . В Германии'i'
было бы правильно; в индейке,'ı'
(LATIN SMALL LETTER I DOTLESS I) является ожидаемым результатом (который, опять же, составляет более одного байта в кодировке UTF-8). Еще один пример - греческая сигма , прописные'∑'
, строчные'σ'
... за исключением конца слова, где оно находится'ς'
.Так, любое преобразование, которое работает с символом за раз или, что еще хуже, с байтом за раз, нарушается по замыслу.
Тогда есть смысл, что стандартная библиотека, для чего она это способен делать, в зависимости от того , локали поддерживается на машине ваше программное обеспечение работает на ... и что делать , если это не так ?
Так что вы действительно ищете строковый класс, который способен справиться со всем этим правильно, и это не какой-либо из
std::basic_string<>
вариантов .(Примечание C ++ 11:
std::u16string
иstd::u32string
это лучше ., Но все еще не совершенны C ++ 20 принесstd::u8string
, но все это сделать , это указать кодировку во многих других отношениях они все еще остаются в неведении о механике Unicode, как нормализация, обобщению, ... .)Хотя Boost выглядит неплохо, с точки зрения API, Boost.Locale по сути является оболочкой для ICU .Если Boost скомпилирован с поддержкой ICU ... если нет, Boost.Locale ограничен поддержкой локали, скомпилированной для стандартной библиотеки.
И поверьте мне, заставить Boost компилироваться с ICU иногда бывает очень больно. (Для Windows нет предварительно скомпилированных двоичных файлов, так что вам придется поставлять их вместе с вашим приложением, и это открывает новую банку с червями ...)
Поэтому лично я бы порекомендовал получить полную поддержку Unicode прямо изо рта лошади и напрямую использовать библиотеку ICU :
Компиляция (с G ++ в этом примере):
Это дает:
Обратите внимание, что преобразование Σ <-> σ в середине слова и преобразование Σ <-> ς в конце слова. Никакое
<algorithm>
решение не может дать вам это.[1] В 2017 году Совет по немецкой орфографии постановил, что "ẞ" U + 1E9E LATIN CAPITAL LAPTER SHARP S может быть официально использован, как вариант, помимо традиционной конверсии "SS", чтобы избежать двусмысленности, например, в паспортах (где имена пишутся с большой буквы) ). Мой прекрасный пример, устарел по решению комитета ...
источник
toupper
иtolower
все еще работают над отдельными персонажами. Класс строки все еще не имеет понятия нормализации (например, кодируется ли «ü» как «u с диарезом» или «u + комбинирующий диарез») или где строка может быть или не быть разделена. Список можно продолжить. Строка u8string (как и другие стандартные классы строк) подходит для "прохождения". Но если вы хотите обработать Unicode, вам нужен ICU.Используя основанный на диапазоне цикл for C ++ 11, более простой код будет:
источник
Если строка содержит символы UTF-8 вне диапазона ASCII, то boost ::gorith :: to_lower не преобразует их. Лучше использовать boost :: locale :: to_lower, когда задействован UTF-8. См. Http://www.boost.org/doc/libs/1_51_0/libs/locale/doc/html/conversions.html.
источник
Это продолжение ответа Стефана Май: если вы хотите поместить результат преобразования в другую строку, вам необходимо предварительно выделить место для хранения перед вызовом
std::transform
. Поскольку STL сохраняет преобразованные символы в итераторе назначения (увеличивая его на каждой итерации цикла), размер строки назначения не будет автоматически изменяться, и вы рискуете переполнить память.источник
Другой подход с использованием диапазона на основе для цикла с контрольной переменной
источник
Насколько я вижу, библиотеки Boost действительно плохо влияют на производительность. Я проверил их unordered_map в STL, и в среднем он был в 3 раза медленнее (лучший случай 2, худший - 10 раз). Также этот алгоритм выглядит слишком низким.
Разница настолько велика, что я уверен, что любое дополнение, которое вам нужно сделать,
tolower
чтобы сделать его равным ускорению «для ваших нужд», будет намного быстрее чем буст.Я провел эти тесты на Amazon EC2, поэтому производительность менялась во время теста, но вы все еще поняли.
-O2
сделал это так:Источник:
Я предполагаю, что мне следует пройти тесты на выделенной машине, но я буду использовать этот EC2, поэтому мне не нужно тестировать его на моей машине.
источник
Простейший способ преобразовать строку в loweercase, не заботясь о пространстве имен std, заключается в следующем
1: строка с / без пробелов
2: строка без пробелов
источник
std::ctype::tolower()
из стандартной библиотеки локализации C ++ правильно сделает это за вас. Вот пример, извлеченный из справочной страницы Tolowerисточник
const
? Это, кажется, делает его немного более грязным (например, это не похоже на то, что вы можете использоватьf.tolower()
), так как вам нужно поместить символы в новую строку. Будете ли вы использоватьtransform()
и что-то вродеstd::bind1st( std::mem_fun() )
для оператора?tolower
сlocale
параметром неявный вызовuse_facet
является узким местом производительности. Один из моих коллег добился увеличения скорости на несколько 100%, заменивboost::iequals
(что имеет эту проблему) версией, котораяuse_facet
вызывается только один раз за пределами цикла.Альтернативой Boost является POCO (pocoproject.org).
POCO предлагает два варианта:
Версии «на месте» всегда имеют «InPlace» в названии.
Обе версии демонстрируются ниже:
источник
Есть способ преобразовать верхний регистр в нижний БЕЗ выполнения тестов , и это довольно просто. Использование функции / макроса isupper () для clocale.h должно решить проблемы, связанные с вашим местоположением, но если нет, вы всегда можете настроить UtoL [] на свое усмотрение.
Учитывая, что символы C на самом деле являются просто 8-битными целыми числами (игнорируя широкие наборы символов на данный момент), вы можете создать 256-байтовый массив, содержащий альтернативный набор символов, и в функции преобразования использовать символы в вашей строке в качестве индексов в массив преобразования.
Вместо сопоставления 1-в-1 задайте для членов массива в верхнем регистре значения BYTE int для символов в нижнем регистре. Вы можете найти islower () и isupper () полезными здесь.
Код выглядит так ...
Этот подход, в то же время, позволит вам переназначить любые другие символы, которые вы хотите изменить.
Этот подход имеет одно огромное преимущество при работе на современных процессорах: нет необходимости делать предсказание ветвления, поскольку нет тестов, содержащих ветвление. Это сохраняет логику предсказания ветвления ЦП для других циклов и предотвращает задержки конвейера.
Некоторые здесь могут признать этот подход тем же, который использовался для преобразования EBCDIC в ASCII.
источник
Поскольку ни в одном из ответов не упоминалась будущая библиотека Ranges, которая доступна в стандартной библиотеке начиная с C ++ 20 и в настоящее время отдельно доступна на GitHub as
range-v3
, я хотел бы добавить способ выполнить это преобразование, используя ее.Чтобы изменить строку на месте:
Чтобы сгенерировать новую строку:
(Не забудьте
#include <cctype>
и требуемые заголовки диапазонов.)Примечание: использование в
unsigned char
качестве аргумента лямбды основано на cppreference , который гласит:источник
Мой собственный шаблон функций, который выполняет верхний / нижний регистр.
источник
towlower
для широких символов, который поддерживает UTF-16.Вот метод макроса, если вы хотите что-то простое:
Тем не менее, обратите внимание, что комментарий @ AndreasSpindler к этому ответу все еще является важным соображением, однако, если вы работаете над чем-то, что не является просто символами ASCII.
источник
void strtoupper(std::string& x) { std::transform (x.begin(), x.end(), x.begin(), ::toupper); }
x
может быть допустимым выражением, которое просто правильно компилируется, но из-за макросов даст совершенно фиктивные результаты.Для получения дополнительной информации: http://www.cplusplus.com/reference/locale/tolower/
источник
нет
Есть несколько вопросов, которые вы должны задать себе, прежде чем выбрать метод в нижнем регистре.
Получив ответы на эти вопросы, вы можете начать искать решение, которое соответствует вашим потребностям. Не существует одного размера, который подходит всем, кто работает везде!
источник
Попробуйте эту функцию :)
источник
На платформах Microsoft вы можете использовать
strlwr
семейство функций: http://msdn.microsoft.com/en-us/library/hkxwh33z.aspxисточник
Фрагмент кода
источник
Используйте fplus :: to_lower_case ().
(fplus: https://github.com/Dobiasd/FunctionalPlus .
Поиск 'to_lower_case' в http://www.editgym.com/fplus-api-search/ )
источник
Скопируйте, потому что было запрещено улучшать ответ. Спасибо ТАК
Объяснение:
for(auto& c : test)
представляет собой диапазон на основе цикла такого типа :for (
range_declaration
:
range_expression
)
loop_statement
range_declaration
:auto& c
Здесь автоматический спецификатор используется для автоматического вывода типа. Таким образом, тип вычитается из инициализатора переменных.
range_expression
:test
Диапазон в этом случае - символы строки
test
.Символы строки
test
доступны в качестве ссылки внутри цикла for с идентификаторомc
.источник
C ++ не имеет методов tolower или toupper для строки, но он доступен для char. Можно легко прочитать каждый символ строки, преобразовать его в нужный регистр и вернуть обратно в строку. Пример кода без использования сторонней библиотеки:
Для символьной операции над строкой: для каждого символа в строке
источник
Это может быть еще одна простая версия для преобразования прописных букв в строчные и наоборот. Я использовал версию сообщества VS2017 для компиляции этого исходного кода.
Примечание: если есть специальные символы, то их нужно обрабатывать с помощью проверки состояния.
источник
Я пробовал std :: transform, все, что я получаю, это отвратительная ошибка компиляции stl, которую могут понять только друиды 200 лет назад (не может конвертировать из в flibidi flabidi flu)
это прекрасно работает и может быть легко настроено
источник