Как преобразовать std:: string в нижний регистр?
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
или, для не-в-месте:
#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-битных кодировок) с .substr() это тикающая бомба. (Поскольку разделение многобайтовой последовательности приведет к недопустимой (суб) строке.)
и как только вы попробуете что-то вроде std::toupper( 'ß' )
, в любой кодирование, у вас серьезные проблемы. (Потому что это просто невозможно сделать "правильно" со стандартной библиотекой, которая может только доставить один символ результата, а не "SS"
нужен здесь.) [1] Другой пример std::tolower( 'I' )
, которые должны давать разные результаты в зависимости от локали. В Германии 'i'
было бы правильно; в Турции,'ı'
(Латинская маленькая буква DOTLESS I) - ожидаемый результат.
тогда есть точка, что стандартная библиотека зависит от того, какие локали поддерживает на устройстве работает программное обеспечение... а что делать, если нет?
так что действительно поиск-это класс string, который способен справиться со всем этим правильно,и это не std::string
.
(в C++11 Примечание: std::u16string
и std::u32string
are лучше, но все равно не идеальный.)
В То Время Как Boost выглядит хороший, API мудрый, повышение.Locale-это в основном оболочка вокруг ОИТ. если Boost является составлен С поддержкой ICU... если это не так, увеличьте.Locale ограничивается поддержкой локали, скомпилированной для стандартной библиотеки.
и поверь мне, начало Boost для компиляции с ICU иногда может быть настоящей болью. (Для Windows нет предварительно скомпилированных двоичных файлов, поэтому у вас будет чтобы поставить их вместе с вашим заявлением, и это открывает совершенно новую банку червей...)
поэтому лично я бы рекомендовал получить полную поддержку Unicode прямо из уст лошади и использовать ОИТ библиотеки напрямую:
#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;
}
Compile (с G++ в этом примере):
g++ -Wall example.cpp -licuuc -licuio
это дает:
eidengesäß
EIDENGESÄSS
[1] в 2017 году Совет по немецкой орфографии постановил, что "" U+1E9E латинский Заглавная буква 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
использование диапазона на основе цикла 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 справочная страница
#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";
}
простой способ, чтобы преобразовать строку в 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;
}
другой подход, использующий диапазон, основанный на цикле со ссылочной переменной
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;
}
этот подход, в то же время, позволяет переназначить любые другие символы, которые вы хотите изменение.
этот подход имеет одно огромное преимущество при работе на современных процессорах, нет необходимости делать предсказания ветвлений, а нет ли тестов, содержащий ветвление. Это экономит логику прогнозирования ветвей процессора для других циклов и, как правило, предотвращает остановку конвейера.
некоторые здесь могут распознать этот подход как тот же, который использовался для преобразования 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
Здесь авто описатель это используется для автоматического определения типов. Таким образом, тип вычитается из инициализатора переменных.range_expression
:test
Диапазоном в этом случае являются символы stringtest
.
символы строки 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;
}
//You can really just write one on the fly whenever you need one.
#include <string>
void _lower_case(std::string& s){
for(unsigned short l = s.size();l;s[--l]|=(1<<5));
}
//Here is an example.
//http://ideone.com/mw2eDK