Перетворення рядка в C ++ у верхній регістр


268

Як можна перетворити рядок у верхній регістр. Приклади, які я знайшов у Google, мають стосуватися лише знаків.

Відповіді:


205

Алгоритми збільшення рядкових рядків:

#include <boost/algorithm/string.hpp>
#include <string>

std::string str = "Hello World";

boost::to_upper(str);

std::string newstr = boost::to_upper_copy<std::string>("Hello World");

5
Це також має перевагу i18n, де ::toupper, швидше за все, передбачається ASCII.
Бен Штрауб

4
Ваш останній рядок не std::string newstr(boost::to_upper_copy<std::string>("Hello World"));
складається

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

44
так, я збираюся встановити boost тільки для to_upper ... відмінна ідея! </sarcasm> :)
thang

12
Я особисто не налаштований на прискорення як відповідь на "як мені зробити х в C ++?" адже прискорення - це зовсім не легке рішення. Здається, ви або купуєте boost як рамки (або ACE, або Qt, або Recusion ToolKit ++, або ...), або цього не робите. Я вважаю за краще бачити мовні рішення.
jwm

486
#include <algorithm>
#include <string>

std::string str = "Hello World";
std::transform(str.begin(), str.end(),str.begin(), ::toupper);

8
Насправді toupper()може бути реалізований як макрос. Це може спричинити проблему.
неспокійно

3
прив'язка (:: toupper, конструкція <неподписаний char> (_ 1)) з boost.lambda буде служити прекрасно, я думаю.
Йоханнес Шауб - ліб

11
Такий підхід працює добре для ASCII, але не вдається для багатобайтових кодувань символів або для спеціальних правил обкладинки, таких як німецька 'ß'.
dan04

9
Я змінив прийняту відповідь на той, що використовує збільшити бібліотеки, тому що це було швидше (в моєму неофіційному тестуванні), простіше у використанні і не має проблем, пов'язаних з цим рішенням. Все-таки гарне рішення для випадків, коли не можна використовувати прискорення.
OrangeAlmondSoap

2
Я не можу зрозуміти, чому компілятор відкидає цей код без :: kvalifier раніше toupper. Будь-які ідеї?
sasha.sochka

89

Коротке рішення з використанням C ++ 11 та toupper ().

for (auto & c: str) c = toupper(c);

Не cбуде const charтипу (від auto)? Якщо так, ви не можете призначити його (через constчастину) тому, що повертається toupper(c).
PolGraphic

5
@PolGraphic: Range - заснований на використанні методів start () / end () контейнера, щоб перебирати його вміст. std :: basic_string має як const, так і змінний ітератор (повертається cbegin () та start () відповідно, див. std :: basic_string :: begin ), тому для (:) використовується відповідний (cbegin (), якщо str оголошено const, з auto =: = const char, почніть () в іншому випадку, з auto =: = char).
Thanasis Papoutsidakis

5
Дивіться диверсифікуючий вугілля внизу, його cпотрібно зробити unsigned charдля того, щоб це було виправлено.
Cris Luengo

to_upper () boost здається набагато більш відповідає функціям c ++ STL, ніж toupper.
tartaruga_casco_mole

29
struct convert {
   void operator()(char& c) { c = toupper((unsigned char)c); }
};

// ... 
string uc_str;
for_each(uc_str.begin(), uc_str.end(), convert());

Примітка. Кілька проблем з найкращим рішенням:

21.5 Утиліти послідовності, що закінчуються нулем

Вміст цих заголовків повинен бути таким самим, як і заголовки Стандартної бібліотеки C <ctype.h>, <wctype.h>, <string.h>, <wchar.h> та <stdlib.h> [...]

  • Що означає, що cctypeчленами цілком можуть бути макроси, не придатні для прямого споживання в стандартних алгоритмах.

  • Ще одна проблема з тим же прикладом полягає в тому, що він не наводить аргумент і не перевіряє, що це негативно; це особливо небезпечно для систем, де charпідписано рівнину . (Причина: якщо це реалізовано як макрос, він, ймовірно, використовувати таблицю пошуку та ваші аргументи індексує цю таблицю. Від'ємний індекс дасть вам UB.)


Нормальні члени cctype - макроси. Я пам'ятаю, як читав, що вони також повинні були бути функціями, хоча у мене немає копії стандарту C90 і не знаю, чи було це чітко зазначено чи ні.
Девід Торнлі

1
вони повинні бути функціями в C ++ - навіть якщо C дозволяє їм бути макросами. Я згоден з вашим другим пунктом щодо кастингу, хоча. верхнє рішення може передати негативні значення і викликати UB з цим. ось чому я не проголосував за нього (але я також не проголосував) :)
Йоханнес Шауб - ліб

