Bagaimana cara mengubah std :: string menjadi huruf kecil?


777

Saya ingin mengonversi a std::stringmenjadi 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::stringakan membutuhkan pengulangan atas setiap karakter.

Apakah ada alternatif yang berfungsi 100% dari waktu?


34
Bagaimana lagi Anda mengonversi setiap elemen daftar sesuatu menjadi sesuatu yang lain, tanpa mengulangi daftar itu? Sebuah string hanyalah daftar karakter, jika Anda perlu menerapkan beberapa fungsi untuk setiap karakter, Anda harus beralih melalui string. Tidak ada jalan lain.

14
Mengapa tepatnya pertanyaan ini menurunkan peringkat? Saya tidak punya masalah dengan iterasi melalui string saya, tetapi saya bertanya apakah ada fungsi lain selain tolower (), toupper () dll.
Konrad

3
Jika Anda memiliki larik karakter gaya C, maka saya kira Anda mungkin dapat menambahkan ox20202020 ke setiap blok yang terdiri dari 4 karakter (asalkan mereka SEMUA sudah huruf besar) untuk mengonversi 4 karakter menjadi huruf kecil sekaligus.

13
@Dan: Jika mereka mungkin sudah huruf kecil, tetapi pasti AZ atau az, Anda bisa ATAU dengan 0x20 bukannya menambahkan. Salah satu optimisasi yang sangat pintar-mungkin-bodoh-yang hampir tidak pernah sepadan ...
Steve Jessop

4
Saya tidak tahu mengapa itu akan dipilih-turun ... tentu itu dikatakan sedikit aneh (karena Anda entah bagaimana harus mengulangi setiap item), tapi itu pertanyaan yang valid
warren

Jawaban:


905

Diadaptasi dari Pertanyaan yang Tidak Sering Diajukan :

#include <algorithm>
#include <cctype>
#include <string>

std::string data = "Abc";
std::transform(data.begin(), data.end(), data.begin(),
    [](unsigned char c){ return std::tolower(c); });

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:

char asciitolower(char in) {
    if (in <= 'Z' && in >= 'A')
        return in - ('Z' - 'z');
    return in;
}

std::transform(data.begin(), data.end(), data.begin(), asciitolower);

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.


25
(Lama mungkin, algoritma yang dimaksud telah berubah sedikit) @Stefan Mai: Apa jenis "seluruh banyak overhead" yang ada dalam memanggil algoritma STL? Fungsi-fungsinya agak ramping (yaitu simpel untuk loop) dan sering kali digarisbawahi karena Anda jarang memiliki banyak panggilan ke fungsi yang sama dengan parameter templat yang sama di unit kompilasi yang sama.
Persamaan

257
Setiap kali Anda menganggap karakter adalah ASCII, Tuhan membunuh anak kucing. :(
Brian Gordon

13
Contoh pertama Anda berpotensi memiliki perilaku yang tidak terdefinisi (beralih charke ::tolower(int).) Anda perlu memastikan Anda tidak memberikan nilai negatif.
juanchopanza

37
-1 penggunaan ini ::tolowermungkin macet, itu UB untuk input non-ASCII.
Ceria dan hth. - Alf

7
The :: diperlukan sebelum tolower untuk menunjukkan bahwa ia berada di namespace terluar. Jika Anda menggunakan kode ini di namespace lain, mungkin ada definisi tolower yang berbeda (mungkin tidak terkait) yang akhirnya akan dipilih secara istimewa tanpa ::.
Charles Ofria

320

Boost menyediakan algoritme string untuk ini :

#include <boost/algorithm/string.hpp>

std::string str = "HELLO, WORLD!";
boost::algorithm::to_lower(str); // modifies str

Atau, untuk yang tidak di tempat :

#include <boost/algorithm/string.hpp>

const std::string str = "HELLO, WORLD!";
const std::string lower_str = boost::algorithm::to_lower_copy(str);

2
Saya menganggap ini tidak punya masalah yang sama seperti tolower dengan input ASCII?
paulm

19
Gagal untuk non-ASCII-7.
DevSolar

1
Apakah ada versi tidak di tempat ini?
Ray

5
@ Ray, ya,to_lower_copy
smac89

234

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 karakter 0x7f?)

Jika Anda menggunakan UTF-8 (satu-satunya pilihan yang waras di antara penyandian 8-bit) dengan std::stringsebagai 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 adalah std::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 bukanstd::basic_string<> varian apa pun .

