Як перетворити std :: string в малі регістри?


777

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

Чи існує альтернатива, яка працює 100% часу?


34
Як інакше ви перетворили б кожен елемент списку чого-небудь іншого, не повторюючи список? Рядок - це лише список символів, якщо вам потрібно застосувати якусь функцію до кожного символу, вам доведеться повторити рядок. Нічого цього не можна.

14
Чому саме це питання знижує рейтинг? У мене немає проблеми з ітерацією через мою рядок, але я запитую, чи є інші функції, крім tolower (), toupper () тощо.
Конрад

3
Якщо у вас є масив символів у стилі C, то, мабуть, ви зможете додати ox20202020 до кожного блоку з 4 символів (за умови, що ВСІ вже є великими літерами), щоб одночасно перетворити 4 символи в малі регістри.

13
@Dan: Якщо вони можуть бути вже малими літерами, але, безумовно, AZ або az, ви можете АБО з 0x20 замість додавання. Один із таких настільки розумних - це, мабуть, німі оптимізації, які майже ніколи не варті ...
Стів Джессоп

4
Я не знаю, чому це було б знято з голосом ... звичайно, це написано трохи дивно (адже вам доведеться якось повторити кожен пункт), але це дійсне питання
warren

Відповіді:


905

Адаптовано з не так часто заданих питань :

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

std::string data = "Abc";
std::transform(data.begin(), data.end(), data.begin(),
    [](unsigned char c){ return std::tolower(c); });

Ви насправді не збираєтеся тікати, не перебираючи кожного персонажа. Немає способу дізнатися, чи є символ символом малого чи великого регістру.

Якщо ви справді ненавидите tolower(), ось спеціалізована альтернатива лише ASCII, яку я не рекомендую вам використовувати:

char asciitolower(char in) {
    if (in <= 'Z' && in >= 'A')
        return in - ('Z' - 'z');
    return in;
}

std::transform(data.begin(), data.end(), data.begin(), asciitolower);

Будьте в курсі, що tolower()можна робити лише заміну на один байт символів, що погано підходить для багатьох сценаріїв, особливо якщо використовується багатобайтове кодування на зразок UTF-8.


25
(Це може бути старе, алгоритми, про які йдеться, мало змінилися) @Stefan Mai: Що таке "ціла партія накладних витрат" у виклику алгоритмів STL? Функції є досить худими (тобто прості для циклів) і часто вбудовані, оскільки ви рідко маєте багато викликів до тієї ж функції з тими ж параметрами шаблону в одному і тому ж блоці компіляції.
eq-