1
стандартна цитата не повинна бракувати: 7.4.2.2/1 (погана лампочка, це лише посилання на чернетку C99 TC2) та C ++ 17.4.1.2/6 у стандарті c ++ 98.
Йоханнес Шауб - ліб

1
(Зверніть увагу на виносці до нього: «Ця Забороняє звичайна практика надання маскування макро .... бла blupp .. Єдиний спосіб зробити це в C ++ є забезпечення Екстерн функцію інлайн.») :)
Johannes Schaub - ліб

1
... що досягається цією хитрістю: stackoverflow.com/questions/650461/…
Йоханнес Шауб - ліб

27

Ця проблема піддається перебору SIMD для набору символів ASCII.


Швидкісні порівняння:

Попередній тест на x86-64 gcc 5.2 -O3 -march=nativeна Core2Duo (Merom). Один і той же рядок з 120 символів (змішаний нижній і нижній регістр ASCII), перетворений у цикл 40 М разів (без перехресних файлів, тому компілятор не може оптимізувати або витягнути його з циклу). Буфери одних і тих же джерел і dest, тому жодних накладних ефектів або ефектів пам'яті / кешу немає, дані зберігаються в кеш-пам'яті L1 весь час, і ми суто пов'язані з процесором.

  • boost::to_upper_copy<char*, std::string>(): 198,0с . Так, Boost 1.58 на Ubuntu 15.10 справді такий повільний. Я профілював одномоментну зону у відладчику, і це справді, дуже погано: у кожного персонажа відбувається динамічна передача змінної локалі !!! (dynamic_cast приймає кілька викликів до strcmp). Це буває з LANG=Cі з LANG=en_CA.UTF-8.

    Я не тестував використання RangeT, крім std :: string. Можливо, інша формаto_upper_copy оптимізує краще, але я думаю, що це завжди буде new/ mallocмісця для копії, тому важче перевірити. Можливо, щось, що я зробив, відрізняється від звичайного випадку використання, і, можливо, нормально зупинений g ++ може підняти інформацію про налаштування локалі з циклу для персонажа. Моє читання з циклу від std::stringі записування до char dstbuf[4096]сенсу має сенс для перевірки.

  • циклічний виклик glibc toupper: 6,67s (не перевіряючи intрезультат для потенційного багатобайтового UTF-8. Хоча це має значення для турецької мови.)

  • Цикл лише для ASCII: 8,79s (моя базова версія для результатів нижче). Очевидно, пошук таблиць швидший, ніж a cmov, в будь-якому випадку таблиця гаряча в L1.
  • Авто-векторизований лише для ASCII: 2,51 с . (120 знаків - це половина шляху між найгіршим та найкращим випадком, див. Нижче)
  • ASCII тільки вручну векторизований: 1,35s

Дивіться також це питання про toupper()повільність роботи в Windows, коли встановлено локальний код .


Мене вразило, що Boost на порядок повільніше, ніж інші варіанти. Я ще раз перевірив, що я -O3ввімкнув, і навіть однокроковим кроком засвідчив, що він робить. Це майже однакова швидкість з клангом ++ 3,8. Він має величезні накладні знаки всередині циклу. perf record/ reportРезультат (для cyclesподії перфорування) є:

  32.87%  flipcase-clang-  libstdc++.so.6.0.21   [.] _ZNK10__cxxabiv121__vmi_class_type_info12__do_dyncastElNS_17__class_type_info10__sub_kindEPKS1_PKvS4_S6_RNS1_16
  21.90%  flipcase-clang-  libstdc++.so.6.0.21   [.] __dynamic_cast                                                                                                 
  16.06%  flipcase-clang-  libc-2.21.so          [.] __GI___strcmp_ssse3                                                                                            
   8.16%  flipcase-clang-  libstdc++.so.6.0.21   [.] _ZSt9use_facetISt5ctypeIcEERKT_RKSt6locale                                                                     
   7.84%  flipcase-clang-  flipcase-clang-boost  [.] _Z16strtoupper_boostPcRKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE                                   
   2.20%  flipcase-clang-  libstdc++.so.6.0.21   [.] strcmp@plt                                                                                                     
   2.15%  flipcase-clang-  libstdc++.so.6.0.21   [.] __dynamic_cast@plt                                                                                             
   2.14%  flipcase-clang-  libstdc++.so.6.0.21   [.] _ZNKSt6locale2id5_M_idEv                                                                                       
   2.11%  flipcase-clang-  libstdc++.so.6.0.21   [.] _ZNKSt6locale2id5_M_idEv@plt                                                                                   
   2.08%  flipcase-clang-  libstdc++.so.6.0.21   [.] _ZNKSt5ctypeIcE10do_toupperEc                                                                                  
   2.03%  flipcase-clang-  flipcase-clang-boost  [.] _ZSt9use_facetISt5ctypeIcEERKT_RKSt6locale@plt                                                                 
   0.08% ...

