Ubah String Dalam C ++ Ke Huruf Besar


268

Bagaimana seseorang dapat mengkonversi string ke huruf besar. Contoh yang saya temukan dari googling hanya harus berurusan dengan karakter.

Jawaban:


205

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");

5
Ini juga memiliki manfaat i18n, di mana ::toupperkemungkinan besar mengasumsikan ASCII.
Ben Straub

4
Baris terakhir Anda tidak dikompilasi - Anda harus mengubah ke sesuatu seperti:std::string newstr(boost::to_upper_copy<std::string>("Hello World"));
maxschlepzig

58
ini seharusnya bukan jawaban yang diterima karena membutuhkan dorongan, atau judul harus diubah.
Andrea

44
ya, saya akan menginstal boost hanya untuk to_upper ... ide bagus! </sarcasm> :)
thang

12
Saya pribadi tidak suka dengan boost sebagai jawaban untuk "bagaimana saya melakukan x dalam C ++?" karena meningkatkan bukanlah solusi yang ringan sama sekali. Tampaknya Anda membeli boost sebagai kerangka kerja (atau ACE atau Qt atau Recusion ToolKit ++ atau ...) atau tidak. Saya lebih suka melihat solusi bahasa.
jwm

486
#include <algorithm>
#include <string>

std::string str = "Hello World";
std::transform(str.begin(), str.end(),str.begin(), ::toupper);

8
Sebenarnya, toupper()bisa diimplementasikan sebagai makro. Ini dapat menyebabkan masalah.
dirkgently

3
sebuah bind (:: toupper, build <unsigned char> (_ 1)) dengan boost.lambda akan berfungsi dengan baik, saya rasa.
Johannes Schaub - litb

11
Pendekatan ini berfungsi baik untuk ASCII, tetapi gagal untuk pengkodean karakter multi-byte, atau untuk aturan casing khusus seperti Jerman 'ß'.
dan04

9
Saya mengubah jawaban yang diterima menjadi menggunakan pustaka boost, karena lebih cepat (dalam pengujian informal saya), lebih mudah digunakan, dan tidak memiliki masalah yang terkait dengan solusi ini. Masih merupakan solusi yang baik untuk kasus di mana dorongan tidak dapat digunakan.
OrangeAlmondSoap

2
Saya tidak mengerti mengapa kompiler menolak kode ini tanpa :: kualifikasi sebelumnya toupper. Ada ide?
sasha.sochka

90

Solusi singkat menggunakan C ++ 11 dan toupper ().

for (auto & c: str) c = toupper(c);

Tidak akan cmenjadi const charjenis (dari auto)? Jika demikian, Anda tidak dapat menetapkannya (karena constsebagian) untuk apa yang dikembalikan oleh toupper(c).
PolGraphic

5
@PolGraphic: Rentang - berbasis untuk menggunakan metode begin () / end () kontainer untuk beralih pada isinya. std :: basic_string memiliki const dan iterator yang dapat berubah (masing-masing dikembalikan oleh cbegin () dan begin (), lihat std :: basic_string :: begin ), jadi untuk (:) menggunakan yang sesuai (cbegin () jika str adalah mendeklarasikan const, dengan auto =: = const char, begin () sebaliknya, dengan auto =: = char).
Thanasis Papoutsidakis

5
Lihat dirkgently's anser di bawah ini, cperlu di-cast agar unsigned charini bisa dikorupsi.
Cris Luengo

boost to_upper () tampaknya jauh lebih konsisten dengan fungsi c ++ STL daripada 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());

Catatan: Beberapa masalah dengan solusi teratas:

21.5 Utilitas urutan yang diakhiri null

Isi header ini harus sama dengan header Perpustakaan C Standar <ctype.h>, <wctype.h>, <string.h>, <wchar.h>, dan <stdlib.h> [...]

  • Yang berarti bahwa cctypeanggota 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 charditandatangani. (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.)


Anggota cctype normal adalah makro. Saya ingat pernah membaca bahwa mereka juga harus berfungsi, walaupun saya tidak memiliki salinan standar C90 dan tidak tahu apakah itu dinyatakan secara eksplisit atau tidak.
David Thornley

1
mereka harus menjadi fungsi di C ++ - bahkan jika C memungkinkan mereka untuk menjadi makro. Saya setuju dengan poin kedua Anda tentang casting. solusi teratas bisa melewati nilai negatif dan menyebabkan UB dengan itu. itulah alasan saya tidak memilih itu (tapi saya tidak memilih turun baik) :)
Johannes Schaub - litb