257
Кожного разу, коли ви припускаєте, що персонажі є ASCII, Бог вбиває кошеня. :(
Брайан Гордон

13
У вашому першому прикладі потенційно є невизначена поведінка (перехід charдо ::tolower(int).) Ви повинні переконатися, що ви не передаєте негативне значення.
juanchopanza

37
-1 це використання ::tolowerможе призвести до збоїв, це UB для не ASCII введення.
ура та хт. - Альф

7
:: потрібний перед тим, як звернутись, щоб вказати, що він знаходиться у найбільш віддаленому просторі імен. Якщо ви використовуєте цей код в іншому просторі імен, може бути інше (можливо непов'язане) визначення tolower, яке в кінцевому підсумку буде обрано переважно без ::.
Чарльз Офрія

320

Boost забезпечує рядковий алгоритм для цього :

#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);

2
Я припускаю, що це не має тих самих проблем, що і толоузер з входом ASCII?
паульм

19
Помилки для не-ASCII-7.
DevSolar

1
Чи є немістовна версія цього?
Рей

5
@Ray, так,to_lower_copy
smac89

234

тл; д-р

Використовуйте бібліотеку ICU . Якщо ви цього не зробите, ваша конвертаційна процедура буде мовчки порушуватися на випадки, про які ви, мабуть, не знаєте навіть існуючих.


По- перше , ви повинні відповісти на питання: Що таке кодування з ваших std::string? Це ISO-8859-1? А може, ISO-8859-8? Або кодова сторінка Windows 1252? Чи знає те, що ви використовуєте для перетворення верхніх у малі регістри? (Або це невдало провалюється для персонажів 0x7f?)

Якщо ви використовуєте UTF-8 (єдиний розумний вибір серед 8-бітових кодувань) з std::string якості контейнера, ви вже обманюєте себе, вважаючи, що ви все ще контролюєте речі, оскільки ви зберігаєте багатобайтову послідовність символів у контейнері що не знає багатобайтової концепції. Навіть щось таке просте, як .substr()це тимбол. (Тому що розщеплення багатобайтової послідовності призведе до недійсного (під-) рядка.)

І як тільки ви спробуєте щось подібне std::toupper( 'ß' ), у будь-якому кодуванні ви потрапляєте у глибокі проблеми. (Тому що це неможливо зробити "правильно" зі стандартною бібліотекою, яка може надати лише один результат результату, не "SS"потрібний тут.) [1] Ще один приклад std::tolower( 'I' ), який повинен давати різні результати залежно від локальної локації . У Німеччині 'i'було б правильно; в Туреччині, 'ı'(LATIN SMALL LETTER DOTLESS I) - очікуваний результат (який, знову ж таки, більше кодує один байт у кодуванні UTF-8). Ще один приклад - грецька сигма , великі '∑', малі регістри 'σ'... за винятком кінця слова, де вона знаходиться'ς' .

Отже, будь-яке перетворення випадків, яке працює на персонаж за раз, або, що ще гірше, байт за один раз, порушується дизайном.

Тоді існує точка , що стандартна бібліотека, для чого він це здатний робити, в залежності від локалі , які підтримуються на машині ваше програмне забезпечення працює на ... і що робити , якщо це не так ?

Тож те, що ви насправді шукаєте, це клас струн, який здатний правильно впоратися з усім цим, і це не будь-який з std::basic_string<>варіантів .

(C ++ 11 Примітка: std::u16stringі std::u32stringце краще ., Але все ще не досконалі C ++ 20 принісstd::u8string , але все це потрібно вказати на кодування. У багатьох інших аспектах вони все ще залишаються незнаними щодо механіки Unicode, як нормалізація, порівняння ... .)

Хоча Boost виглядає добре, API мудрий, Boost.Locale - це в основному обгортка навколо ICU . Якщо Boost компілюється з підтримкою ICU ... якщо це не так, Boost.Locale обмежений підтримкою локальної мови, зібраною для стандартної бібліотеки.

І повірте, отримувати підштовхування компілювати з ICU може бути реальною болю іноді. (Для Windows немає попередньо складених бінарних файлів, тому вам доведеться постачати їх разом зі своїм додатком, і це відкриє цілу нову банку черв'яків ...)

Тож особисто я рекомендував би отримати повну підтримку Unicode прямо з вуст коня та безпосередньо користуватися бібліотекою ICU :

#include <unicode/unistr.h>
#include <unicode/ustream.h>
#include <unicode/locid.h>

#include <iostream>

int main()
{
    /*                          "Odysseus" */
    char const * someString = u8"ΟΔΥΣΣΕΥΣ";
    icu::UnicodeString someUString( someString, "UTF-8" );
    // Setting the locale explicitly here for completeness.
    // Usually you would use the user-specified system locale,
    // which *does* make a difference (see ı vs. i above).
    std::cout << someUString.toLower( "el_GR" ) << "\n";
    std::cout << someUString.toUpper( "el_GR" ) << "\n";
    return 0;
}

Компілюйте (із G ++ у цьому прикладі):

g++ -Wall example.cpp -licuuc -licuio

Це дає:

ὀδυσσεύς

Зауважте, що конверсія Σ <-> σ в середині слова, а конверсія Σ <-> ς в кінці слова. Жодне <algorithm>рішення, яке не базується на цьому, не може вам цього дати.


[1] У 2017 році Рада з німецької ортографії вирішила, що "ẞ" U + 1E9E LATIN CAPITAL LETTER SHARP S може бути використаний офіційно, як варіант поруч із традиційним перетворенням "SS", щоб уникнути неоднозначності, наприклад, у паспортах (де імена з великої літери ). Мій гарний приклад, застарілий рішенням комітету ...


19
Це правильна відповідь у загальному випадку. Стандарт не дає нічого для обробки нічого, окрім "ASCII", крім брехні та обману. Це змушує вас думати, що ви можете мати справу з, можливо, UTF-16, але ви не можете. Як зазначено в цій відповіді, ви не можете отримати належну довжину символів (не довжину байтів) рядка UTF-16, не виконавши власне оброблення unicode. Якщо вам доведеться мати справу з реальним текстом, скористайтеся ICU. Дякуємо, @DevSolar
Обмежене спокутування

Чи доступний ICU за замовчуванням в Ubuntu / Windows або його потрібно встановити окремо? А як щодо цієї відповіді: stackoverflow.com/a/35075839/207661 ?
Шітал Шах

1
Гей, дивись, справжня відповідь! Дякуємо, що вказали на мене в правильному напрямку, DevSolar.
Ден Бешард

2
@DevSolar Погодився! Поняття довжини є досить безглуздим у тексті (ми могли б додати лігатури до списку правопорушників). Це означає, що оскільки люди звикли вкладати та керувати символами, що займають одну одиницю довжини, то кодові точки будуть більш інтуїтивно зрозумілим заходом. О, і дякую за правильну відповідь, сумно бачити це так далеко :-(
masaers

3
@LF Незначно краще. Але так багато речей все ще не висвітлено: toupperі tolowerдосі працюють над окремими персонажами. Клас рядків все ще не має поняття нормалізації (наприклад, чи "ü" кодується як "u з діаерезом" або "u +, що поєднує діарез") або де рядок може бути, а може і не бути відокремленою. Список продовжується. u8string (як і інші стандартні класи рядків) підходить для "проходження". Але якщо ви хочете обробити Unicode, вам знадобиться ICU.
DevSolar

36

Використовуючи діапазон для циклу 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);
}

9
Однак на французькій машині ця програма не конвертує символи, що не містять ASCII, дозволеними французькою мовою. Наприклад, рядок 'Test String123. É Ï \ n 'буде перетворено на:' test string123. É Ï \ n ', хоча символи É Ï та їхні малі регістри' é 'та' ï 'дозволені французькою мовою. Здається, що жодне рішення для цього не було надано іншими повідомленнями цього потоку.
надрізає

Я думаю, вам потрібно встановити для цього належну локаль.
користувач1095108

@incises, це тоді хтось опублікував відповідь про ICU, і це, безумовно, шлях. Простіше, ніж більшість інших рішень, які б намагалися зрозуміти місцевість.
Алексіс Вілке

Я вважаю за краще не використовувати зовнішні бібліотеки, коли це можливо, особисто.
kayleeFrye_onDeck


15

Це відповідь на відповідь Стефана Май: якщо ви хочете помістити результат перетворення в інший рядок, перед закликом потрібно заздалегідь виділити його місце для зберігання 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;
}

1
Для мене це не
змінило

Тут також можна використовувати ітератор зворотного вставлення замість ручного розміру.
чилі

11

Інший підхід, що використовує діапазон, заснований на циклі з перемінною змінною

string test = "Hello World";
for(auto& c : test)
{
   c = tolower(c);
}

cout<<test<<endl;

6

Наскільки я бачу, бібліотеки Boost справді погані. Я перевірив їхню невпорядковану карту на 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, тому мені не потрібно тестувати його на своїй машині.


1
Ви відкрили параметри оптимізації під час компіляції? Я думаю, що бібліотека STL для важких прискорень повинна працювати краще з високим рівнем оптимізації.
Вей Пісня

1
Я використовував -O2 в одному з тестів, і більше нічого.
Етереалоне

2
Продуктивність unororder_map залежить від алгоритму хешування в поєднанні з даними, які ви використовуєте. Не існує чарівного алгоритму хешування, який працює для всіх та будь-яких даних, щоб зробити невпорядковану_мапу якомога швидшою. Визначте і спробуйте різні речі. Причина, з якою ви погіршуєте продуктивність, полягає в тому, що при використанні хешу ви отримуєте безліч зіткнень, що в основному викликає пошук у списку. Перегляньте цей сайт для отримання додаткової інформації: fgda.pl/post/7/gcc-hash-map-vs-unordered-map Для моїх цілей функція, що надається за посиланням, зменшила зіткнення і, таким чином, була дуже швидкою.
leetNightshade

6

Найпростіший спосіб перетворити рядок у низький розмір, не турбуючись про 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;
}