Автовекторизація

Gcc і clang будуть автоматично векторизувати цикли лише тоді, коли перед циклом буде відомо число ітерацій. (тобто петлі пошуку, як звичайна реалізація на C strlen, не автоматично визначають розмір.)

Таким чином, для рядків, досить малих, щоб вміститись у кеш, ми отримуємо значну швидкість для довгих ~ 128 символів, починаючи з strlenперших. Це не буде необхідним для рядків явної довжини (наприклад, C ++ std::string).

// char, not int, is essential: otherwise gcc unpacks to vectors of int!  Huge slowdown.
char ascii_toupper_char(char c) {
    return ('a' <= c && c <= 'z') ? c^0x20 : c;    // ^ autovectorizes to PXOR: runs on more ports than paddb
}

// gcc can only auto-vectorize loops when the number of iterations is known before the first iteration.  strlen gives us that
size_t strtoupper_autovec(char *dst, const char *src) {
    size_t len = strlen(src);
    for (size_t i=0 ; i<len ; ++i) {
        dst[i] = ascii_toupper_char(src[i]);  // gcc does the vector range check with psubusb / pcmpeqb instead of pcmpgtb
    }
    return len;
}

Будь-який пристойний libc матиме ефективніше, strlenщо набагато швидше, ніж циклічно виконувати байт, тому окремі векторизовані strlen та toupper цикли швидше.

Базова лінія: цикл, який перевіряє, чи закінчується 0 на льоту.

Час для ітерацій 40M на Core2 (Merom) 2,4 ГГц. gcc 5.2 -O3 -march=native. (Ubuntu 15.10). dst != src(тому ми робимо копію), але вони не перетинаються (і не знаходяться поруч). Обидва вирівняні.

  • 15 символів: базовий рядок: 1,08 с. autovec: 1.34s
  • 16 рядок char: базовий рівень: 1.16s. autovec: 1,52с
  • 127 char string: базовий рівень: 8.91s. autovec: 2,98s // Невекторне очищення має 15 символів для обробки
  • 128 рядків: основна лінія: 9,00 с. autovec: 2,06с
  • 129 char string: базовий рівень: 9.04s. autovec: 2.07s // Невекторне очищення має 1 знак для обробки

Деякі результати дещо відрізняються від кланг.

Цикл мікро-орієнтирів, який викликає функцію, знаходиться в окремому файлі. В іншому випадку він вставляється і strlen()піднімається з петлі, і він працює різко швидше, особливо. для 16 рядків char (0,187s).

Це має головну перевагу в тому, що gcc може автоматично векторизувати його для будь-якої архітектури, але головним недоліком є ​​те, що для звичайних звичайних випадків малих рядків це повільніше.


Отже, існують великі прискорення, але автоматична векторизація компілятора не робить великого коду, особливо. для очищення останніх до 15 символів.

Ручна векторизація за допомогою SSE:

На основі моєї функції обертання регістру, яка інвертує регістр кожного алфавітного символу. Скористається "підписом порівняння без підпису", де ви можете виконати low < a && a <= highодне неподписане порівняння шляхом зміщення діапазону, так що будь-яке значення менше, ніж lowобгортання, на значення, яке перевищує high. (Це працює, якщо lowі highне надто далеко один від одного.)

SSE має лише підписане порівняння-більше, але ми все ще можемо використовувати трюк "неподписане порівняння" шляхом зміщення діапазону в нижній частині підписаного діапазону: Віднімайте "a" + 128, тому алфавітні символи варіюються від -128 до -128 +25 (-128 + 'z' - 'a')

Зауважте, що додавання 128 і віднімання 128 - це одне і те ж для 8-бітних цілих чисел. Нікуди перевезти нікуди, тож просто xor (переносимо додати), перевертаючи високий шматочок.

#include <immintrin.h>

__m128i upcase_si128(__m128i src) {
    // The above 2 paragraphs were comments here
    __m128i rangeshift = _mm_sub_epi8(src, _mm_set1_epi8('a'+128));
    __m128i nomodify   = _mm_cmpgt_epi8(rangeshift, _mm_set1_epi8(-128 + 25));  // 0:lower case   -1:anything else (upper case or non-alphabetic).  25 = 'z' - 'a'

    __m128i flip  = _mm_andnot_si128(nomodify, _mm_set1_epi8(0x20));            // 0x20:lcase    0:non-lcase

    // just mask the XOR-mask so elements are XORed with 0 instead of 0x20
    return          _mm_xor_si128(src, flip);
    // it's easier to xor with 0x20 or 0 than to AND with ~0x20 or 0xFF
}

Враховуючи цю функцію, яка працює для одного вектора, ми можемо викликати її в циклі для обробки цілого рядка. Оскільки ми вже орієнтовані на SSE2, ми можемо одночасно робити векторизовану перевірку кінцевих рядків.

