Как преобразовать std:: string в нижний регистр?


Я хочу, чтобы преобразовать std::string в нижний регистр. Я знаю о функции tolower(), однако в прошлом у меня были проблемы с этой функцией и вряд ли подходит так как использовать с std::string потребуется итерация по каждому символу.

есть ли альтернатива, которая работает 100% времени?

21 634

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 предоставляет два варианта:

  1. первый вариант делает копию без изменения исходной строки.
  2. второй вариант изменяет исходную строку на месте.
    "На месте" версии всегда имеют "на месте" в названии.

обе версии показано ниже:

#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() полезное здесь.

enter image description here

код выглядит так...

#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:

  1. range_declaration:auto& c
    Вот это авто описатель is используется для автоматического типа вычета. Таким образом, тип вычитается из инициализатора переменных.

  2. 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;
}
//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