Как преобразовать std:: string в нижний регистр?
Я хочу, чтобы преобразовать std::string
в нижний регистр. Я знаю о функции tolower()
, однако в прошлом у меня были проблемы с этой функцией и вряд ли подходит так как использовать с std::string
потребуется итерация по каждому символу.
есть ли альтернатива, которая работает 100% времени?
21 ответ:
С этой:
#include <algorithm> #include <string> std::string data = "Abc"; std::transform(data.begin(), data.end(), data.begin(), ::tolower);
вам действительно не сойдет с рук повторение каждого символа. Нет никакого способа узнать, является ли символ строчным или прописным в противном случае.
если вы действительно ненавижу
tolower()
, вот непортативная альтернатива, которую я не рекомендую вам использовать:char easytolower(char in) { if(in <= 'Z' && in >= 'A') return in - ('Z' - 'z'); return in; } std::transform(data.begin(), data.end(), data.begin(), easytolower);
имейте в виду, что
::tolower()
может выполнять только однобайтовую подстановку символов, что плохо подходит для многих сценариев, особенно при использовании многобайтовые кодировки, такие как UTF-8.
для этого есть алгоритм Boost string:
#include <boost/algorithm/string.hpp> std::string str = "HELLO, WORLD!"; boost::algorithm::to_lower(str); // modifies str
или, для non-in-place:
#include <boost/algorithm/string.hpp> const std::string str = "HELLO, WORLD!"; const std::string lower_str = boost::algorithm::to_lower_copy(str);
tl; dr
использовать библиотека ICU.
сначала надо ответить на вопрос: Что такое кодирование вашего
std::string
? Это ISO-8859-1? Или возможно, кодировка ISO-8859-8? Или Кодовая Страница Windows 1252? знает ли это то, что вы используете для преобразования верхнего в нижний регистр? (или он терпит неудачу с треском для символов над0x7f
?)если вы используете UTF-8 (единственный вменяемый выбор среди 8-битных кодировок) с
std::string
как контейнер, вы уже обманываете себя, полагая, что вы все еще контролируете вещи, потому что вы храните многобайтовую последовательность символов в контейнере, который не знает о многобайтовой концепции. Даже что-то такое простое, как.substr()
- тикающая бомба замедленного действия. (Потому что разделение многобайтовой последовательности приведет к недопустимой (суб-) строке.)и как только вы попробуете что-то вроде
std::toupper( 'ß' )
, in любой кодировка, вы находитесь в глубокой беде. (Потому что это просто невозможно сделать это "правильно" со стандартной библиотекой, которая может доставить только один символ результата, а не"SS"
нужен здесь.) [1] Другой примерstd::tolower( 'I' )
, что должно дать разные результаты в зависимости от локали. В Германии,'i'
было бы правильно; в Турции,'ı'
(Латинская строчная буква DOTLESS I) - это ожидаемый результат.тогда есть дело в том, что стандартная библиотека зависит от того, какие локали поддерживает на машине работает ваше программное обеспечение... а что делать, если это не так?
так что действительно ищет строковый класс, который способен справиться со всем этим правильно,и это не
std::string
.(в C++11 Примечание:
std::u16string
иstd::u32string
are лучше, но все равно не идеальный.)В То Время Как Boost выглядит хороший, API мудрый, повышение.Locale-это в основном обертка вокруг ICU. если Boost - это compiled С поддержкой ICU... если это не так, увеличьте.Локаль ограничена поддержкой локали, скомпилированной для стандартной библиотеки.
и поверь мне, начало Boost для компиляции с ICU иногда может быть настоящей болью. (Для Windows нет предварительно скомпилированных двоичных файлов, поэтому у вас есть чтобы поставить их вместе с вашим заявлением, и это открывает совершенно новую банку червей...)
поэтому лично я бы рекомендовал получить полную поддержку Unicode прямо из уст лошади и использовать ICU библиотеки напрямую:
#include <unicode/unistr.h> #include <unicode/ustream.h> #include <unicode/locid.h> #include <iostream> int main() { char const * someString = "Eidenges\xe4\xdf"; icu::UnicodeString someUString( someString, "ISO-8859-1" ); // Setting the locale explicitly here for completeness. // Usually you would use the user-specified system locale. std::cout << someUString.toLower( "de_DE" ) << "\n"; std::cout << someUString.toUpper( "de_DE" ) << "\n"; return 0; }
компиляция (с G++ в этом примере):
g++ -Wall example.cpp -licuuc -licuio
это дает:
eidengesäß EIDENGESÄSS
[1] в 2017 году Совет по немецкой орфографии постановил, что "" U+1E9E LATIN Заглавная буква SHARP S может использоваться официально, как вариант помимо традиционного преобразования "SS", чтобы избежать двусмысленности, например, в паспортах (где имена заглавными буквами). Мой прекрасный пример, ставший устаревшим по решению комитета...
Если строка содержит 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
используя range-based для цикла 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); }
это продолжение ответа Стефана МАИ: если вы хотите поместить результат преобразования в другую строку, вам нужно предварительно выделить его место для хранения перед вызовом
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 действительно плохи с точки зрения производительности. Я проверил их unordered_map в STL, и это было в среднем в 3 раза медленнее (лучший случай 2, Худший был в 10 раз). Также этот алгоритм выглядит слишком низко.
разница настолько велика, что я уверен, что вы должны сделать, чтобы
tolower
чтобы сделать его равным для повышения "для ваших нужд" будет быстрее чем повысить.Я сделал эти тесты на 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, поэтому мне действительно не нужно тестировать его на своей машине.
std::ctype::tolower()
из стандартной библиотеки C++ локализация будет правильно сделать это для вас. Вот пример, извлеченный из tolower reference page#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"; }
самый простой способ преобразовать строку в нижний регистр, не беспокоясь о пространстве имен 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; }
другой подход с использованием диапазона на основе цикла с опорной переменной
string test = "Hello World"; for(auto& c : test) { c = tolower(c); } cout<<test<<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);
есть способ преобразовать верхний регистр в Нижний не делая, если тесты, и это довольно прямо вперед. Функция isupper () / использование макроса clocale.h следует позаботиться о проблемах, связанных с вашим местоположением, но если нет, вы всегда можете настроить UtoL[] в свое удовольствие.
учитывая, что символы C на самом деле являются всего лишь 8-битными ints (игнорируя широкие наборы символов на данный момент), Вы можете создать 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.
вот метод макроса, если вы хотите что-то простое:
#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.
// 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/
на платформах 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 ); }
Код
#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: https://github.com/Dobiasd/FunctionalPlus.
Поиск 'to_lower_case' вhttp://www.editgym.com/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 (
range_declaration
:
range_expression
)
loop_statement
:
range_declaration
:auto& c
Вот это авто описатель is используется для автоматического типа вычета. Таким образом, тип вычитается из инициализатора переменных.
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; }
Примечание: если есть специальные символы, то должны быть обработаны с помощью проверки состояния.
я попробовал 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; }