Ми також можемо зробити набагато краще для "очищення" останніх до 15-ти байтів, що залишилися після виконання векторів 16B: верхній кожух є безсильним, тому повторна обробка деяких вхідних байтів чудово. Ми робимо нерівне завантаження останнього 16B джерела і зберігаємо його в буфер dest, який перекриває останній сховище 16B з циклу.

Єдиний раз, коли це не працює, це коли весь рядок знаходиться під 16B: Навіть коли dst=srcнеатомне читання-зміна-запис - це не те саме, що взагалі не торкатися деяких байтів, і може порушити багатопотоковий код.

Для цього у нас є скалярна петля, а також для srcвирівнювання. Оскільки ми не знаємо, де буде завершувати 0, нестандартне завантаження з сайту srcможе перейти на наступну сторінку та розміститись за замовчуванням. Якщо нам потрібні будь-які байти у вирівняному фрагменті 16В, завжди безпечно завантажувати весь вирівняний шматок 16В.

Повне джерело: у суглобі github .

// FIXME: doesn't always copy the terminating 0.
// microbenchmarks are for this version of the code (with _mm_store in the loop, instead of storeu, for Merom).
size_t strtoupper_sse2(char *dst, const char *src_begin) {
    const char *src = src_begin;
    // scalar until the src pointer is aligned
    while ( (0xf & (uintptr_t)src) && *src ) {
        *(dst++) = ascii_toupper(*(src++));
    }

    if (!*src)
        return src - src_begin;

    // current position (p) is now 16B-aligned, and we're not at the end
    int zero_positions;
    do {
        __m128i sv = _mm_load_si128( (const __m128i*)src );
        // TODO: SSE4.2 PCMPISTRI or PCMPISTRM version to combine the lower-case and '\0' detection?

        __m128i nullcheck = _mm_cmpeq_epi8(_mm_setzero_si128(), sv);
        zero_positions = _mm_movemask_epi8(nullcheck);
        // TODO: unroll so the null-byte check takes less overhead
        if (zero_positions)
            break;

        __m128i upcased = upcase_si128(sv);   // doing this before the loop break lets gcc realize that the constants are still in registers for the unaligned cleanup version.  But it leads to more wasted insns in the early-out case

        _mm_storeu_si128((__m128i*)dst, upcased);
        //_mm_store_si128((__m128i*)dst, upcased);  // for testing on CPUs where storeu is slow
        src += 16;
        dst += 16;
    } while(1);

    // handle the last few bytes.  Options: scalar loop, masked store, or unaligned 16B.
    // rewriting some bytes beyond the end of the string would be easy,
    // but doing a non-atomic read-modify-write outside of the string is not safe.
    // Upcasing is idempotent, so unaligned potentially-overlapping is a good option.

    unsigned int cleanup_bytes = ffs(zero_positions) - 1;  // excluding the trailing null
    const char* last_byte = src + cleanup_bytes;  // points at the terminating '\0'

    // FIXME: copy the terminating 0 when we end at an aligned vector boundary
    // optionally special-case cleanup_bytes == 15: final aligned vector can be used.
    if (cleanup_bytes > 0) {
        if (last_byte - src_begin >= 16) {
            // if src==dest, this load overlaps with the last store:  store-forwarding stall.  Hopefully OOO execution hides it
            __m128i sv = _mm_loadu_si128( (const __m128i*)(last_byte-15) ); // includes the \0
            _mm_storeu_si128((__m128i*)(dst + cleanup_bytes - 15), upcase_si128(sv));
        } else {
            // whole string less than 16B
            // if this is common, try 64b or even 32b cleanup with movq / movd and upcase_si128
#if 1
            for (unsigned int i = 0 ; i <= cleanup_bytes ; ++i) {
                dst[i] = ascii_toupper(src[i]);
            }
#else
            // gcc stupidly auto-vectorizes this, resulting in huge code bloat, but no measurable slowdown because it never runs
            for (int i = cleanup_bytes - 1 ;  i >= 0 ; --i) {
                dst[i] = ascii_toupper(src[i]);
            }
#endif
        }
    }

    return last_byte - src_begin;
}

Час для ітерацій 40M на Core2 (Merom) 2,4 ГГц. gcc 5.2 -O3 -march=native. (Ubuntu 15.10). dst != src(тому ми робимо копію), але вони не перетинаються (і не знаходяться поруч). Обидва вирівняні.

  • 15 символів: базовий рядок: 1,08 с. autovec: 1.34s. посібник: 1,29с
  • 16 рядок char: базовий рівень: 1.16s. autovec: 1,52с. посібник: 0,335с
  • 31 рядок: посібник: 0,479s
  • 127 char string: базовий рівень: 8.91s. autovec: 2,98с. посібник: 0,925с
  • 128 рядків: основна лінія: 9,00 с. autovec: 2,06с. посібник: 0,931с
  • 129 char string: базовий рівень: 9.04s. autovec: 2,07с. посібник: 1,02с

