Bagaimana seseorang dapat mengkonversi string ke huruf besar. Contoh yang saya temukan dari googling hanya harus berurusan dengan karakter.
268
Meningkatkan algoritma string:
#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
kemungkinan besar mengasumsikan ASCII.std::string newstr(boost::to_upper_copy<std::string>("Hello World"));
sumber
toupper()
bisa diimplementasikan sebagai makro. Ini dapat menyebabkan masalah.toupper
. Ada ide?Solusi singkat menggunakan C ++ 11 dan toupper ().
sumber
c
menjadiconst char
jenis (dariauto
)? Jika demikian, Anda tidak dapat menetapkannya (karenaconst
sebagian) untuk apa yang dikembalikan olehtoupper(c)
.c
perlu di-cast agarunsigned char
ini bisa dikorupsi.Catatan: Beberapa masalah dengan solusi teratas:
Yang berarti bahwa
cctype
anggota mungkin makro tidak cocok untuk konsumsi langsung dalam algoritma standar.Masalah lain dengan contoh yang sama adalah tidak memberikan argumen atau memverifikasi bahwa ini tidak negatif; ini sangat berbahaya untuk sistem tempat dataran
char
ditandatangani. (Alasannya adalah: jika ini diterapkan sebagai makro mungkin akan menggunakan tabel pencarian dan indeks argumen Anda ke dalam tabel itu. Indeks negatif akan memberi Anda UB.)sumber
Masalah ini dapat diubah dengan SIMD untuk rangkaian karakter ASCII.
Perbandingan speedup:
Pengujian awal dengan x86-64 gcc 5.2
-O3 -march=native
pada Core2Duo (Merom). String yang sama dengan 120 karakter (campuran huruf kecil dan ASCII non-huruf kecil), dikonversikan dalam loop 40M kali (tanpa inline file-silang, sehingga kompiler tidak dapat mengoptimalkan atau menghilangkan semua itu dari loop). Sumber dan buffer yang sama, jadi tidak ada overhead malloc atau efek memori / cache: data panas di L1 cache sepanjang waktu, dan kami murni terikat CPU.boost::to_upper_copy<char*, std::string>()
: 198.0s . Ya, Boost 1.58 di Ubuntu 15.10 benar-benar lambat. Saya memprofilkan dan melangkah satu langkah dalam debugger, dan itu benar- benar buruk: ada dynamic_cast dari variabel lokal yang terjadi per karakter !!! (dynamic_cast mengambil beberapa panggilan ke strcmp). Ini terjadi denganLANG=C
dan denganLANG=en_CA.UTF-8
.Saya tidak menguji menggunakan RangeT selain std :: string. Mungkin bentuk lain
to_upper_copy
mengoptimalkan lebih baik, tapi saya pikir itu akan selalunew
/malloc
ruang untuk salinan, jadi lebih sulit untuk menguji. Mungkin sesuatu yang saya lakukan berbeda dari kasus penggunaan normal, dan mungkin biasanya berhenti g ++ dapat mengangkat hal-hal pengaturan lokal keluar dari loop per-karakter. Perulangan saya daristd::string
dan menulis hinggachar dstbuf[4096]
masuk akal untuk pengujian.loop calling glibc
toupper
: 6.67s (tidak memeriksaint
hasil untuk multi-byte UTF-8, meskipun. Ini penting untuk Turki.)cmov
, dengan tabel panas di L1.Lihat juga pertanyaan ini tentang
toupper()
menjadi lambat di Windows saat lokal diatur .Saya terkejut bahwa Meningkatkan adalah urutan besarnya lebih lambat daripada opsi lain. Saya memeriksa ulang bahwa saya telah
-O3
mengaktifkannya, dan bahkan satu langkah untuk melihat apa yang dilakukannya. Kecepatannya hampir persis sama dengan dentang ++ 3.8. Ini memiliki overhead besar di dalam per-karakter loop. Theperf record
/report
hasil (untukcycles
acara perf) adalah:Autovectorization
Gcc dan dentang hanya akan auto-vectorize loop ketika jumlah iterasi diketahui di depan loop. (mis. loop pencarian seperti implementasi plain-C
strlen
tidak akan melakukan otomatisasi.)Jadi, untuk string yang cukup kecil untuk disimpan dalam cache, kita mendapatkan speedup yang signifikan untuk string ~ 128 chars lama dari melakukan
strlen
pertama. Ini tidak diperlukan untuk string dengan panjang eksplisit (seperti C ++std::string
).Setiap libc yang layak akan memiliki efisiensi
strlen
yang jauh lebih cepat daripada perulangan byte pada suatu waktu, jadi memisahkan vektor dan loop toupper vektor lebih cepat.Baseline: loop yang memeriksa untuk mengakhiri 0 dengan cepat.
Waktu untuk iterasi 40M, pada Core2 (Merom) 2.4GHz. gcc 5.2
-O3 -march=native
. (Ubuntu 15.10).dst != src
(jadi kami membuat salinan), tetapi mereka tidak tumpang tindih (dan tidak di dekatnya). Keduanya selaras.Beberapa hasil sedikit berbeda dengan dentang.
Loop microbenchmark yang memanggil fungsi berada dalam file terpisah. Kalau tidak, inline dan
strlen()
diangkat keluar dari loop, dan berjalan secara dramatis lebih cepat, esp. selama 16 string char (0,187s).Ini memiliki keuntungan besar bahwa gcc dapat melakukan auto-vektorisasi untuk arsitektur apa pun, tetapi kelemahan utama adalah bahwa gcc lebih lambat untuk kasus string kecil yang biasanya umum.
Jadi ada speedup besar, tapi kompilasi otomatis vektor tidak membuat kode yang bagus, khususnya. untuk pembersihan hingga 15 karakter terakhir.
Vektorisasi manual dengan intrinsik SSE:
Berdasarkan fungsi case-flip saya yang membalik kasus dari setiap karakter alfabet. Ini mengambil keuntungan dari "trik perbandingan yang tidak ditandatangani", di mana Anda dapat melakukan
low < a && a <= high
dengan satu perbandingan yang tidak ditandatangani dengan menggeser rentang, sehingga nilai apa pun yang kurang darilow
nilai yang lebih besar daripada nilai yang lebih besarhigh
. (Ini berfungsi jikalow
danhigh
tidak terlalu jauh.)SSE hanya memiliki perbandingan-ditandatangani yang lebih besar, tetapi kita masih dapat menggunakan trik "perbandingan tidak ditandatangani" dengan menggeser rentang ke bagian bawah rentang yang ditandatangani: Kurangi 'a' + 128, sehingga karakter alfabet berkisar dari -128 hingga -128 +25 (-128 + 'z' - 'a')
Perhatikan bahwa menambahkan 128 dan mengurangi 128 adalah hal yang sama untuk bilangan bulat 8bit. Tidak ada tempat untuk membawa, jadi itu hanya xor (carryless add), membalik bit tinggi.
Dengan fungsi ini yang bekerja untuk satu vektor, kita dapat memanggilnya dalam satu lingkaran untuk memproses seluruh string. Karena kami sudah menargetkan SSE2, kami dapat melakukan pemeriksaan end-of-string secara vektor pada saat yang sama.
Kita juga dapat melakukan jauh lebih baik untuk "pembersihan" dari byte terakhir hingga 15 yang tersisa setelah melakukan vektor 16B: casing atas idempoten, jadi memproses ulang beberapa byte input baik-baik saja. Kami melakukan beban yang tidak selaras dari 16B terakhir dari sumber, dan menyimpannya ke buffer dest yang tumpang tindih dengan toko 16B terakhir dari loop.
Satu-satunya saat ini tidak berfungsi adalah ketika seluruh string di bawah 16B: Bahkan ketika
dst=src
, baca-modifikasi-tulis non-atom bukan hal yang sama dengan tidak menyentuh beberapa byte sama sekali, dan dapat memecahkan kode multithreaded.Kami memiliki loop skalar untuk itu, dan juga untuk mendapatkan
src
selaras. Karena kita tidak tahu di mana 0 terminating akan berada, beban yang tidak selarassrc
mungkin akan masuk ke halaman berikutnya dan segfault. Jika kita membutuhkan byte dalam chunk 16B sejajar, selalu aman untuk memuat chunk 16B sejajar.Sumber lengkap: dalam inti github .
Waktu untuk iterasi 40M, pada Core2 (Merom) 2.4GHz. gcc 5.2
-O3 -march=native
. (Ubuntu 15.10).dst != src
(jadi kami membuat salinan), tetapi mereka tidak tumpang tindih (dan tidak di dekatnya). Keduanya selaras.(Sebenarnya diatur
_mm_store
dalam loop, bukan_mm_storeu
, karena storeu lebih lambat pada Merom bahkan ketika alamat disejajarkan. Tidak masalah pada Nehalem dan nanti. Saya juga membiarkan kode apa adanya untuk saat ini, alih-alih memperbaiki kegagalan untuk menyalin penghentian 0 dalam beberapa kasus, karena saya tidak ingin mengatur ulang waktu semuanya.)Jadi untuk string pendek lebih panjang dari 16B, ini secara dramatis lebih cepat daripada auto-vektor. Panjang satu-kurang-dari-vektor-lebar tidak menimbulkan masalah. Mereka mungkin menjadi masalah ketika beroperasi di tempat, karena kedai toko-forwarding. (Tetapi perhatikan bahwa masih baik untuk memproses output kita sendiri, daripada input asli, karena toupper idempotent).
Ada banyak ruang untuk menyetel ini untuk berbagai kasus penggunaan, tergantung pada apa yang diinginkan kode di sekitarnya, dan mikroarsitektur target. Mendapatkan kompiler untuk mengeluarkan kode yang bagus untuk bagian pembersihan itu sulit. Menggunakan
ffs(3)
(yang mengkompilasi bsf atau tzcnt pada x86) tampaknya baik, tetapi jelas bahwa bit perlu dipikirkan kembali karena saya melihat bug setelah menulis sebagian besar jawaban ini (lihat komentar FIXME).Vektor speedup untuk string yang lebih kecil dapat diperoleh dengan
movq
ataumovd
memuat / menyimpan. Sesuaikan seperlunya untuk use case Anda.UTF-8:
Kita dapat mendeteksi kapan vektor kita memiliki byte dengan set bit tinggi, dan dalam kasus itu kembali ke loop skalar utf-8-aware untuk vektor itu. The
dst
point dapat memajukan dengan jumlah yang berbeda darisrc
pointer, tetapi setelah kita kembali ke selarassrc
pointer, kami akan tetap lakukan toko vektor unaligned kedst
.Untuk teks yang UTF-8, tetapi sebagian besar terdiri dari bagian ASCII dari UTF-8, ini bisa bagus: kinerja tinggi dalam kasus umum dengan perilaku yang benar dalam semua kasus. Ketika ada banyak non-ASCII, itu mungkin akan lebih buruk daripada tetap di loop sadar UTF-8 scalar sepanjang waktu.
Membuat bahasa Inggris lebih cepat dengan mengorbankan bahasa lain bukanlah keputusan yang tahan masa depan jika kelemahannya signifikan.
Sadar lokal:
Di lokal Turki (
tr_TR
), hasil yang benar daritoupper('i')
adalah'İ'
(U0130), bukan'I'
(ASCII polos). Lihat komentar Martin Bonner tentang pertanyaan tentangtolower()
memperlambat pada Windows.Kita juga dapat memeriksa daftar pengecualian dan mundur ke skalar di sana, seperti untuk karakter input UTF8 multi-byte.
Dengan kerumitan sebanyak ini, SSE4.2
PCMPISTRM
atau sesuatu mungkin dapat melakukan banyak pemeriksaan kami dalam sekali jalan.sumber
Apakah Anda memiliki karakter ASCII atau Internasional di string?
Jika ini kasus terakhir, "huruf besar" tidak semudah itu, dan itu tergantung pada alfabet yang digunakan. Ada huruf bikameral dan unikameral. Hanya huruf bikameral yang memiliki karakter berbeda untuk huruf besar dan kecil. Juga, ada karakter gabungan, seperti huruf latin 'DZ' (\ u01F1 'DZ') yang menggunakan case title . Ini berarti bahwa hanya karakter pertama (D) yang diubah.
Saya sarankan Anda melihat ke ICU , dan perbedaan antara Pemetaan Kasus Sederhana dan Lengkap. Ini mungkin membantu:
http://userguide.icu-project.org/transforms/casemappings
sumber
Atau,
sumber
**
setelah parameter pada solusi pertama?**
ini adalah kesalahan ketik yang tersisa dari mencoba menggunakan font tebal dalam sintaksis kode.toupper
dipanggil dengan angka negatif.Berikut ini berfungsi untuk saya.
sumber
toupper
dipanggil dengan angka negatif.Gunakan lambda.
sumber
Yang lebih cepat jika Anda hanya menggunakan karakter ASCII :
Harap perhatikan bahwa kode ini berjalan lebih cepat tetapi hanya berfungsi pada ASCII dan bukan merupakan solusi "abstrak".
Jika Anda membutuhkan solusi UNICODE atau solusi yang lebih konvensional dan abstrak, buka jawaban lain dan bekerja dengan metode string C ++.
sumber
C++
, tetapi Anda menulisC
jawaban di sini. (Saya bukan salah satu dari para downvoter.)'
?Selama Anda baik-baik saja dengan ASCII saja dan Anda dapat memberikan pointer yang valid ke memori RW, ada one-liner sederhana dan sangat efektif di C:
Ini sangat baik untuk string sederhana seperti pengidentifikasi ASCII yang ingin Anda normalkan ke dalam case-karakter yang sama. Anda kemudian dapat menggunakan buffer untuk membangun instance string std:.
sumber
sumber
for (size_t i = 0 ...
. Juga tidak ada alasan bagus untuk membuatnya jadi sulit dibaca. Ini juga menyalin string pertama dan kemudian mengulanginya. @ Luke menjawab lebih baik dalam beberapa hal, kecuali untuk tidak memanfaatkan'a'
konstanta karakter.Ini akan berkinerja lebih baik daripada semua jawaban yang menggunakan fungsi toupper global, dan mungkin apa yang meningkatkan :: to_upper lakukan di bawahnya.
Ini karena :: toupper harus mencari lokal - karena mungkin telah diubah oleh utas berbeda - untuk setiap doa, sedangkan di sini hanya panggilan ke lokal () yang memiliki penalti ini. Dan mencari lokasi biasanya melibatkan mengambil kunci.
Ini juga berfungsi dengan C ++ 98 setelah Anda mengganti otomatis, gunakan str.data non-const baru (), dan tambahkan spasi untuk memecahkan penutupan templat (">>" ke ">>") seperti ini:
sumber
sumber
reserve
danback_inserter
(membuatnya jadi hanya disalin sekali).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; }
sumber
toupper
dipanggil dengan angka negatif.coba
toupper()
fungsi (#include <ctype.h>
). itu menerima karakter sebagai argumen, string terdiri dari karakter, jadi Anda harus beralih setiap karakter individu yang ketika disatukan terdiri dari stringsumber
toupper
dipanggil dengan angka negatif. Anda harus menyebutkan pemeran yang diperlukanunsigned char
.Ini adalah kode terbaru dengan C ++ 11
sumber
toupper
dipanggil dengan angka negatif.Menggunakan Boost.Text, yang akan berfungsi untuk teks Unicode
sumber
The jawaban dari @dirkgently sangat menginspirasi, tapi saya ingin menekankan bahwa karena kekhawatiran seperti yang ditunjukkan di bawah ini,
penggunaan yang benar
std::toupper
harus:Keluaran:
sumber
tidak yakin ada fungsi bawaan. Coba ini:
Sertakan pustaka ctype.h ATAU cctype, serta stdlib.h sebagai bagian dari arahan preprocessor.
sumber
toupper
dipanggil dengan angka negatif.Solusi saya (menghapus bit ke-6 untuk alpha):
sumber
toupper
dipanggil dengan angka negatif.SEMUA solusi di halaman ini lebih sulit daripada yang seharusnya.
Melakukan hal ini
RegName
adalah Andastring
. Dapatkan ukuran string Anda tidak digunakanstring.size()
sebagai tester Anda yang sebenarnya, sangat berantakan dan dapat menyebabkan masalah. kemudian.for
loop paling dasar .ingat ukuran string mengembalikan pembatas juga jadi gunakan <dan tidak <= dalam tes loop Anda.
output akan menjadi: beberapa string yang ingin Anda konversi
sumber
tolower
loop sederhana , dan kebanyakan dari mereka menggunakan nama variabel loop standar sepertii
, bukan yang anehforLoop
.Tanpa menggunakan perpustakaan apa pun:
sumber
Jika Anda hanya peduli dengan 8 bit karakter (yang semua jawaban lain kecuali Milan Babuškov asumsikan juga), Anda bisa mendapatkan kecepatan tercepat dengan membuat tabel pencarian pada waktu kompilasi menggunakan metaprogramming. Di ideone.com ini berjalan 7x lebih cepat dari fungsi perpustakaan dan 3x lebih cepat dari versi tulisan tangan ( http://ideone.com/sb1Rup ). Ini juga dapat dikustomisasi melalui sifat-sifat tanpa memperlambat.
dengan use case:
Untuk penjelasan mendalam (banyak halaman) tentang cara kerjanya, izinkan saya untuk menyambungkan blog saya tanpa malu-malu: http://metaporky.blogspot.de/2014/07/part-4-generating-look-up-tables-at.html
sumber
sumber
Fungsi c ++ ini selalu mengembalikan string huruf besar ...
sumber
Saya menggunakan solusi ini. Saya tahu Anda tidak seharusnya memodifikasi area data itu .... tapi saya pikir itu sebagian besar untuk bug buffer overrun dan karakter nol .... casing bagian atas tidak sama.
sumber
I know you're not supposed to modify that data area
- area data apa yang tidak seharusnya Anda modifikasi?str[i] = toupper(str[i]);
baik-baik saja (well, tidak sepenuhnya baik, tetapi memperbaiki sebagian besar hal yang salah).