Avatar ·

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

Привет всем! Я новичок в C++ и пишу небольшую программу для обработки текста. Мне нужно привести все символы в строке к нижнему регистру. Я знаю, что в C есть функция tolower() из , но она работает только с отдельными символами (int). Как мне применить её ко всей строке std::string? Я пробовал написать цикл вроде этого: ```cpp #include #include #include int main() { std::string str = "Hello World!"; for (int i = 0; i < str.size(); ++i) { str[i] = tolower(str[i]); } std::cout << str << std::endl; return 0; } ``` Код компилируется, но я не уверен, что это правильно и безопасно. Может, есть более идиоматичный способ с использованием STL? Я слышал про std::transform, но не понимаю, как его применить вместе с tolower. Плюс, tolower принимает int, а не char — не будет ли проблем с отрицательными значениями char (ведь char может быть signed)? Подскажите, пожалуйста, как правильно и эффективно преобразовать std::string в нижний регистр в современном C++ (C++11 и новее). Заранее спасибо! <<>>

Правильный способ: std::transform с std::tolower

Ваш цикл в целом рабочий, но есть несколько нюансов, которые стоит учесть. Главная проблема — std::tolower принимает int, который должен быть представлен как unsigned char (или EOF). Если char у вас знаковый (signed), то при передаче символа с кодом > 127 (например, буквы «ё» в Windows-1251) может произойти отрицательное значение, что приведёт к неопределённому поведению (UB).

Вот правильное и идиоматичное решение на C++11 и новее:

#include <algorithm>
#include <cctype>
#include <string>

std::string to_lower(const std::string& str) {
    std::string result = str;
    std::transform(result.begin(), result.end(), result.begin(),
                   [](unsigned char c) { return std::tolower(c); });
    return result;
}

Или, если хотите изменить строку на месте:

void to_lower_inplace(std::string& str) {
    std::transform(str.begin(), str.end(), str.begin(),
                   [](unsigned char c) { return std::tolower(c); });
}

Разбор решения

  • std::transform — алгоритм из <algorithm>, который применяет функцию к каждому элементу диапазона и записывает результат в выходной диапазон. Третий аргумент может совпадать с первым (изменяем на месте).
  • Лямбда-функция принимает unsigned char — это ключевой момент. Мы явно приводим char к unsigned char, чтобы избежать UB при передаче в std::tolower.
  • std::tolower из <cctype> корректно работает только с неотрицательными значениями. Приведение к unsigned char решает эту проблему.

Альтернативы для разных версий C++

C++20 и новее — используйте ranges:

#include <ranges>
#include <cctype>

std::string to_lower(const std::string& str) {
    return str | std::views::transform([](unsigned char c) {
        return std::tolower(c);
    }) | std::ranges::to<std::string>();
}

C++17 — можно использовать std::string_view для входного параметра, чтобы избежать копирования:

std::string to_lower(std::string_view str) {
    std::string result;
    result.reserve(str.size());
    for (unsigned char c : str) {
        result.push_back(std::tolower(c));
    }
    return result;
}

Для работы с Unicode (русские буквы, UTF-8)

Если ваша строка содержит кириллицу в UTF-8, std::tolower не подойдёт — он работает только с ASCII. Вам понадобится библиотека вроде ICU или Boost.Locale:

#include <boost/locale.hpp>

std::string to_lower_utf8(const std::string& str) {
    return boost::locale::to_lower(str, std::locale("ru_RU.UTF-8"));
}

Производительность

Если вы вызываете преобразование очень часто (миллионы строк), рассмотрите ручной цикл без лямбды — он может быть немного быстрее за счёт меньшего количества косвенных вызовов. Но для 99% случаев std::transform — лучший выбор по читаемости и надёжности.

В итоге: используйте std::transform с приведением к unsigned char — это стандартный, безопасный и эффективный способ преобразования std::string в нижний регистр в C++.

<<>>

Ручной цикл с явным приведением типа

Если вы предпочитаете не использовать std::transform или работаете в среде, где <algorithm> недоступен (например, в embedded-разработке), можно обойтись простым циклом. Главное — не забыть про приведение к unsigned char:

#include <string>
#include <cctype>

void to_lower_inplace(std::string& str) {
    for (char& ch : str) {
        ch = static_cast<char>(std::tolower(static_cast<unsigned char>(ch)));
    }
}

Этот код работает так же, как и версия с std::transform, но без дополнительных накладных расходов на вызов алгоритма. Для коротких строк разница незаметна, но в цикле с миллионами итераций ручной вариант может быть на 5-10% быстрее.

Недостаток: если вы хотите сохранить исходную строку, придётся вручную копировать её перед вызовом. Для этого удобнее использовать версию, возвращающую новую строку:

std::string to_lower_copy(const std::string& str) {
    std::string result = str;
    to_lower_inplace(result);
    return result;
}

В целом, оба подхода корректны. Выбирайте тот, который вам интуитивно понятнее и лучше вписывается в стиль вашего кода.