(Насправді приурочено _mm_storeдо циклу, ні _mm_storeu, тому що storeu повільніше на Merom, навіть коли адреса вирівнюється. Це добре в Nehalem і пізніше. Я також залишив код таким, який є зараз, замість того, щоб виправити невдачу копіювати закінчення 0 у деяких випадках, тому що я не хочу все час повторювати.)

Отже, для коротких рядків довше 16B це значно швидше, ніж автовекторизовані. Довжина шириною однієї, меншої за вектор не представляє проблеми. Вони можуть виникнути проблеми при роботі на місці через стійла переадресації магазину. (Але зауважте, що все-таки добре обробити власний вихід, а не оригінальний ввід, тому що таппер є ідентичним).

Існує багато можливостей для налаштування цього для різних випадків використання, залежно від того, що хоче оточуючий код, і цільової мікроархітектури. Отримати компілятор, щоб видати приємний код для частини очищення, складно. Використання ffs(3)(яке компілюється в bsf або tzcnt на x86) здається хорошим, але очевидно, що цей біт потребує переосмислення, оскільки я помітив помилку після написання більшості цього відповіді (див. Коментарі FIXME).

Вектор прискорення для ще менших рядків можуть бути отримані з допомогою movqабо movdнавантажень / магазинів. Налаштуйте по мірі необхідності для вашого використання.


UTF-8:

Ми можемо виявити, коли наш вектор має будь-які байти з високим набором бітів, і в такому випадку повернемося до скалярного циклу utf-8 для цього вектора. dstТочка може просуватися по різному кількості , ніж srcпокажчик, але як тільки ми повернемося до вирівняні srcвказівником, ми ще тільки зробити невирівняні вектор магазини в dst.

Для тексту UTF-8, але в основному складається з підмножини ASCII UTF-8, це може бути хорошою: висока ефективність у загальній справі з правильною поведінкою у всіх випадках. Коли є багато не-ASCII, це, мабуть, буде гірше, ніж весь час залишатися в скалярному циклі UTF-8.

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


Відомо про місце:

У турецькій локалі ( tr_TR), правильний результат з toupper('i')є 'İ'(U0130), а НЕ 'I'(звичайний ASCII). Дивіться коментарі Мартіна Боннера до питання про tolower()повільність роботи в Windows.

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

З такою значною складністю SSE4.2 PCMPISTRMабо щось подібне може зробити багато наших перевірок за один раз.


20

Чи є у вас рядки ASCII або International у рядках?

Якщо це останній випадок, "верхній регістр" не такий простий, і це залежить від використовуваного алфавіту. Існують двопалатні та однопалатні алфавіти. Тільки двопалатні алфавіти мають різні символи для верхнього та нижнього регістру. Також є складені символи, такі як латинська велика літера 'DZ' (\ u01F1 'DZ'), які використовують так званий регістр заголовка . Це означає, що змінюється лише перший символ (D).

Я пропоную вам розібратися в ICU та відмінність між простими та повнорозмірними відображеннями. Це може допомогти:

http://userguide.icu-project.org/transforms/casemappings


7
Або німецький eszet (sp?), Що схоже на грецьку букву beta, і означає "ss". Немає жодного німецького символу, який означає "SS", що є великим еквівалентом. Німецьке слово "street", коли великі літери, набирає один символ довше.
Девід Торнлі

6
Інший особливий випадок - грецька літера сигма (Σ), яка має дві малі версії, залежно від того, знаходиться вона в кінці слова (ς) чи ні (σ). І тоді є мовні правила, як, наприклад, у турецьких, які мають відображення випадку I↔ı та İ↔i.
dan04

1
"Перекриття" називається складанням корпусу.
Коламбо

20
string StringToUpper(string strToConvert)
{
   for (std::string::iterator p = strToConvert.begin(); strToConvert.end() != p; ++p)
       *p = toupper(*p);

   return p;
}

Або,

string StringToUpper(string strToConvert)
{
    std::transform(strToConvert.begin(), strToConvert.end(), strToConvert.begin(), ::toupper);

    return strToConvert;
}

4
якщо у вас немає доступу до підвищення другого рішення, мабуть, найкраще, що ви можете отримати. що роблять зірки **після параметрів першого рішення?
Сем Брінк

1
Я впевнений, що **це помилка друку від спроби використання жирного шрифту в синтаксисі коду.
MasterHD

1
Цей код викликає невизначене поведінку, коли toupperвикликається з негативними числами.
Роланд Ілліг

17

Наступні роботи для мене.

#include <algorithm>
void  toUpperCase(std::string& str)
{
    std::transform(str.begin(), str.end(), str.begin(), ::toupper);
}