1
kutipan standar tidak boleh dilewatkan: 7.4.2.2/1 (litb yang buruk, yang merujuk hanya konsep C99 TC2), dan C ++ 17.4.1.2/6 dalam standar c ++ 98 kemuliaan.
Johannes Schaub - litb

1
(catat catatan kaki untuk itu: "Ini melarang praktik umum menyediakan makro masking .... bla blupp .. satu-satunya cara untuk melakukannya di C ++ adalah dengan menyediakan fungsi inline eksternal.") :)
Johannes Schaub - litb

1
... yang dicapai oleh tipu daya ini: stackoverflow.com/questions/650461/...
Johannes Schaub - litb

27

Masalah ini dapat diubah dengan SIMD untuk rangkaian karakter ASCII.


Perbandingan speedup:

Pengujian awal dengan x86-64 gcc 5.2 -O3 -march=nativepada 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 dengan LANG=Cdan dengan LANG=en_CA.UTF-8.

    Saya tidak menguji menggunakan RangeT selain std :: string. Mungkin bentuk lainto_upper_copy mengoptimalkan lebih baik, tapi saya pikir itu akan selalu new/ mallocruang 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 dari std::stringdan menulis hingga char dstbuf[4096]masuk akal untuk pengujian.

  • loop calling glibc toupper: 6.67s (tidak memeriksa inthasil untuk multi-byte UTF-8, meskipun. Ini penting untuk Turki.)

  • Satu-satunya ASCII loop: 8,79s (versi dasar saya untuk hasil di bawah ini.) Rupanya pencarian tabel lebih cepat daripada cmov, dengan tabel panas di L1.
  • Hanya vektor otomatis ASCII: 2,51s . (120 karakter adalah setengah jalan antara kasus terburuk dan terbaik, lihat di bawah)
  • ASCII-hanya vektor secara manual: 1.35s

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 -O3mengaktifkannya, 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. The perf record/ reporthasil (untuk cyclesacara perf) adalah:

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

Autovectorization

Gcc dan dentang hanya akan auto-vectorize loop ketika jumlah iterasi diketahui di depan loop. (mis. loop pencarian seperti implementasi plain-C strlentidak 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 strlenpertama. Ini tidak diperlukan untuk string dengan panjang eksplisit (seperti 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;
}

Setiap libc yang layak akan memiliki efisiensi strlenyang 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.

  • 15 char string: baseline: 1.08s. autovec: 1,34s
  • 16 char string: baseline: 1.16s. autovec: 1,52s
  • 127 char string: baseline: 8.91s. autovec: 2.98s // pembersihan non-vektor memiliki 15 karakter untuk diproses
  • 128 char string: baseline: 9.00s. autovec: 2.06s
  • 129 char string: baseline: 9.04s. autovec: 2.07s // pembersihan non-vektor memiliki 1 karakter untuk diproses

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 <= highdengan satu perbandingan yang tidak ditandatangani dengan menggeser rentang, sehingga nilai apa pun yang kurang dari lownilai yang lebih besar daripada nilai yang lebih besar high. (Ini berfungsi jika lowdan hightidak 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.

#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
}

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 srcselaras. Karena kita tidak tahu di mana 0 terminating akan berada, beban yang tidak selaras srcmungkin 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 .

// 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;
}

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.

  • 15 char string: baseline: 1.08s. autovec: 1,34s. manual: 1.29s
  • 16 char string: baseline: 1.16s. autovec: 1,52s. manual: 0,335s
  • 31 char string: manual: 0,479s
  • 127 char string: baseline: 8.91s. autovec: 2,98s. manual: 0,925s
  • 128 char string: baseline: 9.00s. autovec: 2.06s. manual: 0,931s
  • 129 char string: baseline: 9.04s. autovec: 2.07s. manual: 1.02s

