Я хочу преобразовать std::string в нижний регистр. Мне известна функция tolower(), однако в прошлом у меня были проблемы с этой функцией, и в любом случае она вряд ли идеальна, так как использование с std::string потребует повторения каждого символа.
Есть ли альтернатива, которая работает 100% времени?
Почему именно этот вопрос снижает рейтинг? У меня нет проблем с повторением моей строки, но я спрашиваю, есть ли другие функции, кроме tolower (), toupper () и т. д.
Если у вас есть массив символов в стиле C, то я думаю, вы сможете добавить ox20202020 в каждый блок из 4 символов (при условии, что ВСЕ они уже в верхнем регистре), чтобы преобразовать 4 символа в нижний регистр за раз.
@Dan: Если они уже могут быть в нижнем регистре, но определенно A-Z или a-z, вы можете использовать ИЛИ с 0x20 вместо добавления. Одна из тех оптимизаций, которые почти никогда не стоят того ...
Я не знаю, почему его проголосовали против ... конечно, он сформулирован немного странно (потому что вам нужно как-то перебирать каждый элемент), но это правильный вопрос
Когда я печатаю вопросы, я просто выбрасываю то, что в данный момент находится в моем мысленном буфере. Это не всегда имеет смысл. ;)
@onebyone: Ах, никогда не думал об этом! Ну, я никогда не имел в виду, что это был полезный способ сделать это, просто это возможно. На самом деле, мне было бы больше интересно попробовать что-то подобное с большими текстами на графическом процессоре, просто для смеха.
Это хороший вопрос. Большинство языков сценариев обрабатывают это так, как вы ожидаете.
Обратите внимание, что выбранный вами ответ потенциально имеет неопределенное поведение. Несмотря на все голоса «за», это небезопасно.
Я думаю, что под «повторением каждого символа» подразумевается «явное повторение каждого символа», например, чтобы уменьшить раздувание кода или подробный код.
Примечание: tolower() не работает в 100% случаев. Операции с нижним и верхним регистром применяются только к символам, а std :: string по сути представляет собой массив байтов, а не символов. Обычный tolower подходит для строки ASCII, но он не будет правильно писать строчные буквы latin-1 или utf-8. Вы должны знать кодировку строки и, возможно, декодировать ее, прежде чем сможете строчные буквы.
Прочитав все эти ответы и многократные комментарии, я не уверен, что это то, с чем вы хотели бы иметь дело непосредственно в своей программе. Возможно, вы захотите использовать автономный модуль, который принимает строки и аргументы кодирования / локали и дает только хороший результат, если его можно проверяемо преобразовать, что, по-видимому, требует использования библиотеки ICU для максимальной надежности. В качестве альтернативы, вы всегда можете играть в него еще безопаснее и удалить требование использования проверки регистра в качестве проверки, если только приложение не переводит эти буквы в нижний регистр.
DevSolar дает отличный ответ, который содержит очень хороший пример того, почему это не может быть решено как чисто программное упражнение. Он, кажется, согласен, а также не согласен со мной по этому поводу и, по-видимому, не будет включать в себя то, что вы должны знать о культурных изменениях, чтобы любое решение сработало. Это не может быть решено идеально на все времена и во всех случаях.





