Как преобразовать std :: string в нижний регистр? - PullRequest
686 голосов
/ 24 ноября 2008

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

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

Ответы [ 25 ]

3 голосов
/ 19 сентября 2013

Альтернативой Boost является POCO (pocoproject.org).

POCO предоставляет два варианта:

  1. Первый вариант создает копию без изменения исходной строки.
  2. Второй вариант изменяет исходную строку на месте.
    Версии «на месте» всегда имеют в названии «InPlace».

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

#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);
2 голосов
/ 31 января 2016

Вот метод макроса, если вы хотите что-то простое:

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

2 голосов
/ 08 января 2014

Есть способ преобразовать верхний регистр в нижний БЕЗ выполнения, если тестирует , и это довольно просто. Использование функции / макроса isupper () для clocale.h должно позаботиться о проблемах, связанных с вашим местоположением, но если нет, вы всегда можете настроить UtoL [] на свое усмотрение.

Учитывая, что символы C на самом деле являются просто 8-битными целыми числами (игнорируя широкие наборы символов на данный момент), вы можете создать 256-байтовый массив, содержащий альтернативный набор символов, и в функции преобразования использовать символы в вашей строке как индексы в массив преобразования.

Вместо сопоставления 1-к-1 задайте для членов массива верхнего регистра значения int BYTE для символов нижнего регистра. 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;
}

Этот подход, в то же время, позволит вам переназначить любые другие символы, которые вы хотите изменить.

Этот подход имеет одно огромное преимущество при работе на современных процессорах: нет необходимости делать предсказание ветвления, поскольку нет тестов, содержащих ветвление. Это сохраняет логику предсказания ветвления ЦП для других циклов и предотвращает задержки конвейера.

Некоторые здесь могут признать этот подход тем же, который использовался для преобразования EBCDIC в ASCII.

1 голос
/ 15 апреля 2019

Поскольку ни в одном из ответов не упоминалась будущая библиотека 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> и необходимые заголовки Ranges.)

Примечание: использование unsigned char в качестве аргумента для лямбды основано на cppreference , который гласит:

Как и все другие функции из <cctype>, поведение std::tolower не определено, если значение аргумента не представляется как unsigned char и не равно EOF. Чтобы безопасно использовать эти функции с обычными char с (или signed char с), аргумент должен быть сначала преобразован в unsigned char:

char my_tolower(char ch)
{
    return static_cast<char>(std::tolower(static_cast<unsigned char>(ch)));
}

Аналогично, они не должны напрямую использоваться со стандартными алгоритмами, когда тип значения итератора равен char или signed char. Вместо этого сначала преобразуйте значение в unsigned char:

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;
}
1 голос
/ 29 января 2019

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

нет

Нет

Есть несколько вопросов, которые вы должны задать себе, прежде чем выбрать метод в нижнем регистре.

  1. Как закодирована строка? простой ASCII? UTF-8? какая-то форма расширенного унаследованного кодирования ASCII?
  2. Что вы подразумеваете под строчными? Правила отображения дел варьируются в зависимости от языка! Вы хотите что-то, что локализовано в локали пользователей? Вы хотите что-то, что ведет себя согласованно на всех системах, на которых работает ваше программное обеспечение? Вы просто хотите использовать символы ASCII в нижнем регистре и проходить через все остальное?
  3. Какие библиотеки доступны?

Получив ответы на эти вопросы, вы можете начать искать решение, которое соответствует вашим потребностям. Не существует единого размера, подходящего всем, кто работает везде!

1 голос
/ 20 марта 2017
// 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/

0 голосов
/ 18 мая 2019

Мои собственные функции шаблонов, которые выполняют прописные / строчные буквы.

#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);
}
0 голосов
/ 17 марта 2019

C ++ не имеет методов tolower или toupper, реализованных для строки, но он доступен для 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;
}

Для символьной операции со строкой: Для каждого символа в строке

0 голосов
/ 04 июня 2018

Это может быть еще одна простая версия для преобразования прописных букв в строчные и наоборот. Я использовал версию сообщества 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;
}

Примечание: если есть специальные символы, их необходимо обработать с помощью проверки состояния.

0 голосов
/ 17 апреля 2018

Копировать, потому что было запрещено улучшать ответ. Спасибо ТАК


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
    Здесь авто спецификатор используется для автоматического вывода типа. Таким образом, тип вычитается из инициализатора переменных.

  2. range_expression: test
    Диапазон в этом случае - символы строки test.

Символы строки test доступны в качестве ссылки внутри цикла for с идентификатором c.

...