int main()
{
   std::string str = "hello";
   toUpperCase(&str);
}

Зауважимо, що std :: перетворення визначено в <алгоритмі>
редакція

Так. цей # включити обов'язково, #include <алгоритм>
Pabitra Dash

1
Цей код викликає невизначене поведінку, коли toupperвикликається з негативними числами.
Роланд Ілліг

дублікат відповіді, наданий користувачем648545 - -1
Пьотр Доброгост

@PiotrDobrogost Я не маю уявлення про відповідь, яку дав user648545. Я не скопіював це. Коли я порівнюю два методи, підпис методу зовсім відрізняється, хоча обидві функції використовують перетворення функції бібліотеки.
Пабітра Даш

13

Використовуйте лямбда.

std::string s("change my case");

auto to_upper = [] (char_t ch) { return std::use_facet<std::ctype<char_t>>(std::locale()).toupper(ch); };

std::transform(s.begin(), s.end(), s.begin(), to_upper);

2
Байрон, не хвилюйся за інші коментарі. Цілком нормально відповідати на старі питання з новим (сучасним) рішенням, як і ви.
Ківерія

13

Більш швидкий, якщо ви використовуєте лише символи ASCII :

for(i=0;str[i]!=0;i++)
  if(str[i]<='z' && str[i]>='a')
    str[i]-=32;

Зауважте, що цей код працює швидше, але працює лише на ASCII і не є "абстрактним" рішенням.

Якщо вам потрібні рішення UNICODE або більш звичайні та абстрактні рішення, знайдіть інші відповіді та працюйте з методами рядків C ++.


1
Питання позначене як C++, але ви тут написали Cвідповідь. (Я не один з низовидів.)
hkBattousai

6
Я написав відповідь C і відповідь C ++ тут ​​написаний, оскільки C ++ написано так, щоб бути повністю сумісним із джерелами C, тому будь-яке рішення C також є правильним рішенням C ++
Лука C.

Але набагато краще дати відповідь, яка поважає спосіб C ++.
Дмитро Юрченко

Стандартним способом c ++ було б використання std :: transform з toupper. Це менш код і точно портативний. Цей код покладається на "факт", що система буде використовувати ascii як механізм кодування символів. Не впевнений, що всі системи базуються на цьому кодуванні, і тому не впевнений, що це портативний.
AlexTheo

1
Чому ви вирішили використовувати коди ASCII замість вписаних символів '?
HolyBlackCat

11

Поки ви добре працюєте лише з ASCII, і ви можете надати дійсний вказівник на RW-пам’ять, в C є простий і дуже ефективний однолінійний:

void strtoupper(char* str)
{ 
    while (*str) *(str++) = toupper((unsigned char)*str);
}

Це особливо добре для простих рядків, таких як ідентифікатори ASCII, які ви хочете нормалізувати, в той самий регістр символів. Потім можна використовувати буфер для побудови екземпляра std: string.


Один зазначає, що ця відповідь стосується змінного струму, а не std :: string
EvilTeach

Це має очевидний властивий недолік безпеки. Я б цього не робив.
Байрон

9
//works for ASCII -- no clear advantage over what is already posted...

std::string toupper(const std::string & s)
{
    std::string ret(s.size(), char());
    for(unsigned int i = 0; i < s.size(); ++i)
        ret[i] = (s[i] <= 'z' && s[i] >= 'a') ? s[i]-('a'-'A') : s[i];
    return ret;
}

s.size () має тип std :: size_t, який AFAIK міг би бути неподписаним int залежно від реалізації
odinthenerd

Я не думаю, що існує жодна сучасна реалізація, в якій підписаний результат std :: string :: size. З огляду на те, що як семантично, так і практично, немає такого поняття, як негативний розмір, я збираюся перейти з size_t, принаймні, 32-бітним непідписаним цілим числом.
user1329482