Boost предоставляет для этого строковый алгоритм:
#include <boost/algorithm/string.hpp>
std::string str = "HELLO, WORLD!";
boost::algorithm::to_lower(str); // modifies str
#include <boost/algorithm/string.hpp>
const std::string str = "HELLO, WORLD!";
const std::string lower_str = boost::algorithm::to_lower_copy(str);
Я полагаю, у этого нет тех же проблем, что и у tolower с вводом ASCII?
Не работает для не-ASCII-7.
Есть ли версия этого не на месте?
@Ray, да, to_lower_copy
Адаптировано из Не очень часто задаваемые вопросы:
#include <algorithm>
#include <cctype>
#include <string>
std::string data = "Abc";
std::transform(data.begin(), data.end(), data.begin(),
[](unsigned char c){ return std::tolower(c); });
Вы действительно не обойдетесь без повторения каждого персонажа. В противном случае невозможно узнать, является ли символ строчным или прописным.
Если вы действительно ненавидите tolower(), вот специализированная альтернатива только для ASCII, которую я не рекомендую вам использовать:
char asciitolower(char in) {
if (in <= 'Z' && in >= 'A')
return in - ('Z' - 'z');
return in;
}
std::transform(data.begin(), data.end(), data.begin(), asciitolower);
Имейте в виду, что tolower() может выполнять замену только для однобайтовых символов, что плохо подходит для многих сценариев, особенно при использовании многобайтовой кодировки, такой как UTF-8.
Это потрясающе, я всегда задавался вопросом, как лучше всего это сделать. Я понятия не имел, как использовать std :: transform. :)
uberjumper: На самом деле с вызовами STL связано много накладных расходов, особенно для небольших строк. Решения, использующие цикл for и tolower, вероятно, намного быстрее.
(Может быть, и старые алгоритмы, о которых идет речь, мало изменились) @Stefan Mai: Какие «накладные расходы» возникают при вызове алгоритмов STL? Функции довольно скудные (то есть простые для циклов) и часто встроены, поскольку у вас редко бывает много вызовов одной и той же функции с одинаковыми параметрами шаблона в одном и том же модуле компиляции.
@eq Справедливо, мои тесты согласуются с вами при компиляции с -O3 (хотя STL на самом деле превосходит код, настроенный вручную, поэтому мне интересно, использует ли компилятор какие-то трюки). Хотя отладка кода STL все еще медвежья;).
Это непереносимое решение могло бы быть быстрее. Вы можете избежать ветвления таким образом: inChar | = 0x20. Я думаю, что это самый быстрый способ преобразовать верхний ascii в нижний. Если вы хотите преобразовать нижнее значение в верхнее, тогда: inChar & = ~ 0x20.
@MichalW Это работает, если у вас есть только буквы, что не всегда так. Если вы находитесь в этой сфере, вы, вероятно, можете добиться большего, используя битовые маски для длинных строк - принимайте по 8 символов за раз;)
Каждый раз, когда вы предполагаете, что символы являются ASCII, Бог убивает котенка. :(
Ваш первый пример потенциально имеет неопределенное поведение (передача char в ::tolower(int)). Вам необходимо убедиться, что вы не передаете отрицательное значение.
-1 такое использование ::tolower может привести к сбою, это UB для ввода, отличного от ASCII.
Хотя это должно быть каноническим способом сделать это в разумном мире, у него слишком много проблем, чтобы рекомендовать его. Во-первых, tolower из ctype.h не работает с юникодом. Во-вторых, locale.h, который включен во многие другие заголовки библиотеки std, определяет конфликтующий tolower, который вызывает головную боль, см. stackoverflow.com/q/5539249/339595. Лучше всего использовать std :: locale или boost :: locale :: to_lower, как предлагают другие ответы.
:: Towlower, если вы международный / используете широкие символы
@MichalW Эй, ты можешь объяснить, что ты там написал? Кроме того, почему мы используем :: в ::tolower?
@StefanMai Привет. Почему необходимо "::" перед "tolower"? Я этого не понимаю.
Обратите внимание, что это работает для Unicode, если вы используете std::u32string и ваш языковой стандарт C совместим с Unicode.
:: необходим перед tolower, чтобы указать, что он находится во внешнем пространстве имен. Если вы используете этот код в другом пространстве имен, может быть другое (возможно, несвязанное) определение tolower, которое в конечном итоге будет предпочтительно выбрано без ::.
std::transform(data.begin(), data.end(), data.begin(), easytolower); опасен. Поскольку поведение std::tolower не определено, если вход не представлен как unsigned char и не равен EOF@BrianGordon - Но это намного проще, и в мире уже слишком много кошек.
@BrianGordon Это вопиющая ложь, о чем свидетельствует тот факт, что в мире все еще есть котята! знак равно
Что делает второе решение непереносимым? Могу я просто сделать это? pastebin.com/MPRMpQJS
@BrianGordon также бывают случаи, когда вы знать указали, что ввод - это ASCII (например, проводной формат доменных имен).
@Alnitak Я этого не знал. Как DNS обрабатывает международные доменные имена, которые могут быть в юникоде?
Приложения @BrianGordon должны преобразовать их в кодировку, полностью состоящую из ASCII, под названием "Punycode" (RFC 3492).
@TypicalHog: Потому что нет гарантии, что от 'A' до 'Z' находится непрерывный диапазон (EBCDIC); но что более важно, потому что буквы являются выходят за пределы этого диапазона ('Ü', 'á', ...). Очень, очень грустно, что авторы предпочитают собирать больше голосов за ответы с помощью непереносимых решений вместо того, чтобы правильно указывать на свои недостатки ...
@DevSolar: easytolower кажется мне совершенно правильным решением для латинских символов ASCII. Собираюсь использовать его для нормализации имен тегов HTML.
@ Cheersandhth.-Alf c99 не упоминает, что это UB: он либо возвращает нижний символ, либо не изменяется. std::tolower, однако, упоминает ub
@ L.F. Я исправил твое исправление.
@Deduplicator Если честно, у меня всегда были проблемы с пониманием того, почему char нужно сначала конвертировать в unsigned char. Разве значение (подписанного) char в любом случае не должно быть неотрицательным? Какой смысл tolower в отрицательном char? Думаю, я упускаю суть, так что не могли бы вы немного объяснить, пожалуйста :)
@ L.F. Нет, char может быть аналогом signed char, а signed char может быть отрицательным. tolower принимает только unsigned char и -1. Все, что находится за пределами его домена, является UB, и вы также не хотите объединять его с -1. Хотя все члены базовый набор символов исполнения неотрицательны, это не обязательно выполняется для (полного) набор символов исполнения. Посмотреть текущий черновик.
@Deduplicator Спасибо! Я не знал, что char действительно может быть отрицательным. Но тогда, разве преобразование в unsigned char не просто меняет значение?
@ L.F. char -> unsigned char (сохранение значения, по модулю 2 ** CHAR_BIT) -> неявно для int (сохранение значения). Конечно, с sizeof(int) == 1 все развалится.
@Deduplicator ОК ... Кажется, я это пропустил ... Затем int преобразуется в char, я думаю, поэтому результирующее значение определяется реализацией до C++ 20 и гарантированно будет исходным значением, начиная с C++ 20 ?
@ L.F. Преобразование результата из tolower() (int) обратно в char - тоже интересная история, да.
Я не понимаю, почему здесь tolower завернут в лямбду, а не просто передает его для преобразования самостоятельно.
@ JPhi1618 1), чтобы убедиться, что символ сначала преобразован в unsigned char (см. Комментарии дедупликатора выше); 2) для разрешения перегрузки для выбора перегрузки int tolower( int ch );, определенной в <cctype>, вместо перегрузки template< class charT > charT tolower( charT ch, const locale& loc );, определенной в <clocale>.
Насколько я понимаю, библиотеки Boost действительно плохи с точки зрения производительности. Я протестировал их unordered_map на STL, и он был в среднем в 3 раза медленнее (в лучшем случае 2, в худшем - в 10 раз). Также этот алгоритм выглядит слишком заниженным.
Разница настолько велика, что я уверен, что любое добавление, которое вам нужно будет сделать для tolower, чтобы сделать его равным boost "для ваших нужд", будет намного быстрее, чем boost.
Я проводил эти тесты на Amazon EC2, поэтому во время теста производительность менялась, но вы все равно понимаете.
./test
Elapsed time: 12365milliseconds
Elapsed time: 1640milliseconds
./test
Elapsed time: 26978milliseconds
Elapsed time: 1646milliseconds
./test
Elapsed time: 6957milliseconds
Elapsed time: 1634milliseconds
./test
Elapsed time: 23177milliseconds
Elapsed time: 2421milliseconds
./test
Elapsed time: 17342milliseconds
Elapsed time: 14132milliseconds
./test
Elapsed time: 7355milliseconds
Elapsed time: 1645milliseconds
-O2 сделал это так:
./test
Elapsed time: 3769milliseconds
Elapsed time: 565milliseconds
./test
Elapsed time: 3815milliseconds
Elapsed time: 565milliseconds
./test
Elapsed time: 3643milliseconds
Elapsed time: 566milliseconds
./test
Elapsed time: 22018milliseconds
Elapsed time: 566milliseconds
./test
Elapsed time: 3845milliseconds
Elapsed time: 569milliseconds
Источник:
string str;
bench.start();
for(long long i=0;i<1000000;i++)
{
str = "DSFZKMdskfdsjfsdfJDASFNSDJFXCKVdnjsafnjsdfjdnjasnJDNASFDJDSFSDNJjdsanjfsdnfjJNFSDJFSD";
boost::algorithm::to_lower(str);
}
bench.end();
bench.start();
for(long long i=0;i<1000000;i++)
{
str = "DSFZKMdskfdsjfsdfJDASFNSDJFXCKVdnjsafnjsdfjdnjasnJDNASFDJDSFSDNJjdsanjfsdnfjJNFSDJFSD";
for(unsigned short loop=0;loop < str.size();loop++)
{
str[loop]=tolower(str[loop]);
}
}
bench.end();
Думаю, мне следует пройти тесты на специальной машине, но я буду использовать этот EC2, поэтому мне не нужно тестировать его на моей машине.
Вы открывали возможности оптимизации при компиляции? Я думаю, что библиотека Heavy Boost STL должна работать лучше с высоким уровнем оптимизации.
Я использовал -O2 в одном из тестов, и больше ничего.
Производительность unordered_map зависит от алгоритма хеширования в сочетании с данными, которые вы используете. Не существует волшебного алгоритма хеширования, который работал бы со всеми и любыми данными, чтобы сделать unordered_map как можно быстрее. Сравнивайте и пробуйте разные вещи. Причина, по которой вы получаете худшую производительность, заключается в том, что с хешем, который вы используете, вы получаете много коллизий, что в основном вызывает поиск в списке. Посетите этот сайт для получения дополнительной информации: fgda.pl/post/7/gcc-hash-map-vs-unordered-map Для моих целей функция, предоставленная по ссылке, уменьшила коллизии и, таким образом, была очень быстрой.
Если строка содержит символы UTF-8 за пределами диапазона ASCII, то boost :: algorithm :: 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 хранит преобразованные символы в итераторе-адресате (увеличивая его на каждой итерации цикла), размер целевой строки не будет изменяться автоматически, и вы рискуете потерять память.
#include <string>
#include <algorithm>
#include <iostream>
int main (int argc, char* argv[])
{
std::string sourceString = "Abc";
std::string destinationString;
// Allocate the destination space
destinationString.resize(sourceString.size());
// Convert the source string to lower case
// storing the result in destination string
std::transform(sourceString.begin(),
sourceString.end(),
destinationString.begin(),
::tolower);
// Output the result of the conversion
std::cout << sourceString
<< " -> "
<< destinationString
<< std::endl;
}
Это не изменило размер Ä на ä для меня
Здесь также можно использовать итератор с обратной вставкой вместо ручного изменения размера.
Альтернативой Boost является POCO (pocoproject.org).
POCO предлагает два варианта:
Обе версии показаны ниже:
#include "Poco/String.h"
using namespace Poco;
std::string hello("Stack Overflow!");
// Copies "STACK OVERFLOW!" into 'newString' without altering 'hello.'
std::string newString(toUpper(hello));
// Changes newString in-place to read "stack overflow!"
toLowerInPlace(newString);
Используя диапазон для цикла C++ 11, более простой код будет:
#include <iostream> // std::cout
#include <string> // std::string
#include <locale> // std::locale, std::tolower
int main ()
{
std::locale loc;
std::string str = "Test String.\n";
for(auto elem : str)
std::cout << std::tolower(elem,loc);
}
Однако на французской машине эта программа не преобразует символы, отличные от ASCII, разрешенные на французском языке. Например, строка Test String123. É Ï \ n 'будет преобразовано в:' test string123. É Ï \ n ', хотя символы É Ï и их нижний регистр «é» и «ï» разрешены во французском языке. Похоже, что в других сообщениях этой ветки не было решения для этого.
Я думаю, вам нужно установить для этого подходящую локаль.
@incises, тогда кто-то опубликовал ответ о ICU, и это, безусловно, путь. Проще, чем большинство других решений, пытающихся понять локаль.
Лично я бы предпочел не использовать внешние библиотеки, когда это возможно.
Есть способ преобразовать верхний регистр в нижний регистр БЕЗ выполнения тестов if, и это довольно просто. Функция / макрос isupper (), использующая clocale.h, должна решить проблемы, связанные с вашим местоположением, но если нет, вы всегда можете настроить UtoL [] так, как вам нравится.
Учитывая, что символы C на самом деле являются всего лишь 8-битными целыми числами (на данный момент игнорируя широкие наборы символов), вы можете создать 256-байтовый массив, содержащий альтернативный набор символов, а в функции преобразования использовать символы в вашей строке как индексы в преобразовательный массив.
Однако вместо сопоставления 1 к 1 присвойте элементам массива верхнего регистра значения BYTE int для символов нижнего регистра. Вы можете найти здесь islower () и isupper ().