5

std::ctype::tolower()зі стандартної бібліотеки локалізації C ++ це правильно зробить для вас. Ось приклад, витягнутий із довідкової сторінки на толовер

#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";
}

Приємно, доки ви можете перетворити символів на місце. Що робити, якщо ваш джерельний рядок const? Це, мабуть, робить його трохи бруднішим (наприклад, він не виглядає так, як можна використовувати f.tolower()), оскільки вам потрібно поставити символи в нову рядок. Чи використовуєте ви transform()щось подібне std::bind1st( std::mem_fun() )для оператора?
квазар

Для рядка const ми можемо просто зробити локальну копію, а потім перетворити її на місце.
Самєр

Так, хоча виготовлення копії додає більше витрат.
quazar

Ви можете використовувати std :: transform з версією ctype :: tolower, яка не бере покажчиків. Використовуйте адаптер ітератора заднього вставки, і вам навіть не потрібно турбуватися про попередній розмір вихідного рядка.
чилі

Чудово, тим більше, що в параметрі libstdc ++ tolowerз localeпараметром неявний виклик, як use_facetвидається, є вузьким місцем продуктивності. Один із моїх колег досяг декількох 100-відсоткового збільшення швидкості, замінивши boost::iequals(що має цю проблему) на версію, в якій use_facetлише один раз викликається поза циклу.
Арне Фогель

3

