Преобразовать строку в C ++ в верхний регистр

268

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

OrangeAlmondSoap
источник

Ответы:

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"));
maxschlepzig
58
это не должен быть принятый ответ, так как он требует повышения или заголовок должен быть изменен.
Андреа
44
да, я собираюсь установить boost только для to_upper ... отличная идея! </ sarcasm> :)
Тхан
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()может быть реализовано в виде макроса. Это может вызвать проблему.
Dirkgently
3
я думаю, что bind (:: toupper, construct <unsigned char> (_ 1)) с boost.lambda отлично подойдет.
Йоханнес Шауб -
11
Этот подход хорошо работает для ASCII, но не подходит для многобайтовых кодировок символов или для специальных правил оболочки, таких как немецкий 'ß'.
Ден04
9
Я изменил принятый ответ на ответ с использованием библиотек наддува, потому что он был быстрее (в моем неформальном тестировании), проще в использовании и не имеет проблем, связанных с этим решением. Все еще хорошее решение для случаев, когда повышение не может быть использовано.
OrangeAlmondSoap
2
Я не могу понять, почему компилятор отклоняет этот код без :: qualifier раньше toupper. Любые идеи?
саша.сочка
90

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

for (auto & c: str) c = toupper(c);
Танасис Папуцудакис
источник
Не cбудет const charтипа (с auto)? Если это так, вы не можете присвоить его (из-за constчасти) тому, что возвращается toupper(c).
PolGraphic
5
@PolGraphic: Range - основан на для использования методов begin () / end () контейнера для перебора его содержимого. std :: basic_string имеет и const, и изменяемый итератор (возвращаемый cbegin () и begin () соответственно, см. std :: basic_string :: begin ), поэтому для (:) используется один подходящий (cbegin (), если str объявлено const с auto =: = const char, begin () в противном случае с auto =: = char).
Танасис Папуцудакис,
5
Посмотрите на ответ Диркгентли ниже, cнеобходимо использовать его unsigned charдля исправления.
Крис
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подписано plain . (Причина в том, что если это будет реализовано в виде макроса, он, вероятно, будет использовать таблицу поиска и ваши индексы аргументов в этой таблице. Отрицательный индекс даст вам UB.)