Код выглядит так ...
#include <clocale>
static char UtoL[256];
// ----------------------------------------------------------------------------
void InitUtoLMap() {
for (int i = 0; i < sizeof(UtoL); i++) {
if (isupper(i)) {
UtoL[i] = (char)(i + 32);
} else {
UtoL[i] = i;
}
}
}
// ----------------------------------------------------------------------------
char *LowerStr(char *szMyStr) {
char *p = szMyStr;
// do conversion in-place so as not to require a destination buffer
while (*p) { // szMyStr must be null-terminated
*p = UtoL[*p];
p++;
}
return szMyStr;
}
// ----------------------------------------------------------------------------
int main() {
time_t start;
char *Lowered, Upper[128];
InitUtoLMap();
strcpy(Upper, "Every GOOD boy does FINE!");
Lowered = LowerStr(Upper);
return 0;
}
Этот подход, в то же время, позволит вам переназначить любых других персонажей, которые вы хотите изменить.
У этого подхода есть одно огромное преимущество при работе на современных процессорах: нет необходимости делать предсказание ветвления, поскольку нет тестов if, содержащих ветвление. Это сохраняет логику прогнозирования ветвлений ЦП для других циклов и предотвращает остановку конвейера.
Некоторые здесь могут признать этот подход тем же самым, что использовался для преобразования EBCDIC в ASCII.
"Есть ли способ преобразовать верхний регистр в нижний БЕЗ выполнения тестов" когда-нибудь слышали о таблицах поиска?
Неопределенное поведение для отрицательных символов.
Современные процессоры имеют узкое место в памяти, а не в ЦП. Было бы интересно провести сравнительный анализ.
tl; dr
Используйте Библиотека ICU. Если вы этого не сделаете, ваша процедура преобразования будет молча прервана в случаях, о которых вы, вероятно, даже не подозреваете.
Сначала вы должны ответить на вопрос: каков кодирование вашего std::string? Это ISO-8859-1? Или, может быть, ISO-8859-8? Или кодовая страница Windows 1252? Знает ли об этом все, что вы используете для преобразования верхнего регистра в нижний? (Или это плохо для символов по 0x7f?)
Если вы используете UTF-8 (единственный разумный выбор среди 8-битных кодировок) с std::string в качестве контейнера, вы уже обманываете себя, если считаете, что все еще контролируете ситуацию. Вы сохраняете последовательность многобайтовых символов в контейнере, который не знает о концепции многобайтовых символов, и ни одна из операций, которые вы можете с ним выполнять! Даже такая простая вещь, как .substr(), может привести к недействительным (под) строкам, потому что вы разбиваете их посередине многобайтовой последовательности.
Как только вы попробуете что-то вроде std::toupper( 'ß' ) или std::tolower( 'Σ' ) в кодировке Любые, у вас будут проблемы. Поскольку 1) стандарт всегда работает только с одним символом за раз, поэтому он просто не может превратить ß в SS, что было бы правильным. И 2) стандарт всегда работает только с одним символом за раз, поэтому он не может решить, находится ли Σ в середине слова (где σ будет правильным) или в конце (ς). Другим примером может быть std::tolower( 'I' ), который должен давать разные результаты в зависимости от региона - практически везде, где вы ожидаете i, но в Турции ı (LATIN SMALL LETTER DOTLESS I) - правильный ответ (который, опять же, больше одного байта в UTF-8 кодировка).
Таким образом, преобразование регистра Любые, которое работает с символом за раз, или, что еще хуже, байт за раз, нарушено конструктивно. Сюда входят все варианты std::, существующие в настоящее время.
Тогда есть момент, что стандартная библиотека для того, на что способна является, зависит от того, какие локали имеют поддержанный на машине, на которой работает ваше программное обеспечение ... и что вы делаете, если ваша целевая локаль входит в число неподдерживаемых на машине вашего клиента?
Итак, то, что вы ищете В самом деле, - это строковый класс, способный со всем этим правильно справиться, и это нет любой из вариантов 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 нет предварительно скомпилированных двоичных файлов, включающих ICU, поэтому вам придется поставлять их вместе с вашим приложением, а который открывает совершенно новую банку червей ...)
Так что лично я бы рекомендовал получить полную поддержку Unicode прямо из уст лошади и напрямую использовать библиотеку ICU:
#include <unicode/unistr.h>
#include <unicode/ustream.h>
#include <unicode/locid.h>
#include <iostream>
int main()
{
/* "Odysseus" */
char const * someString = u8"ΟΔΥΣΣΕΥΣ";
icu::UnicodeString someUString( someString, "UTF-8" );
// Setting the locale explicitly here for completeness.
// Usually you would use the user-specified system locale,
// which *does* make a difference (see ı vs. i above).
std::cout << someUString.toLower( "el_GR" ) << "\n";
std::cout << someUString.toUpper( "el_GR" ) << "\n";
return 0;
}
Скомпилируйте (в этом примере с G ++):
g++ -Wall example.cpp -licuuc -licuio
Это дает:
ὀδυσσεύς
Обратите внимание, что преобразование Σ <-> σ в середине слова и преобразование Σ <-> ς в конце слова. Никакое решение на основе <algorithm> не может вам этого дать.
Это правильный ответ в общем случае. Стандарт ничего не дает для обработки чего-либо, кроме "ASCII", кроме лжи и обмана. Это заставляет вас считать, возможно, вы можете иметь дело с UTF-16, но вы не можете. Как говорится в этом ответе, вы не можете получить правильную длину символа (а не длину в байтах) строки UTF-16, не выполняя свою собственную обработку Unicode. Если вам нужно иметь дело с реальным текстом, используйте ICU. Спасибо, @DevSolar
Доступен ли ICU по умолчанию в Ubuntu / Windows или его нужно устанавливать отдельно? Также как насчет этого ответа: stackoverflow.com/a/35075839/207661?
@ShitalShah: Все необходимо устанавливать отдельно в Windows. Во многих инсталляциях Linux libicu входит в состав установки по умолчанию; те, кто этого не делает, предоставляют это через управление пакетами. Если вы хотите использовать развивать против него, вам все равно придется установить его заголовки (libicu-dev или в любом другом дистрибутиве, который это называется). Что касается этого другого ответа, в документации (!) Говорится, что он поддерживает только сопоставления 1: 1. Этот мощь работает для tolower (я на самом деле не знаю другого примера), но этого недостаточно для toupper (см. Мой пример).
(прод.) И обычно в полноразмерном приложении нужны не только toupper / tolower. Вам понадобятся регулярные выражения, нормализация / денормализация UTF, функции календаря, поиск и замена строк с поддержкой Unicode и все такое. Для всего этого ICU - это просто предпочтительная библиотека в, поэтому вы можете использовать ее и для tolower.
Эй, смотри, настоящий ответ! Спасибо, что указали мне правильный путь, DevSolar.
icu :: UnicodeString :: length () технически также обманывает вас (хотя и реже), поскольку сообщает количество 16-битных кодовых единиц, а не количество кодовых точек. ;-)
@masaers: Честно говоря, с такими вещами, как комбинирование символов, объединители нулевой ширины и маркеры с написанием справа налево, количество кодовых точек довольно бессмысленно. Я удалю это замечание.
@DevSolar Согласен! В тексте понятие длины бессмысленно (мы могли бы добавить лигатуры в список нарушителей). Тем не менее, поскольку люди привыкли к вкладкам и управляющим символам, занимающим одну единицу длины, кодовые точки были бы более интуитивно понятной мерой. О, и спасибо за правильный ответ, грустно видеть это так далеко вниз :-(
А как насчет C++ 20 u8string?
@ L.F. Немного лучше. Но многие вещи еще не охвачены: toupper и tolower все еще работают с отдельными символами. Класс строки по-прежнему не имеет понятия нормализации (например, кодируется ли «ü» как «u с диэрезисом» или «u + объединяющий диэрезис») или где строка может или не может быть разделена. Этот список можно продолжить. u8string (как и другие стандартные строковые классы) подходит для «прохождения». Но если вы хотите использовать Unicode процесс, вы необходимость ICU.
Я считаю ответ плохим, потому что он влечет за собой дополнительную зависимость; то же самое с наддувом.
@shevy: Смысл этого ответа в том, что существует просто способ нет для правильного преобразования регистра без с добавлением дополнительных зависимостей, потому что на данный момент (то есть все еще) стандартная библиотека не выполняет преобразование регистра предоставлять с учетом Unicode, и я Я бы предпочел, чтобы люди были осведомленный этого факта и перестали предполагать, что весь текст - это ASCII-7, потому что это просто не так.
На самом деле, std::string не знает, что он содержит текст в многобайтовой кодировке символов, это особенность, а не ошибка. Это единственный разумный способ сделать это, поэтому почти все так делают. Отсутствие надлежащего стандартного API для обработки чего-либо, кроме базового текста из давно минувших дней, которых на самом деле никогда не было, является проблемой, да. Однако это должно быть необязательно даже в размещенной среде, так как это довольно много, и во многих случаях это не нужно.
@Deduplicator: Извините, но это всего лишь уклонение от него всеми возможными способами. Существуют стандарты являются (Unicode), есть квазистандартные API являются для его обработки (ICU), и если вы намерены написать код, который правильно преобразует текст в нижний регистр, если вы не можете гарантия, ваш код всегда будет видеть только ASCII-7 (который был бы довольно частным случаем), все остальные «решения» здесь в лучшем случае 80-20.
Поэтому должны быть такие стандартные API. Это не отменяет того факта, что многие операции со строками лучше всего выполнять, игнорируя все, кроме последовательности кодовых единиц. И для этого множества вариантов использования никогда не требуется ничего более сложного.
@Deduplicator И этот стандартный API в настоящее время является библиотекой ICU, о чем и идет этот ответ.
@Deduplicator Я слышал, что std::text в стадии реализации, возможно, даже вовремя для C++ 23. Не будем пока терять надежду.
Я пробовал std :: transform, все, что я получаю, - это отвратительная ошибка компиляции stl criptic, которую могут понять только друиды 200-летней давности (не могут преобразовать из flibidi flabidi flu)
это отлично работает и может быть легко изменено
string LowerCase(string s)
{
int dif='a'-'A';
for(int i=0;i<s.length();i++)
{
if ((s[i]>='A')&&(s[i]<='Z'))
s[i]+=dif;
}
return s;
}
string UpperCase(string s)
{
int dif='a'-'A';
for(int i=0;i<s.length();i++)
{
if ((s[i]>='a')&&(s[i]<='z'))
s[i]-=dif;
}
return s;
}
На платформах Microsoft вы можете использовать семейство функций strlwr: http://msdn.microsoft.com/en-us/library/hkxwh33z.aspx
// crt_strlwr.c
// compile with: /W3
// This program uses _strlwr and _strupr to create
// uppercase and lowercase copies of a mixed-case string.
#include <string.h>
#include <stdio.h>
int main( void )
{
char string[100] = "The String to End All Strings!";
char * copy1 = _strdup( string ); // make two copies
char * copy2 = _strdup( string );
_strlwr( copy1 ); // C4996
_strupr( copy2 ); // C4996
printf( "Mixed: %s\n", string );
printf( "Lower: %s\n", copy1 );
printf( "Upper: %s\n", copy2 );
free( copy1 );
free( copy2 );
}
Самый простой способ преобразовать строку в loweercase, не беспокоясь о пространстве имен std, выглядит следующим образом
1: строка с / без пробелов
#include <algorithm>
#include <iostream>
#include <string>
using namespace std;
int main(){
string str;
getline(cin,str);
//------------function to convert string into lowercase---------------
transform(str.begin(), str.end(), str.begin(), ::tolower);
//--------------------------------------------------------------------
cout<<str;
return 0;
}
2: строка без пробелов
#include <algorithm>
#include <iostream>
#include <string>
using namespace std;
int main(){
string str;
cin>>str;
//------------function to convert string into lowercase---------------
transform(str.begin(), str.end(), str.begin(), ::tolower);
//--------------------------------------------------------------------
cout<<str;
return 0;
}
std::ctype::tolower() из стандартной библиотеки локализации C++ правильно сделает это за вас. Вот пример, извлеченный из справочная страница tolower
#include <locale>
#include <iostream>
int main () {
std::locale::global(std::locale("en_US.utf8"));
std::wcout.imbue(std::locale());
std::wcout << "In US English UTF-8 locale:\n";
auto& f = std::use_facet<std::ctype<wchar_t>>(std::locale());
std::wstring str = L"HELLo, wORLD!";
std::wcout << "Lowercase form of the string '" << str << "' is ";
f.tolower(&str[0], &str[0] + str.size());
std::wcout << "'" << str << "'\n";
}
Хорошо, если вы можете преобразовать символы на месте. Что, если ваша исходная строка - const? Кажется, что это делает его немного более беспорядочным (например, не похоже, что вы можете использовать f.tolower()), поскольку вам нужно поместить символы в новую строку. Вы бы использовали transform() и что-то вроде std::bind1st( std::mem_fun() ) для оператора?
Для константной строки мы можем просто сделать локальную копию, а затем преобразовать ее на месте.
Да, однако, создание копии увеличивает накладные расходы.
Вы можете использовать std :: transform с версией ctype :: tolower, которая не принимает указатели. Используйте адаптер итератора обратного вставки, и вам даже не нужно беспокоиться о предварительном изменении размера вашей выходной строки.
Отлично, особенно потому, что в tolower из libstdC++ с параметром locale неявный вызов use_facet оказывается узким местом производительности. Один из моих коллег добился увеличения скорости на несколько 100%, заменив boost::iequals (у которого есть эта проблема) версией, в которой use_facet вызывается только один раз вне цикла.
Вот макросъемка, если вам нужно что-то простое:
#define STRTOLOWER(x) std::transform (x.begin(), x.end(), x.begin(), ::tolower)
#define STRTOUPPER(x) std::transform (x.begin(), x.end(), x.begin(), ::toupper)
#define STRTOUCFIRST(x) std::transform (x.begin(), x.begin()+1, x.begin(), ::toupper); std::transform (x.begin()+1, x.end(), x.begin()+1,::tolower)
Однако обратите внимание, что комментарий @ AndreasSpindler к этот ответ по-прежнему является важным соображением, однако, если вы работаете над чем-то, кроме символов ASCII.
Я голосую против того, чтобы давать макросы, когда существует совершенно хорошее решение - вы даже даете эти решения.
Техника макросов означает меньше набора кода для чего-то, что обычно часто используется в программировании. Почему бы не использовать это? Иначе зачем вообще макросы?
Макросы в наследство от С, который прорабатывается тяжело избавиться. Если вы хотите уменьшить объем набора текста, используйте функцию или лямбду. void strtoupper(std::string& x) { std::transform (x.begin(), x.end(), x.begin(), ::toupper); }
@Clearer Поскольку я хочу стать лучшим программистом, не могли бы вы предоставить мне какие-либо ссылки на документы ANSI, где какие-либо комитеты ANSI C++ говорят что-то вроде: «Нам нужно созвать собрание, чтобы избавиться от макросов в C++»? Или какая-то другая дорожная карта?
Нет, не могу. Тем не менее, позиция Бьярна по этой теме несколько раз высказывалась довольно четко. Кроме того, есть множество причин не использовать макросы как в C, так и в C++. x может быть допустимым выражением, которое просто компилируется правильно, но дает совершенно ложные результаты из-за макросов.
хорошие макросы! Макросы @Clearer так нам помогают ... Я думаю, они никогда от этого не избавятся.
@AquariusPower Я не согласен. Мне еще предстоит увидеть макрос, который нельзя было бы сделать лучше в качестве шаблона или лямбда.
Другой подход, использующий цикл на основе диапазона со ссылочной переменной
string test = "Hello World";
for(auto& c : test)
{
c = tolower(c);
}
cout<<test<<endl;
// tolower example (C++)
#include <iostream> // std::cout
#include <string> // std::string
#include <locale> // std::locale, std::tolower
int main ()
{
std::locale loc;
std::string str = "Test String.\n";
for (std::string::size_type i=0; i<str.length(); ++i)
std::cout << std::tolower(str[i],loc);
return 0;
}
Для получения дополнительной информации: http://www.cplusplus.com/reference/locale/tolower/
#include<bits/stdc++.h>
using namespace std;
int main ()
{
ios::sync_with_stdio(false);
string str = "String Convert\n";
for(int i=0; i<str.size(); i++)
{
str[i] = tolower(str[i]);
}
cout<<str<<endl;
return 0;
}
Используйте fplus::to_lower_case() из библиотеки fplus.
Искать to_lower_case в fplus API Search
Пример:
fplus::to_lower_case(std::string("ABC")) == std::string("abc");
Копировать, потому что это было запрещено для улучшения ответа. Спасибо ТАК
string test = "Hello World";
for(auto& c : test)
{
c = tolower(c);
}
Объяснение:
for(auto& c : test) - это цикл for на основе диапазона типа for (range_declaration:range_expression)loop_statement:
range_declaration: auto& c
Здесь автоматический спецификатор используется для автоматического определения типа. Таким образом, тип вычитается из инициализатора переменных.
range_expression: test
Диапазон в данном случае - символы строки test.
Символы строки test доступны в качестве ссылки внутри идентификатора цикла for c.
Уточните, откуда вы скопировали свой ответ.
Это может быть еще одна простая версия для преобразования верхнего регистра в нижний и наоборот. Я использовал версию сообщества VS2017 для компиляции этого исходного кода.
#include <iostream>
#include <string>
using namespace std;
int main()
{
std::string _input = "lowercasetouppercase";
#if 0
// My idea is to use the ascii value to convert
char upperA = 'A';
char lowerA = 'a';
cout << (int)upperA << endl; // ASCII value of 'A' -> 65
cout << (int)lowerA << endl; // ASCII value of 'a' -> 97
// 97-65 = 32; // Difference of ASCII value of upper and lower a
#endif // 0
cout << "Input String = " << _input.c_str() << endl;
for (int i = 0; i < _input.length(); ++i)
{
_input[i] -= 32; // To convert lower to upper
#if 0
_input[i] += 32; // To convert upper to lower
#endif // 0
}
cout << "Output String = " << _input.c_str() << endl;
return 0;
}
Примечание: если есть специальные символы, их нужно обрабатывать с помощью проверки условий.
Is there an alternative which works 100% of the time?
Нет
Перед тем, как выбрать метод нижнего регистра, вам нужно задать себе несколько вопросов.
Получив ответы на эти вопросы, вы можете приступить к поиску решения, которое соответствует вашим потребностям. Не существует универсального решения, подходящего для всех и везде!
В C++ нет методов tolower или toupper, реализованных для std::string, но он доступен для char. Каждый символ строки можно легко прочитать, преобразовать в нужный регистр и вернуть обратно в строку.
Пример кода без использования сторонней библиотеки:
#include<iostream>
int main(){
std::string str = std::string("How IS The Josh");
for(char &ch : str){
ch = std::tolower(ch);
}
std::cout<<str<<std::endl;
return 0;
}
Для символьной операции со строкой: Для каждого символа в строке
Поскольку ни в одном из ответов не упоминается предстоящая библиотека Ranges, которая доступна в стандартной библиотеке с C++ 20 и в настоящее время отдельно доступна на GitHub как range-v3, я хотел бы добавить способ выполнить это преобразование с ее помощью.
Чтобы изменить строку на месте:
str |= action::transform([](unsigned char c){ return std::tolower(c); });
Чтобы сгенерировать новую строку:
auto new_string = original_string
| view::transform([](unsigned char c){ return std::tolower(c); });
(Не забудьте #include <cctype> и необходимые заголовки диапазонов.)
Примечание: использование unsigned char в качестве аргумента лямбда вдохновлено cppreference, в котором говорится:
Like all other functions from
<cctype>, the behavior ofstd::toloweris undefined if the argument's value is neither representable asunsigned charnor equal toEOF. To use these functions safely with plainchars (orsigned chars), the argument should first be converted tounsigned char:char my_tolower(char ch) { return static_cast<char>(std::tolower(static_cast<unsigned char>(ch))); }Similarly, they should not be directly used with standard algorithms when the iterator's value type is
charorsigned char. Instead, convert the value tounsigned charfirst:std::string str_tolower(std::string s) { std::transform(s.begin(), s.end(), s.begin(), // static_cast<int(*)(int)>(std::tolower) // wrong // [](int c){ return std::tolower(c); } // wrong // [](char c){ return std::tolower(c); } // wrong [](unsigned char c){ return std::tolower(c); } // correct ); return s; }
Мои собственные функции шаблона, которые выполняют верхний / нижний регистр.
#include <string>
#include <algorithm>
//
// Lowercases string
//
template <typename T>
std::basic_string<T> lowercase(const std::basic_string<T>& s)
{
std::basic_string<T> s2 = s;
std::transform(s2.begin(), s2.end(), s2.begin(), tolower);
return std::move(s2);
}
//
// Uppercases string
//
template <typename T>
std::basic_string<T> uppercase(const std::basic_string<T>& s)
{
std::basic_string<T> s2 = s;
std::transform(s2.begin(), s2.end(), s2.begin(), toupper);
return std::move(s2);
}
Это то, что мне было нужно. Я просто использовал towlower для широких символов, который поддерживает UTF-16.
Попробуйте эту функцию :)
string toLowerCase(string str) {
int str_len = str.length();
string final_str = "";
for(int i=0; i<str_len; i++) {
char character = str[i];
if (character>=65 && character<=92) {
final_str += (character+32);
} else {
final_str += character;
}
}
return final_str;
}
Я написал эту простую вспомогательную функцию:
#include <locale> // tolower
string to_lower(string s) {
for(char &c : s)
c = tolower(c);
return s;
}
Использование:
string s = "TEST";
cout << to_lower("HELLO WORLD"); // output: "hello word"
cout << to_lower(s); // won't change the original variable.
Как еще вы могли бы преобразовать каждый элемент списка из чего-либо во что-то еще, не просматривая список? Строка - это просто список символов, если вам нужно применить какую-то функцию к каждому символу, вам придется перебирать строку. Никакого другого пути.