(Sebenarnya diatur _mm_storedalam 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 movqatau movdmemuat / 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 dstpoint dapat memajukan dengan jumlah yang berbeda dari srcpointer, tetapi setelah kita kembali ke selaras srcpointer, kami akan tetap lakukan toko vektor unaligned ke dst.

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 dari toupper('i')adalah 'İ'(U0130), bukan 'I'(ASCII polos). Lihat komentar Martin Bonner tentang pertanyaan tentang tolower()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 PCMPISTRMatau sesuatu mungkin dapat melakukan banyak pemeriksaan kami dalam sekali jalan.


20

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


7
Atau eszet Jerman (sp?), Hal yang terlihat seperti huruf Yunani beta, dan berarti "ss". Tidak ada karakter Jerman tunggal yang berarti "SS", yang merupakan padanan huruf besar. Kata Jerman untuk "jalan", ketika huruf besar, mendapat satu karakter lebih lama.
David Thornley

6
Kasus khusus lainnya adalah huruf Yunani sigma (Σ), yang memiliki dua versi huruf kecil, tergantung pada apakah itu di akhir kata (ς) atau tidak (σ). Dan kemudian ada aturan khusus bahasa, seperti Turki memiliki pemetaan kasus I↔ı dan İ↔i.
dan04

1
"Uppercasing" disebut lipat case.
Columbo

20
string StringToUpper(string strToConvert)
{
   for (std::string::iterator p = strToConvert.begin(); strToConvert.end() != p; ++p)
       *p = toupper(*p);

   return p;
}

Atau,

string StringToUpper(string strToConvert)
{
    std::transform(strToConvert.begin(), strToConvert.end(), strToConvert.begin(), ::toupper);

    return strToConvert;
}

4
jika Anda tidak memiliki akses untuk meningkatkan solusi kedua mungkin adalah yang terbaik yang bisa Anda dapatkan. apa yang dilakukan bintang **setelah parameter pada solusi pertama?
Sam Brinck

1
Saya cukup yakin **ini adalah kesalahan ketik yang tersisa dari mencoba menggunakan font tebal dalam sintaksis kode.
MasterHD

1
Kode ini memunculkan perilaku tidak terdefinisi ketika toupperdipanggil dengan angka negatif.
Roland Illig

17

Berikut ini berfungsi untuk saya.

#include <algorithm>
void  toUpperCase(std::string& str)
{
    std::transform(str.begin(), str.end(), str.begin(), ::toupper);
}

int main()
{
   std::string str = "hello";
   toUpperCase(&str);
}

Perhatikan bahwa std :: transform didefinisikan dalam <algorithm>
edj

Iya. # ini diperlukan, #include <algorithm>
Pabitra Dash

1
Kode ini memunculkan perilaku tidak terdefinisi ketika toupperdipanggil dengan angka negatif.
Roland Illig

duplikat jawaban yang diberikan oleh pengguna648545 - -1
Piotr Dobrogost

@PiotrDobrogost Saya tidak tahu tentang jawaban yang diberikan oleh user648545. Saya belum menyalin itu. Ketika saya membandingkan dua metode metode tanda tangan berbeda sama sekali meskipun keduanya menggunakan fungsi transformasi fungsi perpustakaan.
Pabitra Dash

13

Gunakan lambda.

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);

2
Byron, jangan khawatir tentang komentar lainnya. Cukup oke untuk menjawab pertanyaan lama dengan solusi baru (modern) seperti yang Anda lakukan.
Kyberias

13

Yang lebih cepat jika Anda hanya menggunakan karakter ASCII :

for(i=0;str[i]!=0;i++)
  if(str[i]<='z' && str[i]>='a')
    str[i]-=32;

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


1
Pertanyaan ditandai sebagai C++, tetapi Anda menulis Cjawaban di sini. (Saya bukan salah satu dari para downvoter.)
hkBattousai

6
Saya menulis jawaban C DAN jawaban C ++ di sini karena C ++ ditulis agar sepenuhnya kompatibel dengan sumber C, sehingga setiap solusi C juga merupakan solusi C ++ yang benar
Luca C.

Tetapi jauh lebih baik untuk memberikan jawaban yang menghormati cara C ++.
Dmitriy Yurchenko

Cara standar c ++ adalah menggunakan std :: transform dengan toupper. Itu kode kurang dan pasti portabel. Kode ini mengandalkan "fakta" bahwa sistem akan menggunakan ascii sebagai mekanisme pengkodean karakter. Tidak yakin bahwa semua sistem didasarkan pada pengkodean ini dan karena itu tidak yakin bahwa ini portabel.
AlexTheo

1
Mengapa Anda memutuskan untuk menggunakan kode ASCII alih-alih karakter yang disertakan '?
HolyBlackCat

11

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:

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

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


Satu catatan bahwa jawaban ini untuk string ac daripada string std :: string
EvilTeach

Ini memiliki cacat keamanan bawaan yang jelas. Saya tidak akan melakukan ini.
Byron

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 () adalah tipe std :: size_t yang, AFAIK bisa menjadi unsigned int tergantung pada implementasinya
odinthenerd

Saya tidak berpikir ada implementasi modern di mana hasil dari std :: string :: size ditandatangani. Mengingat bahwa, baik secara semantik dan praktis, tidak ada yang namanya ukuran negatif, saya akan pergi dengan size_t menjadi setidaknya bilangan bulat 32-bit unsigned.
user1329482

Tidak ada alasan untuk tidak menulis 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.
Peter Cordes

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());

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:

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 );

