Saya ingin mengonversi a std::string
menjadi huruf kecil. Saya menyadari fungsi tolower()
, tetapi di masa lalu saya memiliki masalah dengan fungsi ini dan itu hampir tidak ideal karena penggunaan dengan std::string
akan membutuhkan pengulangan atas setiap karakter.
Apakah ada alternatif yang berfungsi 100% dari waktu?
c++
string
c++-standard-library
tolower
Konrad
sumber
sumber
Jawaban:
Diadaptasi dari Pertanyaan yang Tidak Sering Diajukan :
Anda benar-benar tidak akan pergi tanpa mengulangi setiap karakter. Tidak ada cara untuk mengetahui apakah karakter huruf kecil atau huruf besar sebaliknya.
Jika Anda benar-benar benci
tolower()
, inilah alternatif khusus ASCII yang tidak saya sarankan Anda gunakan:Perlu diketahui bahwa
tolower()
hanya dapat melakukan per-byte-byte-character subtitusi, yang tidak pas untuk banyak skrip, terutama jika menggunakan multi-byte-encoding seperti UTF-8.sumber
char
ke::tolower(int)
.) Anda perlu memastikan Anda tidak memberikan nilai negatif.::tolower
mungkin macet, itu UB untuk input non-ASCII.Boost menyediakan algoritme string untuk ini :
Atau, untuk yang tidak di tempat :
sumber
to_lower_copy
tl; dr
Gunakan perpustakaan ICU . Jika tidak, rutin konversi Anda akan membungkam kasus yang mungkin tidak Anda sadari bahkan ada.
Pertama, Anda harus menjawab pertanyaan: Apa pengkodean Anda
std::string
? Apakah ISO-8859-1? Atau mungkin ISO-8859-8? Atau Windows Codepage 1252? Apakah apa pun yang Anda gunakan untuk mengonversi huruf besar ke kecil tahu itu? (Atau apakah itu gagal total untuk karakter0x7f
?)Jika Anda menggunakan UTF-8 (satu-satunya pilihan yang waras di antara penyandian 8-bit) dengan
std::string
sebagai wadah, Anda sudah menipu diri sendiri untuk percaya bahwa Anda masih mengendalikan hal-hal, karena Anda menyimpan urutan karakter multibyte dalam wadah yang tidak menyadari konsep multibyte. Bahkan sesuatu yang sederhana seperti.substr()
bom waktu. (Karena pemisahan urutan multibyte akan menghasilkan string (sub-) yang tidak valid.)Dan begitu Anda mencoba sesuatu seperti
std::toupper( 'ß' )
, dalam pengkodean apa pun , Anda berada dalam masalah besar. (Karena sama sekali tidak mungkin untuk melakukan ini "benar" dengan perpustakaan standar, yang hanya dapat memberikan satu karakter hasil, bukan yang"SS"
diperlukan di sini.) [1] Contoh lain adalahstd::tolower( 'I' )
, yang akan menghasilkan hasil yang berbeda tergantung pada lokal . Di Jerman,'i'
akan benar; di Turki,'ı'
(LATIN KECIL SURAT DOTLESS I) adalah hasil yang diharapkan (yang, sekali lagi, lebih dari satu byte dalam pengkodean UTF-8). Contoh lain adalah Sigma Yunani , huruf besar'∑'
, huruf kecil'σ'
... kecuali pada akhir kata, di mana itu'ς'
.Jadi, konversi kasus apa pun yang berfungsi pada karakter pada satu waktu, atau lebih buruk, byte pada suatu waktu, rusak oleh desain.
Lalu ada titik bahwa perpustakaan standar, untuk apa yang mampu dilakukannya, tergantung pada daerah mana yang didukung pada mesin perangkat lunak Anda menjalankan ... dan apa yang Anda lakukan jika tidak?
Jadi yang benar - benar Anda cari adalah kelas string yang mampu menangani semua ini dengan benar, dan itu bukan
std::basic_string<>
varian apa pun .(C ++ 11 catatan:
std::u16string
danstd::u32string
yang lebih baik ., Tapi masih tidak sempurna C ++ 20 dibawastd::u8string
, tapi semua do ini adalah menentukan encoding Dalam banyak hal lain mereka masih tetap bodoh mekanika Unicode, seperti normalisasi, pemeriksaan, ... .)Sementara Boost terlihat bagus, API bijak, Boost.Locale pada dasarnya adalah pembungkus di sekitar ICU . Jika Boost dikompilasi dengan dukungan ICU ... jika tidak, Boost.Locale terbatas pada dukungan lokal yang dikompilasi untuk pustaka standar.
Dan percayalah, mendapatkan Boost untuk dikompilasi dengan ICU terkadang bisa sangat menyakitkan. (Tidak ada binari yang dikompilasi sebelumnya untuk Windows, jadi Anda harus menyediakannya bersama dengan aplikasi Anda, dan itu membuka kaleng cacing yang sama sekali baru ...)
Jadi secara pribadi saya akan merekomendasikan mendapatkan dukungan Unicode penuh langsung dari mulut kuda dan menggunakan perpustakaan ICU langsung:
Kompilasi (dengan G ++ dalam contoh ini):
Ini memberi:
Perhatikan bahwa konversi Σ <-> σ di tengah kata, dan konversi Σ <-> ς di akhir kata. Tidak ada
<algorithm>
solusi berbasis yang bisa memberi Anda itu.[1] Pada tahun 2017, Dewan untuk Orthografi Jerman memutuskan bahwa "ẞ" U + 1E9E LATIN MODAL SURAT SHARP S dapat digunakan secara resmi, sebagai opsi di samping konversi "SS" tradisional untuk menghindari ambiguitas misalnya dalam paspor (di mana nama ditulis dengan huruf besar ). Contoh masuk saya yang indah, dibuat usang oleh keputusan komite ...
sumber
toupper
dantolower
masih bekerja pada karakter tunggal. Kelas string masih tidak memiliki gagasan normalisasi (misalnya apakah "ü" dikodekan sebagai "u dengan diaeresis" atau "u + menggabungkan diaeresis") atau di mana string mungkin atau mungkin tidak dapat dipisahkan. Daftarnya berlanjut. u8string adalah (seperti kelas string standar lainnya) yang sesuai untuk "melewati". Tetapi jika Anda ingin memproses Unicode, Anda memerlukan ICU.Menggunakan rentang berbasis untuk loop C ++ 11 kode yang lebih sederhana adalah:
sumber
Jika string berisi karakter UTF-8 di luar rentang ASCII, maka boost :: algorithm :: to_lower tidak akan mengonversinya. Lebih baik gunakan boost :: locale :: to_lower saat UTF-8 terlibat. Lihat http://www.boost.org/doc/libs/1_51_0/libs/locale/doc/html/conversions.html
sumber
Ini adalah tindak lanjut dari tanggapan Stefan Mai: jika Anda ingin menempatkan hasil konversi di string lain, Anda perlu mengalokasikan terlebih dahulu ruang penyimpanannya sebelum menelepon
std::transform
. Karena STL menyimpan karakter yang diubah di iterator tujuan (menambahkannya di setiap iterasi dari loop), string tujuan tidak akan secara otomatis diubah ukurannya, dan Anda berisiko menginjak memori.sumber
Pendekatan lain menggunakan rentang berdasarkan untuk loop dengan variabel referensi
sumber
Sejauh yang saya lihat, Meningkatkan perpustakaan adalah kinerja yang sangat buruk. Saya telah menguji unordered_map mereka ke STL dan rata-rata 3 kali lebih lambat (kasus terbaik 2, terburuk adalah 10 kali). Algoritma ini juga terlihat terlalu rendah.
Perbedaannya sangat besar sehingga saya yakin penambahan apa pun yang perlu Anda lakukan
tolower
untuk membuatnya setara dengan meningkatkan "untuk kebutuhan Anda" akan jauh lebih cepat daripada meningkatkan.Saya telah melakukan tes ini pada Amazon EC2, oleh karena itu kinerja bervariasi selama tes tetapi Anda masih mendapatkan ide.
-O2
membuatnya seperti ini:Sumber:
Saya kira saya harus melakukan tes pada mesin khusus tetapi saya akan menggunakan EC2 ini sehingga saya tidak benar-benar perlu mengujinya di mesin saya.
sumber
Cara termudah untuk mengubah string menjadi loweercase tanpa perlu repot tentang std namespace adalah sebagai berikut
1: string dengan / tanpa spasi
2: string tanpa spasi
sumber
std::ctype::tolower()
dari pustaka C ++ Lokalisasi standar akan melakukan ini dengan benar untuk Anda. Berikut adalah contoh yang diambil dari halaman referensi tolowersumber
const
? Tampaknya membuatnya sedikit lebih berantakan (mis. Sepertinya tidak bisa Anda gunakanf.tolower()
), karena Anda perlu memasukkan karakter ke string baru. Apakah Anda akan menggunakantransform()
dan sepertistd::bind1st( std::mem_fun() )
untuk operator?tolower
denganlocale
parameter, panggilan implisituse_facet
tampaknya merupakan hambatan kinerja. Salah satu rekan kerja saya telah mencapai beberapa peningkatan kecepatan 100% dengan menggantiboost::iequals
(yang memiliki masalah ini) dengan versi di manause_facet
hanya disebut sekali di luar loop.Alternatif untuk Meningkatkan adalah POCO (pocoproject.org).
POCO menyediakan dua varian:
Versi "In Place" selalu memiliki "InPlace" dalam namanya.
Kedua versi ditunjukkan di bawah ini:
sumber
Ada cara untuk mengubah huruf besar menjadi lebih rendah TANPA melakukan tes jika , dan itu cukup mudah. Fungsi isupper () / makro dari clocale.h harus menangani masalah yang berkaitan dengan lokasi Anda, tetapi jika tidak, Anda selalu dapat mengubah UtoL [] sesuai dengan isi hati Anda.
Mengingat bahwa karakter C benar-benar hanya int 8-bit (mengabaikan set karakter lebar untuk saat ini), Anda dapat membuat array 256 byte yang memegang set karakter alternatif, dan dalam fungsi konversi gunakan karakter dalam string Anda sebagai subskrip ke dalam array konversi.
Alih-alih pemetaan 1-untuk-1, berikan nilai array BYTE untuk huruf besar untuk karakter huruf kecil. Anda mungkin menemukan islower () dan isupper () berguna di sini.
Kode ini terlihat seperti ini ...
Pendekatan ini akan, pada saat yang sama, memungkinkan Anda untuk memetakan kembali karakter lain yang ingin Anda ubah.
Pendekatan ini memiliki satu keuntungan besar ketika berjalan pada prosesor modern, tidak perlu melakukan prediksi cabang karena tidak ada jika tes terdiri dari percabangan. Ini menyimpan logika prediksi cabang CPU untuk loop lain, dan cenderung mencegah warung pipa.
Beberapa di sini mungkin mengenali pendekatan ini sebagai yang sama digunakan untuk mengkonversi EBCDIC ke ASCII.
sumber
Karena tidak ada jawaban yang menyebutkan perpustakaan Ranges yang akan datang, yang tersedia di perpustakaan standar sejak C ++ 20, dan saat ini tersedia secara terpisah di GitHub sebagai
range-v3
, saya ingin menambahkan cara untuk melakukan konversi ini menggunakannya.Untuk memodifikasi string di tempat:
Untuk menghasilkan string baru:
(Jangan lupa
#include <cctype>
dan header Ranges yang diperlukan.)Catatan: penggunaan
unsigned char
sebagai argumen ke lambda terinspirasi oleh cppreference , yang menyatakan:sumber
Fungsi templat saya sendiri yang menjalankan huruf besar / kecil.
sumber
towlower
karakter lebar untuk yang mendukung UTF-16.Berikut adalah teknik makro jika Anda menginginkan sesuatu yang sederhana:
Namun, perhatikan bahwa komentar @ AndreasSpindler pada jawaban ini masih merupakan pertimbangan penting, namun, jika Anda sedang mengerjakan sesuatu yang bukan hanya karakter ASCII.
sumber
void strtoupper(std::string& x) { std::transform (x.begin(), x.end(), x.begin(), ::toupper); }
x
bisa menjadi ekspresi yang valid, yang kebetulan mengkompilasi dengan benar tetapi akan memberikan hasil yang sepenuhnya palsu karena makro.Untuk informasi lebih lanjut: http://www.cplusplus.com/reference/locale/tolower/
sumber
Tidak
Ada beberapa pertanyaan yang perlu Anda tanyakan pada diri sendiri sebelum memilih metode huruf kecil.
Setelah Anda menjawab pertanyaan-pertanyaan itu, Anda dapat mulai mencari solusi yang sesuai dengan kebutuhan Anda. Tidak ada satu ukuran yang cocok untuk semua orang di mana saja!
sumber
Coba fungsi ini :)
sumber
Pada platform microsoft Anda dapat menggunakan
strlwr
keluarga fungsi: http://msdn.microsoft.com/en-us/library/hkxwh33z.aspxsumber
Cuplikan Kode
sumber
Gunakan fplus :: to_lower_case ().
(fplus: https://github.com/Dobiasd/FunctionalPlus .
Cari 'to_lower_case' di http://www.editgym.com/fplus-api-search/ )
sumber
Salin karena tidak diizinkan untuk meningkatkan jawaban. Terima kasih
Penjelasan:
for(auto& c : test)
adalah range-based untuk loop semacam itu :for (
range_declaration
:
range_expression
)
loop_statement
range_declaration
:auto& c
Di sini specifier otomatis digunakan untuk pengurangan tipe otomatis. Jadi tipe akan dikurangkan dari variabel initializer.
range_expression
:test
Rentang dalam hal ini adalah karakter string
test
.Karakter string
test
tersedia sebagai referensi di dalam for loop through identifierc
.sumber
C ++ tidak menerapkan metode tolower atau toupper untuk string, tetapi tersedia untuk char. Seseorang dapat dengan mudah membaca setiap karakter string, mengubahnya menjadi case yang diperlukan dan mengembalikannya ke string. Kode sampel tanpa menggunakan perpustakaan pihak ketiga mana pun:
Untuk operasi berbasis karakter pada string: Untuk setiap karakter dalam string
sumber
Ini bisa menjadi versi sederhana lainnya untuk mengonversi huruf besar menjadi huruf kecil dan sebaliknya. Saya menggunakan versi komunitas VS2017 untuk mengkompilasi kode sumber ini.
Catatan: jika ada karakter khusus maka perlu ditangani menggunakan pemeriksaan kondisi.
sumber
Saya mencoba std :: transform, semua yang saya dapatkan adalah kesalahan kompilasi criptic stl yang buruk yang hanya dapat dimengerti oleh druid dari 200 tahun yang lalu (tidak dapat mengonversi dari ke flibidi flabidi flu)
ini berfungsi dengan baik dan dapat dengan mudah di-tweak
sumber