Как можно преобразовать строку в верхний регистр. Примеры, которые я нашел из поиска в Google, имеют дело только с символами.
268
Алгоритмы ускорения строк:
#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");
::toupper
, скорее всего, предполагается ASCII.std::string newstr(boost::to_upper_copy<std::string>("Hello World"));
источник
toupper()
может быть реализовано в виде макроса. Это может вызвать проблему.toupper
. Любые идеи?Краткое решение с использованием C ++ 11 и toupper ().
источник
c
будетconst char
типа (сauto
)? Если это так, вы не можете присвоить его (из-заconst
части) тому, что возвращаетсяtoupper(c)
.c
необходимо использовать егоunsigned char
для исправления.Примечание: пара проблем с лучшим решением:
Это означает, что
cctype
члены могут быть макросами, не подходящими для прямого потребления в стандартных алгоритмах.Другая проблема с тем же примером заключается в том, что он не приводит аргумент и не проверяет, что он неотрицательный; это особенно опасно для систем, в которых
char
подписано plain . (Причина в том, что если это будет реализовано в виде макроса, он, вероятно, будет использовать таблицу поиска и ваши индексы аргументов в этой таблице. Отрицательный индекс даст вам UB.)источник
Эта проблема векторизована с 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, хотя. Это важно для турецкого языка.)cmov
, в любом случае с горячей таблицей в L1.См. Также этот вопрос о
toupper()
медленной работе в Windows при заданной локали .Я был шокирован тем, что Boost на порядок медленнее, чем другие варианты. Я дважды проверил, что я
-O3
включил, и даже пошагово осмотрел, чтобы увидеть, что он делает. Это почти точно такая же скорость с Clang ++ 3.8. Это имеет огромные накладные расходы внутри цикла за символ. Результатperf record
/report
(дляcycles
события perf):Автовекторизация
Gcc и clang будут автоматически векторизовывать циклы только тогда, когда счетчик итераций известен перед циклом. (то есть поисковые циклы, такие как реализация на обычном C
strlen
, не будут автоматически векторизованы.)Таким образом, для строк, достаточно маленьких, чтобы поместиться в кэш, мы получаем значительное ускорение для строк длиной ~ 128 символов при
strlen
первом выполнении. Это не будет необходимо для строк с явной длиной (например, C ++std::string
).Любой приличный libc будет иметь эффективный
strlen
которая намного быстрее, чем зацикливание байта за раз, поэтому отдельные векторизованные циклы strlen и toupper работают быстрее.Базовая линия: цикл, который проверяет завершающий 0 на лету.
Время для 40M итераций на Core2 (Merom) 2,4 ГГц. GCC 5.2
-O3 -march=native
. (Ubuntu 15.10).dst != src
(поэтому мы делаем копию), но они не пересекаются (и не находятся рядом). Оба выровнены.Некоторые результаты немного отличаются от 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 (безвоздушное добавление), переключающее старшие биты.
Учитывая, что эта функция работает для одного вектора, мы можем вызвать ее в цикле для обработки всей строки. Поскольку мы уже нацелены на SSE2, мы можем одновременно выполнять векторизованную проверку конца строки.
Мы также можем сделать намного лучше для «очистки» последних до 15 байтов, оставшихся после выполнения векторов 16B: верхний регистр идемпотентен, поэтому повторная обработка некоторых входных байтов в порядке. Мы делаем невыровненную загрузку последних 16B источника и сохраняем ее в буфере dest, перекрывающем последнее 16B хранилище из цикла.
Единственный раз, когда это не работает, это когда вся строка меньше 16B: даже когда
dst=src
неатомарное чтение-изменение-запись это не то же самое, что совсем не трогать некоторые байты, и может сломать многопоточный код.У нас есть скалярный цикл для этого, а также для
src
выравнивания. Так как мы не знаем, где будет завершающий 0, несогласованная загрузка изsrc
может перейти на следующую страницу и привести к ошибке. Если нам нужны какие-либо байты в выровненном фрагменте 16B, всегда безопасно загрузить весь выровненный фрагмент 16B.Полный источник: в GitHub Gist .
Время для 40M итераций на Core2 (Merom) 2,4 ГГц. GCC 5.2
-O3 -march=native
. (Ubuntu 15.10).dst != src
(поэтому мы делаем копию), но они не пересекаются (и не находятся рядом). Оба выровнены.(На самом деле приурочен
_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
или что-то может сделать много наших проверок за один раз.источник
У вас есть ASCII или международные символы в строках?
Если это последний случай, «верхний регистр» не так прост, и это зависит от используемого алфавита. Существуют двухпалатные и однопалатные алфавиты. Только двухпалатные алфавиты имеют разные символы в верхнем и нижнем регистре. Кроме того, есть составные символы, такие как латинская заглавная буква 'DZ' (\ u01F1 'DZ'), которые используют так называемый регистр заголовка . Это означает, что только первый символ (D) будет изменен.
Я предлагаю вам взглянуть на отделение интенсивной терапии и разницу между простым и полным отображением случаев. Это может помочь:
http://userguide.icu-project.org/transforms/casemappings
источник
Или,
источник
**
после параметров первого решения?**
это опечатка, оставшаяся от попытки использовать жирный шрифт в синтаксисе кода.toupper
вызывается с отрицательными числами.Следующее работает для меня.
источник
toupper
вызывается с отрицательными числами.Используйте лямбду.
источник
Более быстрый, если вы используете только символы ASCII :
Обратите внимание, что этот код работает быстрее, но работает только в ASCII и не является «абстрактным» решением.
Если вам нужны решения UNICODE или более традиционные и абстрактные решения, найдите другие ответы и поработайте с методами строк C ++.
источник
C++
, но вы написалиC
ответ здесь. (Я не один из downvoters.)'
?Пока вы в порядке с ASCII-only и можете предоставить действительный указатель на память RW, в C есть простая и очень эффективная однострочная строка:
Это особенно хорошо для простых строк, таких как идентификаторы ASCII, которые вы хотите нормализовать в том же регистре символов. Затем вы можете использовать буфер для создания экземпляра std: string.
источник
источник
for (size_t i = 0 ...
. Там также нет веских причин, чтобы сделать его так трудно читать. Это также сначала копирует строку, а затем зацикливает ее. @ Ответ Люка лучше в некоторых отношениях, за исключением того, что он не использует'a'
константы символов.Это будет работать лучше, чем все ответы, которые используют глобальную функцию toupper, и, вероятно, это то, что делает boost :: to_upper ниже.
Это потому, что :: toupper должен искать локаль - потому что она могла быть изменена другим потоком - для каждого вызова, тогда как здесь только штраф к вызову locale (). И поиск локали обычно включает взятие блокировки.
Это также работает с C ++ 98 после замены auto, использования нового неконстантного str.data () и добавления пробела, чтобы прервать закрытие шаблона (">>" до ">>") следующим образом:
источник
источник
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; }
источник
toupper
вызывается с отрицательными числами.попробуйте
toupper()
функцию (#include <ctype.h>
). он принимает символы в качестве аргументов, строки состоят из символов, поэтому вам придется перебирать каждый отдельный символ, который при объединении составляет строкуисточник
toupper
вызывается с отрицательными числами. Вы должны были упомянуть необходимый актерский составunsigned char
.Вот последний код с C ++ 11
источник
toupper
вызывается с отрицательными числами.Использование Boost.Text, который будет работать для текста Unicode
источник
Ответ на @dirkgently очень вдохновляет, но я хочу подчеркнуть , что в связи с озабоченностью , как будет показано ниже,
правильное использование
std::toupper
должно быть:Вывод:
источник
не уверен, что есть встроенная функция. Попробуй это:
Включите библиотеки ctype.h ИЛИ cctype, а также stdlib.h как часть директив препроцессора.
источник
toupper
вызывается с отрицательными числами.Мое решение (очистка 6-го бита для альфы):
источник
toupper
вызывается с отрицательными числами.Все эти решения на этой странице сложнее, чем они должны быть.
Сделай это
RegName
это вашеstring
. Получите ваш размер строки не используйте вstring.size()
качестве фактического тестера, очень грязный и может вызвать проблемы. затем. самый простойfor
цикл.помните, что размер строки тоже возвращает разделитель, поэтому используйте <, а не <= в тесте цикла.
вывод будет: некоторая строка, которую вы хотите преобразовать
источник
tolower
циклов, и большинство из них используют стандартные имена переменных цикла, напримерi
, не странныеforLoop
.Без использования каких-либо библиотек:
источник
Если вас интересуют только 8-битные символы (а также все остальные ответы, кроме Милана Бабушкова), вы можете добиться максимальной скорости, создав таблицу соответствия во время компиляции с использованием метапрограммирования. На ideone.com это работает в 7 раз быстрее, чем функция библиотеки, и в 3 раза быстрее, чем рукописная версия ( http://ideone.com/sb1Rup ). Это также настраивается через черты без замедления.
с вариантом использования:
Для более подробного (многостраничного) описания того, как это работает, позвольте мне бесстыдно подключить мой блог: http://metaporky.blogspot.de/2014/07/part-4-generating-look-up-tables-at.html.
источник
источник
Эта функция c ++ всегда возвращает строку верхнего регистра ...
источник
Я использую это решение. Я знаю, что вы не должны изменять эту область данных ... но я думаю, что это в основном из-за ошибок переполнения буфера и пустых символов .... вещи в верхнем регистре не одинаковы.
источник
I know you're not supposed to modify that data area
- какую область данных вы не должны изменять?str[i] = toupper(str[i]);
отлично (ну, не идеально , но исправляет большинство вещей неправильно).