dirkgently
источник
Обычные члены cctype являются макросами. Я помню, что читал, что они тоже должны быть функциями, хотя у меня нет копии стандарта C90 и я не знаю, было ли это прямо указано или нет.
Дэвид Торнли
1
они должны быть функциями в C ++ - даже если C позволяет им быть макросами. Я согласен с вашим вторым замечанием о кастинге. верхнее решение может передать отрицательные значения и вызвать UB с этим. по этой причине я не проголосовал (но и не проголосовал) :)
Йоханнес Шауб -
1
стандартная кавычка не должна отсутствовать: 7.4.2.2/1 (плохое освещение, это относится только к черновому варианту C99 TC2) и C ++ 17.4.1.2/6 в стандарте glory c ++ 98.
Йоханнес Шауб -
1
(обратите внимание на примечание к нему: «Это запрещает обычную практику предоставления маскирующего макроса .... бла-блапп ... единственный способ сделать это в C ++ - предоставить внешнюю встроенную функцию.") :)
Йоханнес Шауб -
1
... это достигается с помощью этой хитрости: stackoverflow.com/questions/650461/…
Йоханнес Шауб - litb
27

Эта проблема векторизована с SIMD для набора символов ASCII.


Ускорение сравнений:

Предварительное тестирование с x86-64 gcc 5.2 -O3 -march=nativeна Core2Duo (Merom). Одна и та же строка из 120 символов (смешанный строчный и нестрочный ASCII), преобразованная в цикле 40M раз (без встраивания между файлами, поэтому компилятор не может оптимизировать или вывести любую из нее из цикла). Одинаковые буферы source и dest, поэтому никаких накладных расходов malloc или эффектов памяти / кэша: данные все время находятся в кеше 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-only loop: 8.79s (моя базовая версия для результатов ниже.) Видимо, поиск в таблице быстрее, чемcmov , в любом случае с горячей таблицей в L1.
  • ASCII-только авто-векторизация: 2,51 с . (120 символов - это половина пути между наихудшим и лучшим случаями, см. Ниже)
  • ASCII-только вручную векторизация: 1,35 с

См. Также этот вопрос о toupper()медленной работе в Windows при заданной локали .


Я был шокирован тем, что Boost на порядок медленнее, чем другие варианты. Я дважды проверил, что я -O3включил, и даже пошагово осмотрел, чтобы увидеть, что он делает. Это почти точно такая же скорость с Clang ++ 3.8. Это имеет огромные накладные расходы внутри цикла за символ. Результат perf record/ report(для cyclesсобытия perf):

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

Таким образом, для строк, достаточно маленьких, чтобы поместиться в кэш, мы получаем значительное ускорение для строк длиной ~ 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,34 с
  • 16 символьная строка: базовая линия: 1,16 с. Autovec: 1,52 с
  • 127 символьная строка: базовый уровень: 8,91 с. autovec: 2.98s // не векторная очистка имеет 15 символов для обработки
  • 128 символьная строка: базовая линия: 9,00 с. Autovec: 2.06s
  • 129 символьная строка: базовая линия: 9,04. autovec: 2.07s // не векторная очистка имеет 1 символ для обработки

Некоторые результаты немного отличаются от Clang.

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

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


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

Ручная векторизация с использованием SSE:

Основан на моей функции case-flip, которая инвертирует регистр каждого буквенного символа. Он использует «трюк сравнения без знака», при котором вы можете сделать 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может перейти на следующую страницу и привести к ошибке. Если нам нужны какие-либо байты в выровненном фрагменте 16B, всегда безопасно загрузить весь выровненный фрагмент 16B.

Полный источник: в GitHub Gist .

// 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,34 с. руководство: 1.29 с
  • 16 символьная строка: базовая линия: 1,16 с. Autovec: 1,52 с. руководство: 0.335 с
  • 31 символьная строка: руководство: 0,479 с
  • 127 символьная строка: базовый уровень: 8,91 с. Autovec: 2,98 с. руководство: 0,925 с
  • 128 символьная строка: базовая линия: 9,00 с. Autovec: 2,06 с. руководство: 0,931 с
  • 129 символьная строка: базовая линия: 9,04. Autovec: 2,07 с. руководство: 1.02 с

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

Таким образом, для коротких строк длиннее 16В это значительно быстрее, чем векторизация. Длина на единицу меньше ширины вектора не представляет проблемы. Они могут быть проблемой при работе на месте из-за остановки магазина. (Но обратите внимание, что все еще нормально обрабатывать наш собственный вывод, а не исходный ввод, потому что toupper является идемпотентом).

Существует много возможностей для настройки этого для различных вариантов использования, в зависимости от того, что хочет окружающий код, и целевой микроархитектуры. Заставить компилятор выдавать хороший код для части очистки сложно. Использование 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 или международные символы в строках?

Если это последний случай, «верхний регистр» не так прост, и это зависит от используемого алфавита. Существуют двухпалатные и однопалатные алфавиты. Только двухпалатные алфавиты имеют разные символы в верхнем и нижнем регистре. Кроме того, есть составные символы, такие как латинская заглавная буква 'DZ' (\ u01F1 'DZ'), которые используют так называемый регистр заголовка . Это означает, что только первый символ (D) будет изменен.

Я предлагаю вам взглянуть на отделение интенсивной терапии и разницу между простым и полным отображением случаев. Это может помочь:

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

Милан Бабушков
источник
7
Или немецкий eszet (sp?), Вещь, которая похожа на греческую букву бета и означает «ss». Не существует единственного немецкого символа, который означает «SS», что является заглавным эквивалентом. Немецкое слово «улица» в верхнем регистре становится длиннее на один символ.
Дэвид Торнли
6
Другим частным случаем является греческая буква сигма (Σ), которая имеет две строчные версии, в зависимости от того, находится ли она в конце слова (ς) или нет (σ). Кроме того, существуют правила, специфичные для конкретного языка, например, в турецком языке есть отображение случаев I↔ı и İ↔i.
апреля
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;
}
user648545
источник
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 :: transform определен в <алгоритме>
edj
Да. это # ​​включение обязательно, #include <алгоритм>
Pabitra Dash
1
Этот код вызывает неопределенное поведение, когда toupperвызывается с отрицательными числами.
Роланд Иллиг
копия ответа от пользователя user648545 - -1
Петр Доброгост
@PiotrDobrogost Я понятия не имею об ответе пользователя 648545. Я не копировал это. Когда я сравниваю два метода, сигнатура метода отличается, хотя обе функции используют функцию преобразования библиотеки.
Пабитра Дэш
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);
Byron
источник
2
Байрон, не беспокойся о других комментариях. Вполне нормально отвечать на старые вопросы с новым (современным) решением, как вы.
Kyberias
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ответ здесь. (Я не один из downvoters.)
hkBattousai
6
Я написал ответ C и ответ C ++ здесь, поскольку C ++ написан так, чтобы быть полностью совместимым с источниками C, поэтому любое решение C также является правильным решением C ++
Luca C.
Но гораздо лучше дать ответ, относящийся к языку C ++.
Дмитрий Юрченко
Стандартный способ c ++ - использовать std :: transform с toupper. Это меньше кода и, безусловно, переносимый. Этот код опирается на «факт», что система будет использовать ascii в качестве механизма кодирования символов. Не уверен, что все системы основаны на этой кодировке, и поэтому не уверен, что это переносимо.
AlexTheo
1
Почему вы решили использовать коды ASCII вместо символов, заключенных в ' ?
HolyBlackCat
11