Альтернативою Boost є POCO (pocoproject.org).

POCO пропонує два варіанти:

  1. Перший варіант робить копію, не змінюючи початковий рядок.
  2. Другий варіант змінює початковий рядок на місці.
    У версіях "In Place" завжди в назві є "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);

3

Існує спосіб перетворити верхній регістр на нижній БЕЗ виконання, якщо тести , і це досить прямо. Функція isupper () / використання макросу clocale.h має вирішити проблеми, пов’язані з вашим місцезнаходженням, але якщо ні, ви завжди можете налаштувати UtoL [] на вміст вашого серця.

Враховуючи, що символи C - це справді лише 8-бітні вставки (ігноруючи широкі набори символів на даний момент), ви можете створити масив 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.


2
"Є спосіб перетворити верхній регістр на нижній БЕЗ виконання, якщо тести" коли-небудь чули про таблиці пошуку?
Габор Буелла

1
Не визначена поведінка для негативних символів.
Roland Illig

Сучасні процесори вузькі у пам'яті, а не в процесорі. Бенчмаркінг був би цікавим.
Контанго

3

Оскільки жодна з відповідей не згадала про майбутню бібліотеку діапазонів, яка доступна в стандартній бібліотеці з 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> і необхідні заголовки діапазонів.)

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

Як і всі інші функції <cctype>, поведінка std::tolowerне визначена, якщо значення аргументу не є ні представним, unsigned charні рівним EOF. Щоб безпечно використовувати ці функції з простими chars (або signed chars), аргумент спочатку слід перетворити на 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;
}

3

Мій власний шаблон функції, який виконує великі та малі регістри.

#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);
}

Це те, що мені було потрібно. Я просто використав towlowerдля широких символів, який підтримує UTF-16.
липня

2

Ось макро-техніка, якщо ви хочете щось просте:

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


1
Я заперечую за тим, щоб надавати макроси, коли існує ідеально хороше рішення - ви навіть даєте ці рішення.
Ясніший

2
Макро-техніка означає менше вводити код для чогось, що зазвичай використовується багато в програмуванні. Чому б не використати це? Інакше навіщо взагалі макроси?
Воломіке

3
Макроси - це спадщина від C, над якою багато працюють, щоб позбутися. Якщо ви хочете зменшити кількість друку, скористайтеся функцією або лямбда. void strtoupper(std::string& x) { std::transform (x.begin(), x.end(), x.begin(), ::toupper); }
Ясніше

1
@Clearer Оскільки я хочу стати кращим кодером, чи можете ви надати мені будь-які посилання на документи ANSI, де будь-які комітети ANSI C ++ говорять про щось: "Нам потрібно скликати нараду, щоб позбутися макросів із C ++"? Або якась інша дорожня карта?
Воломіке

2
Ні, я не можу. Позиція Б'ярне щодо цієї теми неодноразово висловлювалася досить чітко. Крім того, є маса причин не використовувати макроси в C, а також у C ++. xможе бути дійсним виразом, що просто трапляється правильно складати, але дасть абсолютно нечіткі результати через макросів.
Ясніший

2
// 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/


2

Чи існує альтернатива, яка працює 100% часу?

Ні

Є кілька питань, які потрібно задати собі перед тим, як вибрати метод нижнього корпусу.

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

Коли ви отримаєте відповіді на ці питання, ви можете почати шукати рішення, яке відповідає вашим потребам. Немає розміру, який відповідає всім, що працює для всіх скрізь!


2

Спробуйте цю функцію :)

string toLowerCase(string str) {
    int str_len = str.length();
    string final_str = "";
    for(int i=0; i<str_len; i++) {
        char character = str[i];
        if(character>=65 && character<=92) {
            final_str += (character+32);
        } else {
            final_str += character;
        }
    }
    return final_str;
}

1

На платформах 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 );
}

0

Знімок коду

#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;
}


0

Скопіюйте, оскільки відповідь заборонено покращувати. Дякую ТАК


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 for identifier c.


Поясніть, будь ласка, звідки ви скопіювали свою відповідь.
bfontaine

0

У C ++ немає методів толлера або таппер, що реалізуються для рядкових, але він доступний для 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;
}

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


-1

Це може бути ще одна проста версія, щоб перетворити великі регістри в малі і навпаки. Я використовував версію спільноти 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;
}

Примітка: якщо є спеціальні символи, їх потрібно обробляти за допомогою перевірки стану.


-8

Я спробував std :: transform, все, що я отримую, - це гидка stl криптична помилка компіляції, яку можуть зрозуміти лише друїди з 200 років тому (не можуть перетворитись на флібіді-флабіді-грип)

це прекрасно працює і його можна легко налаштувати

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;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.