Немає причин не писати for (size_t i = 0 .... Також немає вагомих причин зробити так важко читати. Це також спочатку копіює рядок, а потім петлю над нею. @ Відповідь Луки в чомусь краща, за винятком того, що не використовуються 'a'символьні константи.
Пітер Кордес

9
#include <string>
#include <locale>

std::string str = "Hello World!";
auto & f = std::use_facet<std::ctype<char>>(std::locale());
f.toupper(str.data(), str.data() + str.size());

Це буде краще, ніж усі відповіді, що використовують глобальну функцію toupper, і, мабуть, те, що підсилюється :: to_upper робиться під ним.

Це тому, що :: toupper повинен шукати локаль - тому що він міг бути змінений іншим потоком - для кожного виклику, тоді як тут лише виклик до locale () має таке покарання. А пошук локалу зазвичай передбачає зняття блокування.

Це також працює з C ++ 98 після того, як ви заміните авто, використовуйте нову нестандартну str.data () та додайте пробіл для розбиття шаблону закриття (">>" to ">>") таким чином:

std::use_facet<std::ctype<char> > & f = 
    std::use_facet<std::ctype<char> >(std::locale());
f.toupper(const_cast<char *>(str.data()), str.data() + str.size());

7
typedef std::string::value_type char_t;

char_t up_char( char_t ch )
{
    return std::use_facet< std::ctype< char_t > >( std::locale() ).toupper( ch );
}

std::string toupper( const std::string &src )
{
    std::string result;
    std::transform( src.begin(), src.end(), std::back_inserter( result ), up_char );
    return result;
}

const std::string src  = "test test TEST";

std::cout << toupper( src );

не рекомендував би back_inserter, оскільки ви вже знаєте довжину; використовувати std :: string result (src.size ()); std :: перетворення (src.begin (), src.end (), result.begin (), up_char);
Віктор Шер

Хоча я впевнений, що ти це знаєш.
Віктор Шер

@Viktor Sehr, @bayda: Я знаю, що це 2 роки, але чому б не отримати найкраще з обох світів. Використовуйте reserveта back_inserter(роблячи так, що рядок копіюється лише один раз). inline std::string to_lower(const std::string &s) { std::string result; result.reserve(s.size()); std::transform(s.begin(), s.end(), std::back_inserter( result ), static_cast<int(*)(int)>(std::tolower)); return result; }
Еван Теран

4
std::string value;
for (std::string::iterator p = value.begin(); value.end() != p; ++p)
    *p = toupper(*p);

Цей код викликає невизначене поведінку, коли toupperвикликається з негативними числами.
Roland Illig

2

спробуйте toupper()функцію ( #include <ctype.h>). він приймає символи як аргументи, рядки складаються з символів, тож вам доведеться перебирати кожен окремий символ, який, коли вони складаються разом, містять рядок


Ця пропозиція викликає невизначене поведінку, коли toupperвикликається з негативними числами. Ви мусили згадати необхідний склад unsigned char.
Roland Illig

2

Ось останній код із C ++ 11

std::string cmd = "Hello World";
for_each(cmd.begin(), cmd.end(), [](char& in){ in = ::toupper(in); });

Цей код викликає невизначене поведінку, коли toupperвикликається з негативними числами.
Roland Illig

1

Використання Boost.Text, який буде працювати для тексту Unicode

boost::text::text t = "Hello World";
boost::text::text uppered;
boost::text::to_title(t, std::inserter(uppered, uppered.end()));
std::string newstr = uppered.extract();

1

Відповідь на @dirkgently дуже надихає, але я хочу підкреслити , що в зв'язку із занепокоєнням , як буде показано нижче,

Як і всі інші функції з, поведінка std :: toupper не визначена, якщо значення аргументу не є ні представним як неподписаний знак, ні рівне EOF. Щоб безпечно використовувати ці функції з простими символами (або з підписаними символами), аргумент спочатку слід перетворити на неподписаний char
Посилання : std :: toupper

правильне використання std::toupperмає бути:

#include <algorithm>
#include <cctype>
#include <iostream>
#include <iterator>
#include <string>

void ToUpper(std::string& input)
{
    std::for_each(std::begin(input), std::end(input), [](char& c) {
        c = static_cast<char>(std::toupper(static_cast<unsigned char>(c)));
    });
}

int main()
{
    std::string s{ "Hello world!" };
    std::cout << s << std::endl;
    ::ToUpper(s);
    std::cout << s << std::endl;

    return 0;
}

Вихід:

Hello world!
HELLO WORLD!

0

не впевнений, що є вбудована функція. Спробуйте це:

Включіть або бібліотеки ctype.h АБО cctype, а також stdlib.h як частина директив препроцесора.

string StringToUpper(string strToConvert)
{//change each element of the string to upper case
   for(unsigned int i=0;i<strToConvert.length();i++)
   {
      strToConvert[i] = toupper(strToConvert[i]);
   }
   return strToConvert;//return the converted string
}

string StringToLower(string strToConvert)
{//change each element of the string to lower case
   for(unsigned int i=0;i<strToConvert.length();i++)
   {
      strToConvert[i] = tolower(strToConvert[i]);
   }
   return strToConvert;//return the converted string
}

.length () не типу 'unsigned int'
malat

Цей код викликає невизначене поведінку, коли toupperвикликається з негативними числами.
Roland Illig

0

Моє рішення (очищення 6-го біта для альфа):

#include <ctype.h>

inline void toupper(char* str)
{
    while (str[i]) {
        if (islower(str[i]))
            str[i] &= ~32; // Clear bit 6 as it is what differs (32) between Upper and Lowercases
        i++;
    }
}

Цей код викликає невизначене поведінку, коли toupperвикликається з негативними числами.
Роланд Ілліг

Ні ... Будь ласка, переконайтеся, що ви праві, перш ніж відмовитись. Іслоуер працював би лише на негативних значеннях ...
Антонін ГАВРЕЛ

-1

ВСІ з цих рішень на цій сторінці важче, ніж потрібно.

Зробити це

RegName = "SomE StRing That you wAnt ConvErTed";
NameLength = RegName.Size();
for (int forLoop = 0; forLoop < NameLength; ++forLoop)
{
     RegName[forLoop] = tolower(RegName[forLoop]);
}

RegNameтвоє string. Отримайте розмір рядка не використовуйте string.size()як власний тестер, дуже брудний і може спричинити проблеми. тоді. найосновніша forпетля.

пам'ятайте, що розмір рядка також повертає роздільник, тому використовуйте <і не <= у своєму циклі тесту.

Вихід буде: деяка рядок, яку ви хочете перетворити


4
Я не бачу, як це простіше, ніж рішення boost :: toupper. Чи можете ви докладно?
tr9sh

2
Уже існує маса простих tolowerциклів, і більшість з них використовують стандартні імена змінних циклів, як i, не дивно forLoop.
Пітер Кордес

-1

Без використання бібліотек:

std::string YourClass::Uppercase(const std::string & Text)
{
    std::string UppperCaseString;
    UppperCaseString.reserve(Text.size());
    for (std::string::const_iterator it=Text.begin(); it<Text.end(); ++it)
    {
        UppperCaseString.push_back(((0x60 < *it) && (*it < 0x7B)) ? (*it - static_cast<char>(0x20)) : *it);
    }
    return UppperCaseString;
}

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

-1

Якщо вас турбують лише 8-бітові символи (на які припускають усі інші відповіді, крім Мілана Бабушкова), ви можете отримати найшвидшу швидкість, створивши таблицю пошуку під час компіляції за допомогою метапрограмування. На ideone.com це працює в 7 разів швидше, ніж функція бібліотеки і в 3 рази швидше, ніж рукописна версія ( http://ideone.com/sb1Rup ). Це також налаштовується через риси без сповільнення.

template<int ...Is>
struct IntVector{
using Type = IntVector<Is...>;
};

template<typename T_Vector, int I_New>
struct PushFront;
template<int ...Is, int I_New>
struct PushFront<IntVector<Is...>,I_New> : IntVector<I_New,Is...>{};

template<int I_Size, typename T_Vector = IntVector<>>
struct Iota : Iota< I_Size-1, typename PushFront<T_Vector,I_Size-1>::Type> {};
template<typename T_Vector>
struct Iota<0,T_Vector> : T_Vector{};

template<char C_In>
struct ToUpperTraits {
    enum { value = (C_In >= 'a' && C_In <='z') ? C_In - ('a'-'A'):C_In };
};

template<typename T>
struct TableToUpper;
template<int ...Is>
struct TableToUpper<IntVector<Is...>>{
    static char at(const char in){
        static const char table[] = {ToUpperTraits<Is>::value...};
        return table[in];
    }
};

int tableToUpper(const char c){
    using Table = TableToUpper<typename Iota<256>::Type>;
    return Table::at(c);
}

із випадком використання:

std::transform(in.begin(),in.end(),out.begin(),tableToUpper);

Поглиблене (багато сторінок) визначення того, як це працює, дозволяє мені безсоромно підключити свій блог: http://metaporky.blogspot.de/2014/07/part-4-generating-look-up-tables-at.html


-1
template<size_t size>
char* toupper(char (&dst)[size], const char* src) {
    // generate mapping table once
    static char maptable[256];
    static bool mapped;
    if (!mapped) {
        for (char c = 0; c < 256; c++) {
            if (c >= 'a' && c <= 'z')
                maptable[c] = c & 0xdf;
            else
                maptable[c] = c;
        }
        mapped = true;
    }

    // use mapping table to quickly transform text
    for (int i = 0; *src && i < size; i++) {
        dst[i] = maptable[*(src++)];
    }
    return dst;
}

-1

Ця функція c ++ завжди повертає верхній регістр ...

#include <locale> 
#include <string>
using namespace std; 
string toUpper (string str){
    locale loc; 
    string n; 
    for (string::size_type i=0; i<str.length(); ++i)
        n += toupper(str[i], loc);
    return n;
}

-3

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

void to_upper(const std::string str) {
    std::string::iterator it;
    int i;
    for ( i=0;i<str.size();++i ) {
        ((char *)(void *)str.data())[i]=toupper(((char *)str.data())[i]);
    }
}

I know you're not supposed to modify that data area- яку область даних ви не повинні змінювати?
користувач93353

3
Це пізно, але що на землі? Цю божевільну лінію можна замінити str[i] = toupper(str[i]);ідеально тонкою (ну, не зовсім тонкою, але вона фіксує більшість речей неправильно).
chris
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.