Пока вы в порядке с ASCII-only и можете предоставить действительный указатель на память RW, в C есть простая и очень эффективная однострочная строка:

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

Это особенно хорошо для простых строк, таких как идентификаторы ASCII, которые вы хотите нормализовать в том же регистре символов. Затем вы можете использовать буфер для создания экземпляра std: string.

K3A
источник
Отмечается, что этот ответ предназначен для строки ac, а не для 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, и, вероятно, это то, что делает boost :: to_upper ниже.

Это потому, что :: toupper должен искать локаль - потому что она могла быть изменена другим потоком - для каждого вызова, тогда как здесь только штраф к вызову locale (). И поиск локали обычно включает взятие блокировки.

Это также работает с C ++ 98 после замены auto, использования нового неконстантного str.data () и добавления пробела, чтобы прервать закрытие шаблона (">>" до ">>") следующим образом:

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 :: transform (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вызывается с отрицательными числами.
Роланд Иллиг
2

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

ZMF
источник
Это предложение вызывает неопределенное поведение, когда toupperвызывается с отрицательными числами. Вы должны были упомянуть необходимый актерский состав unsigned char.
Роланд Иллиг
2

Вот последний код с C ++ 11

std::string cmd = "Hello World";
for_each(cmd.begin(), cmd.end(), [](char& in){ in = ::toupper(in); });
user2787620
источник
Этот код вызывает неопределенное поведение, когда toupperвызывается с отрицательными числами.
Роланд Иллиг
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();
cor3ntin
источник
1

Ответ на @dirkgently очень вдохновляет, но я хочу подчеркнуть , что в связи с озабоченностью , как будет показано ниже,

Как и все другие функции из, поведение std :: toupper не определено, если значение аргумента не может быть представлено как unsigned char и равно EOF. Чтобы безопасно использовать эти функции с простыми символами (или знаковыми), сначала необходимо преобразовать аргумент в unsigned 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!
KaiserKatze
источник
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»
малат
Этот код вызывает неопределенное поведение, когда toupperвызывается с отрицательными числами.
Роланд Иллиг
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вызывается с отрицательными числами.
Роланд Иллиг
Нет ... Пожалуйста, проверьте, что вы правы перед голосованием. Islower будет работать только на неотрицательные значения ...
Антонин ГАВРЕЛЬ
-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;
}
hkBattousai
источник
Приведенный выше код работает только для 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.

odinthenerd
источник
-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;
}
Awais Jameel
источник
-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- какую область данных вы не должны изменять?
user93353
3
Это поздно, но что на земле? Эта сумасшедшая линия может быть заменена на str[i] = toupper(str[i]);отлично (ну, не идеально , но исправляет большинство вещей неправильно).
Крис