(C ++ 11 catatan: std::u16stringdan std::u32stringyang lebih baik ., Tapi masih tidak sempurna C ++ 20 dibawa std::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:

#include <unicode/unistr.h>
#include <unicode/ustream.h>
#include <unicode/locid.h>

#include <iostream>

int main()
{
    /*                          "Odysseus" */
    char const * someString = u8"ΟΔΥΣΣΕΥΣ";
    icu::UnicodeString someUString( someString, "UTF-8" );
    // Setting the locale explicitly here for completeness.
    // Usually you would use the user-specified system locale,
    // which *does* make a difference (see ı vs. i above).
    std::cout << someUString.toLower( "el_GR" ) << "\n";
    std::cout << someUString.toUpper( "el_GR" ) << "\n";
    return 0;
}

Kompilasi (dengan G ++ dalam contoh ini):

g++ -Wall example.cpp -licuuc -licuio

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 ...


19
Ini adalah jawaban yang benar dalam kasus umum. Standar tidak memberikan apa pun untuk menangani apa pun kecuali "ASCII" kecuali kebohongan dan penipuan. Itu membuat Anda berpikir Anda mungkin bisa berurusan dengan mungkin UTF-16, tetapi Anda tidak bisa. Seperti jawaban ini mengatakan, Anda tidak bisa mendapatkan panjang karakter yang tepat (bukan byte-panjang) dari string UTF-16 tanpa melakukan penanganan unicode Anda sendiri. Jika Anda harus berurusan dengan teks asli, gunakan ICU. Terima kasih, @DevSolar
Penebusan Terbatas

Apakah ICU tersedia secara default di Ubuntu / Windows atau perlu diinstal secara terpisah? Juga bagaimana dengan jawaban ini: stackoverflow.com/a/35075839/207661 ?
Shital Shah

1
Hei, lihat, jawaban nyata! Terima kasih telah mengarahkan saya ke kanan langsung, DevSolar.
Dan Bechard

2
@DevSolar Setuju! Konsep panjang agak tidak berarti pada teks (kita bisa menambahkan ligatur ke daftar pelaku). Yang mengatakan, karena orang terbiasa tab dan mengontrol karakter mengambil satu satuan panjang, poin kode akan menjadi ukuran yang lebih intuitif. Oh, dan terima kasih telah memberikan jawaban yang benar, sedih melihatnya sejauh ini :-(
masaers

3
@ LK Sedikit lebih baik. Tetapi begitu banyak hal yang masih belum tercakup: toupperdantolower 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.
DevSolar

36

Menggunakan rentang berbasis untuk loop C ++ 11 kode yang lebih sederhana adalah:

#include <iostream>       // std::cout
#include <string>         // std::string
#include <locale>         // std::locale, std::tolower

int main ()
{
  std::locale loc;
  std::string str="Test String.\n";

 for(auto elem : str)
    std::cout << std::tolower(elem,loc);
}

9
Namun, pada mesin Prancis, program ini tidak mengonversi karakter ASCII non yang diizinkan dalam bahasa Prancis. Misalnya string 'Test String123. É Ï \ n 'akan dikonversi menjadi:' test string123. É Ï \ n 'meskipun karakter É Ï dan huruf kecil couterparts mereka' é 'dan' ï ', diizinkan dalam bahasa Prancis. Tampaknya tidak ada solusi untuk yang disediakan oleh pesan lain dari utas ini.
Mengiris

Saya pikir Anda perlu menetapkan lokal yang tepat untuk itu.
user1095108

@incises, ini kemudian seseorang mengirim jawaban tentang ICU dan itu pasti cara untuk pergi. Lebih mudah daripada kebanyakan solusi lain yang akan berusaha memahami lokal.
Alexis Wilke

Saya lebih suka tidak menggunakan perpustakaan eksternal jika memungkinkan, secara pribadi.
kayleeFrye_onDeck


15

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.

#include <string>
#include <algorithm>
#include <iostream>

int main (int argc, char* argv[])
{
  std::string sourceString = "Abc";
  std::string destinationString;

  // Allocate the destination space
  destinationString.resize(sourceString.size());

  // Convert the source string to lower case
  // storing the result in destination string
  std::transform(sourceString.begin(),
                 sourceString.end(),
                 destinationString.begin(),
                 ::tolower);

  // Output the result of the conversion
  std::cout << sourceString
            << " -> "
            << destinationString
            << std::endl;
}

1
Ini tidak mengubah ukuran Ä menjadi ä untukku
Purefan

Bisa juga menggunakan iterator inserter belakang di sini alih-alih mengubah ukuran manual.
cabai

11

Pendekatan lain menggunakan rentang berdasarkan untuk loop dengan variabel referensi

string test = "Hello World";
for(auto& c : test)
{
   c = tolower(c);
}

cout<<test<<endl;

6

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 toloweruntuk 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.

./test
Elapsed time: 12365milliseconds
Elapsed time: 1640milliseconds
./test
Elapsed time: 26978milliseconds
Elapsed time: 1646milliseconds
./test
Elapsed time: 6957milliseconds
Elapsed time: 1634milliseconds
./test
Elapsed time: 23177milliseconds
Elapsed time: 2421milliseconds
./test
Elapsed time: 17342milliseconds
Elapsed time: 14132milliseconds
./test
Elapsed time: 7355milliseconds
Elapsed time: 1645milliseconds

-O2 membuatnya seperti ini:

./test
Elapsed time: 3769milliseconds
Elapsed time: 565milliseconds
./test
Elapsed time: 3815milliseconds
Elapsed time: 565milliseconds
./test
Elapsed time: 3643milliseconds
Elapsed time: 566milliseconds
./test
Elapsed time: 22018milliseconds
Elapsed time: 566milliseconds
./test
Elapsed time: 3845milliseconds
Elapsed time: 569milliseconds

Sumber:

string str;
bench.start();
for(long long i=0;i<1000000;i++)
{
    str="DSFZKMdskfdsjfsdfJDASFNSDJFXCKVdnjsafnjsdfjdnjasnJDNASFDJDSFSDNJjdsanjfsdnfjJNFSDJFSD";
    boost::algorithm::to_lower(str);
}
bench.end();

bench.start();
for(long long i=0;i<1000000;i++)
{
    str="DSFZKMdskfdsjfsdfJDASFNSDJFXCKVdnjsafnjsdfjdnjasnJDNASFDJDSFSDNJjdsanjfsdnfjJNFSDJFSD";
    for(unsigned short loop=0;loop < str.size();loop++)
    {
        str[loop]=tolower(str[loop]);
    }
}
bench.end();

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.


1
Sudahkah Anda membuka opsi pengoptimalan saat mengompilasinya? Saya pikir perpustakaan peningkatan berat STL harus berjalan lebih baik dengan tingkat optimasi yang tinggi.
Wei Song

1
Saya menggunakan -O2 di salah satu tes, dan tidak ada yang lain.
Etherealone

2
Kinerja unordered_map bergantung pada algoritma hashing yang dikombinasikan dengan data yang Anda gunakan. Tidak ada algoritma hashing ajaib yang berfungsi untuk semua dan semua data untuk membuat unordered_map secepat mungkin. Benchmark dan coba hal yang berbeda. Alasan Anda mendapatkan kinerja yang lebih buruk, adalah karena dengan hash yang Anda gunakan, Anda mendapatkan banyak tabrakan, yang pada dasarnya menyebabkan pencarian dalam daftar. Lihat situs ini untuk info lebih lanjut: fgda.pl/post/7/gcc-hash-map-vs-unordered-map Untuk tujuan saya, fungsi yang disediakan di tautan mengurangi tabrakan dan karenanya sangat cepat.
leetNightshade

6

Cara termudah untuk mengubah string menjadi loweercase tanpa perlu repot tentang std namespace adalah sebagai berikut

1: string dengan / tanpa spasi

#include <algorithm>
#include <iostream>
#include <string>
using namespace std;
int main(){
    string str;
    getline(cin,str);
//------------function to convert string into lowercase---------------
    transform(str.begin(), str.end(), str.begin(), ::tolower);
//--------------------------------------------------------------------
    cout<<str;
    return 0;
}

2: string tanpa spasi

#include <algorithm>
#include <iostream>
#include <string>
using namespace std;
int main(){
    string str;
    cin>>str;
//------------function to convert string into lowercase---------------
    transform(str.begin(), str.end(), str.begin(), ::tolower);
//--------------------------------------------------------------------
    cout<<str;
    return 0;
}

5

std::ctype::tolower()dari pustaka C ++ Lokalisasi standar akan melakukan ini dengan benar untuk Anda. Berikut adalah contoh yang diambil dari halaman referensi tolower

#include <locale>
#include <iostream>

int main () {
  std::locale::global(std::locale("en_US.utf8"));
  std::wcout.imbue(std::locale());
  std::wcout << "In US English UTF-8 locale:\n";
  auto& f = std::use_facet<std::ctype<wchar_t>>(std::locale());
  std::wstring str = L"HELLo, wORLD!";
  std::wcout << "Lowercase form of the string '" << str << "' is ";
  f.tolower(&str[0], &str[0] + str.size());
  std::wcout << "'" << str << "'\n";
}

Bagus, selama Anda dapat mengonversi karakter di tempatnya. Bagaimana jika string sumber Anda const? Tampaknya membuatnya sedikit lebih berantakan (mis. Sepertinya tidak bisa Anda gunakan f.tolower()), karena Anda perlu memasukkan karakter ke string baru. Apakah Anda akan menggunakan transform()dan seperti std::bind1st( std::mem_fun() )untuk operator?
quazar

Untuk string const, kita bisa membuat salinan lokal dan mengubahnya di tempat.
Sameer

Ya, membuat salinan menambah overhead.
quazar

Anda bisa menggunakan std :: transform dengan versi ctype :: tolower yang tidak menggunakan pointer. Gunakan adaptor iterator inserter belakang dan Anda bahkan tidak perlu khawatir tentang pre-sizing string output Anda.
cabai

Hebat, terutama karena dalam libstdc ++ tolowerdengan localeparameter, panggilan implisit use_facettampaknya merupakan hambatan kinerja. Salah satu rekan kerja saya telah mencapai beberapa peningkatan kecepatan 100% dengan mengganti boost::iequals(yang memiliki masalah ini) dengan versi di mana use_facethanya disebut sekali di luar loop.
Arne Vogel

3

Alternatif untuk Meningkatkan adalah POCO (pocoproject.org).

POCO menyediakan dua varian:

  1. Varian pertama membuat salinan tanpa mengubah string asli.
  2. Varian kedua mengubah string asli pada tempatnya.
    Versi "In Place" selalu memiliki "InPlace" dalam namanya.

Kedua versi ditunjukkan di bawah ini:

#include "Poco/String.h"
using namespace Poco;

std::string hello("Stack Overflow!");

// Copies "STACK OVERFLOW!" into 'newString' without altering 'hello.'
std::string newString(toUpper(hello));

// Changes newString in-place to read "stack overflow!"
toLowerInPlace(newString);

3

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.

masukkan deskripsi gambar di sini

Kode ini terlihat seperti ini ...

#include <clocale>
static char UtoL[256];
// ----------------------------------------------------------------------------
void InitUtoLMap()  {
    for (int i = 0; i < sizeof(UtoL); i++)  {
        if (isupper(i)) {
            UtoL[i] = (char)(i + 32);
        }   else    {
            UtoL[i] = i;
        }
    }
}
// ----------------------------------------------------------------------------
char *LowerStr(char *szMyStr) {
    char *p = szMyStr;
    // do conversion in-place so as not to require a destination buffer
    while (*p) {        // szMyStr must be null-terminated
        *p = UtoL[*p];  
        p++;
    }
    return szMyStr;
}
// ----------------------------------------------------------------------------
int main() {
    time_t start;
    char *Lowered, Upper[128];
    InitUtoLMap();
    strcpy(Upper, "Every GOOD boy does FINE!");

    Lowered = LowerStr(Upper);
    return 0;
}

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.


2
"Ada cara untuk mengubah huruf besar menjadi lebih rendah TANPA melakukan jika tes" pernah mendengar tentang tabel pencarian?
Gábor Buella

1
Perilaku tidak terdefinisi untuk karakter negatif.
Roland Illig

CPU modern mengalami hambatan dalam memori, bukan CPU. Benchmarking akan menarik.
Contango

3

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 sebagairange-v3 , saya ingin menambahkan cara untuk melakukan konversi ini menggunakannya.

Untuk memodifikasi string di tempat:

str |= action::transform([](unsigned char c){ return std::tolower(c); });

Untuk menghasilkan string baru:

auto new_string = original_string
    | view::transform([](unsigned char c){ return std::tolower(c); });

(Jangan lupa #include <cctype> dan header Ranges yang diperlukan.)

Catatan: penggunaan unsigned charsebagai argumen ke lambda terinspirasi oleh cppreference , yang menyatakan:

Seperti semua fungsi lain dari <cctype>, perilaku std::tolowertidak terdefinisi jika nilai argumen tidak dapat direpresentasikan unsigned charmaupun tidak sama dengan EOF. Untuk menggunakan fungsi-fungsi ini dengan aman dengan chars (atau signed chars), argumen pertama-tama harus dikonversi ke unsigned char:

char my_tolower(char ch)
{
    return static_cast<char>(std::tolower(static_cast<unsigned char>(ch)));
}

Demikian pula, mereka tidak boleh langsung digunakan dengan algoritma standar ketika tipe nilai iterator adalah charatau signed char. Sebagai gantinya, ubah nilainya menjadi yang unsigned charpertama:

std::string str_tolower(std::string s) {
    std::transform(s.begin(), s.end(), s.begin(), 
                // static_cast<int(*)(int)>(std::tolower)         // wrong
                // [](int c){ return std::tolower(c); }           // wrong
                // [](char c){ return std::tolower(c); }          // wrong
                   [](unsigned char c){ return std::tolower(c); } // correct
                  );
    return s;
}

3

Fungsi templat saya sendiri yang menjalankan huruf besar / kecil.

#include <string>
#include <algorithm>

//
//  Lowercases string
//
template <typename T>
std::basic_string<T> lowercase(const std::basic_string<T>& s)
{
    std::basic_string<T> s2 = s;
    std::transform(s2.begin(), s2.end(), s2.begin(), tolower);
    return std::move(s2);
}

//
// Uppercases string
//
template <typename T>
std::basic_string<T> uppercase(const std::basic_string<T>& s)
{
    std::basic_string<T> s2 = s;
    std::transform(s2.begin(), s2.end(), s2.begin(), toupper);
    return std::move(s2);
}

Inilah yang saya butuhkan. Saya hanya menggunakan towlowerkarakter lebar untuk yang mendukung UTF-16.
Juv

2

Berikut adalah teknik makro jika Anda menginginkan sesuatu yang sederhana:

#define STRTOLOWER(x) std::transform (x.begin(), x.end(), x.begin(), ::tolower)
#define STRTOUPPER(x) std::transform (x.begin(), x.end(), x.begin(), ::toupper)
#define STRTOUCFIRST(x) std::transform (x.begin(), x.begin()+1, x.begin(),  ::toupper); std::transform (x.begin()+1, x.end(),   x.begin()+1,::tolower)

Namun, perhatikan bahwa komentar @ AndreasSpindler pada jawaban ini masih merupakan pertimbangan penting, namun, jika Anda sedang mengerjakan sesuatu yang bukan hanya karakter ASCII.


1
Saya downvoting ini untuk memberikan makro ketika ada solusi yang sangat baik - Anda bahkan memberikan solusi itu.
Clearer

2
Teknik makro berarti kurang mengetik kode untuk sesuatu yang biasanya banyak digunakan dalam pemrograman. Kenapa tidak menggunakannya? Kalau tidak, mengapa ada makro sama sekali?
Volomike

3
Makro adalah warisan dari C yang sedang bekerja keras untuk dihilangkan. Jika Anda ingin mengurangi jumlah pengetikan, gunakan fungsi atau lambda. void strtoupper(std::string& x) { std::transform (x.begin(), x.end(), x.begin(), ::toupper); }
Clearer

1
@Clearer Karena saya ingin menjadi pembuat kode yang lebih baik, dapatkah Anda memberikan saya tautan doc ANSI di mana komite ANSI C ++ mengatakan sesuatu, "Kita perlu mengadakan rapat untuk menyingkirkan makro dari C ++"? Atau beberapa peta jalan lainnya?
Volomike

2
Tidak, saya tidak bisa. Sikap Bjarne pada topik telah dibuat cukup jelas pada beberapa kesempatan. Selain itu, ada banyak alasan untuk tidak menggunakan makro di C serta C ++. xbisa menjadi ekspresi yang valid, yang kebetulan mengkompilasi dengan benar tetapi akan memberikan hasil yang sepenuhnya palsu karena makro.
jelas

2
// tolower example (C++)
#include <iostream>       // std::cout
#include <string>         // std::string
#include <locale>         // std::locale, std::tolower

int main ()
{
  std::locale loc;
  std::string str="Test String.\n";
  for (std::string::size_type i=0; i<str.length(); ++i)
    std::cout << std::tolower(str[i],loc);
  return 0;
}

Untuk informasi lebih lanjut: http://www.cplusplus.com/reference/locale/tolower/


2

Apakah ada alternatif yang berfungsi 100% dari waktu?

Tidak

Ada beberapa pertanyaan yang perlu Anda tanyakan pada diri sendiri sebelum memilih metode huruf kecil.

  1. Bagaimana string dikodekan? ASCII biasa? UTF-8? beberapa bentuk pengkodean warisan ASCII yang diperluas?
  2. Apa yang Anda maksud dengan huruf kecil? Aturan pemetaan kasus bervariasi antara bahasa! Apakah Anda ingin sesuatu yang dilokalisasi ke lokal pengguna? apakah Anda menginginkan sesuatu yang berperilaku konsisten pada semua sistem yang dijalankan oleh perangkat lunak Anda? Apakah Anda hanya ingin huruf kecil ASCII dan melewati semua yang lain?
  3. Perpustakaan apa yang tersedia?

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!


2

Coba fungsi ini :)

string toLowerCase(string str) {
    int str_len = str.length();
    string final_str = "";
    for(int i=0; i<str_len; i++) {
        char character = str[i];
        if(character>=65 && character<=92) {
            final_str += (character+32);
        } else {
            final_str += character;
        }
    }
    return final_str;
}

1

Pada platform microsoft Anda dapat menggunakan strlwrkeluarga fungsi: http://msdn.microsoft.com/en-us/library/hkxwh33z.aspx

// crt_strlwr.c
// compile with: /W3
// This program uses _strlwr and _strupr to create
// uppercase and lowercase copies of a mixed-case string.
#include <string.h>
#include <stdio.h>

int main( void )
{
   char string[100] = "The String to End All Strings!";
   char * copy1 = _strdup( string ); // make two copies
   char * copy2 = _strdup( string );

   _strlwr( copy1 ); // C4996
   _strupr( copy2 ); // C4996

   printf( "Mixed: %s\n", string );
   printf( "Lower: %s\n", copy1 );
   printf( "Upper: %s\n", copy2 );

   free( copy1 );
   free( copy2 );
}

0

Cuplikan Kode

#include<bits/stdc++.h>
using namespace std;


int main ()
{
    ios::sync_with_stdio(false);

    string str="String Convert\n";

    for(int i=0; i<str.size(); i++)
    {
      str[i] = tolower(str[i]);
    }
    cout<<str<<endl;

    return 0;
}


0

Salin karena tidak diizinkan untuk meningkatkan jawaban. Terima kasih


string test = "Hello World";
for(auto& c : test)
{
   c = tolower(c);
}

Penjelasan:

for(auto& c : test)adalah range-based untuk loop semacam itu :
for (range_declaration:range_expression)loop_statement

  1. range_declaration: auto& c
    Di sini specifier otomatis digunakan untuk pengurangan tipe otomatis. Jadi tipe akan dikurangkan dari variabel initializer.

  2. range_expression: test
    Rentang dalam hal ini adalah karakter string test.

Karakter string testtersedia sebagai referensi di dalam for loop through identifier c.


Harap jelaskan dari mana Anda menyalin jawaban Anda.
bfontaine

0

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:

#include<iostream>

int main(){
  std::string str = std::string("How IS The Josh");
  for(char &ch : str){
    ch = std::tolower(ch);
  }
  std::cout<<str<<std::endl;
  return 0;
}

Untuk operasi berbasis karakter pada string: Untuk setiap karakter dalam string


-1

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.

#include <iostream>
#include <string>
using namespace std;

int main()
{
    std::string _input = "lowercasetouppercase";
#if 0
    // My idea is to use the ascii value to convert
    char upperA = 'A';
    char lowerA = 'a';

    cout << (int)upperA << endl; // ASCII value of 'A' -> 65
    cout << (int)lowerA << endl; // ASCII value of 'a' -> 97
    // 97-65 = 32; // Difference of ASCII value of upper and lower a
#endif // 0

    cout << "Input String = " << _input.c_str() << endl;
    for (int i = 0; i < _input.length(); ++i)
    {
        _input[i] -= 32; // To convert lower to upper
#if 0
        _input[i] += 32; // To convert upper to lower
#endif // 0
    }
    cout << "Output String = " << _input.c_str() << endl;

    return 0;
}

Catatan: jika ada karakter khusus maka perlu ditangani menggunakan pemeriksaan kondisi.


-8

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

string LowerCase(string s)
{
    int dif='a'-'A';
    for(int i=0;i<s.length();i++)
    {
        if((s[i]>='A')&&(s[i]<='Z'))
            s[i]+=dif;
    }
   return s;
}

string UpperCase(string s)
{
   int dif='a'-'A';
    for(int i=0;i<s.length();i++)
    {
        if((s[i]>='a')&&(s[i]<='z'))
            s[i]-=dif;
    }
   return s;
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.