tidak akan merekomendasikan back_inserter karena Anda sudah tahu panjangnya; gunakan std :: string result (src.size ()); std :: transform (src.begin (), src.end (), result.begin (), up_char);
Viktor Sehr

Meskipun saya yakin Anda tahu ini.
Viktor Sehr

@ Viktor Sehr, @bayda: Saya tahu ini sudah 2 tahun, tapi mengapa tidak mendapatkan yang terbaik dari kedua dunia. Gunakan reservedan back_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; }
Evan Teran

4
std::string value;
for (std::string::iterator p = value.begin(); value.end() != p; ++p)
    *p = toupper(*p);

Kode ini memunculkan perilaku tidak terdefinisi ketika toupperdipanggil dengan angka negatif.
Roland Illig

2

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 string


Saran ini memunculkan perilaku tidak terdefinisi ketika toupperdipanggil dengan angka negatif. Anda harus menyebutkan pemeran yang diperlukan unsigned char.
Roland Illig

2

Ini adalah kode terbaru dengan C ++ 11

std::string cmd = "Hello World";
for_each(cmd.begin(), cmd.end(), [](char& in){ in = ::toupper(in); });

Kode ini memunculkan perilaku tidak terdefinisi ketika toupperdipanggil dengan angka negatif.
Roland Illig

1

Menggunakan Boost.Text, yang akan berfungsi untuk teks 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();

1

The jawaban dari @dirkgently sangat menginspirasi, tapi saya ingin menekankan bahwa karena kekhawatiran seperti yang ditunjukkan di bawah ini,

Seperti semua fungsi lain dari, perilaku std :: toupper tidak terdefinisi jika nilai argumen tidak diwakili sebagai karakter yang tidak ditandatangani atau sama dengan EOF. Untuk menggunakan fungsi-fungsi ini dengan aman dengan karakter polos (atau karakter bertanda tangan), argumen pertama-tama harus dikonversi ke char yang tidak ditandatangani.
Referensi : std :: toupper

penggunaan yang benar std::toupperharus:

#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;
}

Keluaran:

Hello world!
HELLO WORLD!

0

tidak yakin ada fungsi bawaan. Coba ini:

Sertakan pustaka ctype.h ATAU cctype, serta stdlib.h sebagai bagian dari arahan preprocessor.

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 () bukan tipe 'unsigned int'
malat

Kode ini memunculkan perilaku tidak terdefinisi ketika toupperdipanggil dengan angka negatif.
Roland Illig

0

Solusi saya (menghapus bit ke-6 untuk alpha):

#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++;
    }
}

Kode ini memunculkan perilaku tidak terdefinisi ketika toupperdipanggil dengan angka negatif.
Roland Illig

Tidak ... Silakan periksa Anda benar sebelum downvoting. Islower hanya akan bekerja pada nilai-nilai non negatif ...
Antonin GAVREL

-1

SEMUA solusi di halaman ini lebih sulit daripada yang seharusnya.

Melakukan hal ini

RegName = "SomE StRing That you wAnt ConvErTed";
NameLength = RegName.Size();
for (int forLoop = 0; forLoop < NameLength; ++forLoop)
{
     RegName[forLoop] = tolower(RegName[forLoop]);
}

RegNameadalah Anda string. Dapatkan ukuran string Anda tidak digunakan string.size()sebagai tester Anda yang sebenarnya, sangat berantakan dan dapat menyebabkan masalah. kemudian. forloop 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


4
Saya tidak melihat bagaimana ini lebih sederhana daripada solusi boost :: toupper. Bisakah Anda menguraikan?
tr9sh

2
Sudah ada banyak tolowerloop sederhana , dan kebanyakan dari mereka menggunakan nama variabel loop standar seperti i, bukan yang aneh forLoop.
Peter Cordes

-1

Tanpa menggunakan perpustakaan apa pun:

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;
}

Kode di atas hanya berfungsi untuk penyandian yang kompatibel dengan ASCII. Baik pertanyaan maupun jawaban Anda tidak menyebutkan batasan ini. Salah satunya harus.
Roland Illig

-1

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.

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);
}

dengan use case:

std::transform(in.begin(),in.end(),out.begin(),tableToUpper);

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


-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

Fungsi c ++ ini selalu mengembalikan string huruf besar ...

#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;
}

-3

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.

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- area data apa yang tidak seharusnya Anda modifikasi?
user93353

3
Ini sudah terlambat, tapi apa yang ada di bumi? Garis gila itu bisa diganti dengan str[i] = toupper(str[i]);baik-baik saja (well, tidak sepenuhnya baik, tetapi memperbaiki sebagian besar hal yang salah).
chris
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.