C ++ 'da bir dizeyi büyük harfe dönüştürme


268

Bir dize nasıl büyük harfe dönüştürülebilir. Google'dan bulduğum örnekler yalnızca karakterlerle uğraşmak zorunda.

Yanıtlar:


205

Dize algoritmalarını artırın:

#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
Bu aynı zamanda ::toupperbüyük olasılıkla ASCII olduğu varsayılan i18n avantajına sahiptir .
Ben Straub

4
Son satırınız std::string newstr(boost::to_upper_copy<std::string>("Hello World"));
derlenmiyor

58
desteklenmesi gerektiğinden bu kabul edilen cevap olmamalı veya başlık değiştirilmelidir.
Andrea

44
evet, ben sadece to_upper için destek yükleyeceğim ... mükemmel bir fikir! </sarcasm> :)
thang

12
Ben kişisel olarak " C ++ x nasıl yapabilirim ?" çünkü takviye hiç de hafif bir çözüm değil. Bir çerçeve (veya ACE veya Qt veya Recusion ToolKit ++ veya ...) olarak güçlendirmeye satın aldığınız veya görmediğiniz anlaşılıyor. Dil çözümlerini görmeyi tercih ederim.
jwm

486
#include <algorithm>
#include <string>

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

8
Aslında, toupper()bir makro olarak uygulanabilir. Bu bir soruna neden olabilir.
dirkgently

3
boost.lambda ile bir bağlama (:: toupper, yapı <unsigned char> (_ 1)) bence mükemmel para cezası verecek.
Johannes Schaub - litb

11
Bu yaklaşım ASCII için iyi çalışır, ancak çok baytlı karakter kodlamaları veya Almanca 'ß' gibi özel kasa kuralları için başarısız olur.
dan04

9
Destek kitaplıklarını kullanarak verilen yanıtı değiştirdim, çünkü daha hızlı (gayri resmi testlerimde), kullanımı daha kolaydı ve bu çözümle ilgili sorunlara sahip değildi. Güçlendirmenin kullanılamadığı durumlar için hala iyi bir çözüm.
OrangeAlmondSoap

2
Neden derleyici önce bu kodu :: niteleyici reddetmek alamıyorum toupper. Herhangi bir fikir?
sasha.sochka

89

C ++ 11 ve toupper () kullanarak kısa çözüm.

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

c( const charTür auto) türünden olmaz mı? Öyleyse, ( constkısmen de olsa) tarafından döndürülen öğeye atayamazsınız toupper(c).
PolGraphic

5
@PolGraphic: Aralık tabanlı - kabın içeriğini yinelemek için kablonun begin () / end () yöntemlerini kullanır. std :: basic_string, hem const hem de mutable yineleyiciye sahiptir (sırasıyla cbegin () tarafından döndürülür ve begin (), bkz. std :: basic_string :: begin ), yani (:) str ise uygun olanı (cbegin () kullanır bildirilen const, auto =: = const char ile begin () aksi halde auto =: = char) ile.
Thanasis Papoutsidakis

5
Aşağıdaki dirkgently anser bakın, cbunun düzeltilmesi için dökülmesi gerekiyor unsigned char.
Cris Luengo

boost's to_upper (), c ++ STL fonksiyonları ile toupper'dan çok daha tutarlı görünüyor.
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());

Not: En iyi çözümle ilgili birkaç sorun:

21.5 Boş sonlandırılmış sıra yardımcı programları

Bu başlıkların içeriği <ctype.h>, <wctype.h>, <string.h>, <wchar.h> ve <stdlib.h> [...] Standart C Kütüphane başlıkları ile aynı olmalıdır.

  • Bu, cctypeüyelerin standart algoritmalarda doğrudan tüketime uygun olmayan makrolar olabileceği anlamına gelir .

  • Aynı örnekle ilgili bir başka sorun da argümanı yayınlamaması veya bunun negatif olmadığını doğrulamamasıdır; bu özellikle ova charimzalı sistemler için tehlikelidir . (Bunun nedeni: bu bir makro olarak uygulanırsa, büyük olasılıkla bir arama tablosu kullanır ve argümanınız bu tabloya dizinler. Negatif bir dizin size UB verecektir.)


Normal cctype üyeleri makrolardır. C90 standardının bir kopyasına sahip olmamam ve açıkça belirtilip belirtilmediğini bilmememe rağmen, işlevlerinin de olması gerektiğini okuduğumu hatırlıyorum.
David Thornley

1
C makroda olmalarına izin verseler bile C ++ 'da işlevler olmalıdırlar. Yine de döküm hakkındaki ikinci noktanıza katılıyorum. en üstteki çözüm negatif değerler iletebilir ve bununla UB'ye neden olabilir. bu yüzden oy vermedim (ama ben de oy vermedim) :)
Johannes Schaub - litb

1
standart alıntı eksik olmamalıdır: 7.4.2.2/1 (zayıf litb, sadece bir C99 TC2 taslağına atıfta bulunur) ve c ++ 17.4.1.2/6 zafer c ++ 98 standardında.
Johannes Schaub - litb

1
(buna dikkat edin: "Bu, maskeleme makrosu sağlamanın genel uygulamasına izin vermez .... filan blupp .. C ++ 'da bunu yapmanın tek yolu harici bir satır içi işlev sağlamaktır.") :)
Johannes Schaub - litb


27

Bu sorun ASCII karakter kümesi için SIMD ile vektörleştirilebilir .


Hızlandırma karşılaştırmaları:

-O3 -march=nativeBir Core2Duo (Merom) üzerinde x86-64 gcc 5.2 ile ön test . Aynı 120 karakter dizisi (karışık küçük harf ve küçük harf olmayan ASCII), 40M kez bir döngüye dönüştürülür (dosyalar arasında satır içi çizgi olmadan, derleyici döngüden herhangi birini optimize edemez veya kaldıramaz). Aynı kaynak ve hedef arabellekleri, böylece malloc ek yükü veya bellek / önbellek etkisi yok: veriler L1 önbellekte sürekli sıcaktır ve tamamen CPU'ya bağlıyız.

  • boost::to_upper_copy<char*, std::string>(): 198.0s . Evet, Ubuntu 15.10'da Boost 1.58 gerçekten çok yavaş. Bir hata ayıklayıcı asm profilli ve tek adım, ve bu gerçekten, gerçekten kötü: karakter başına bir yerel değişken değişken bir dynamic_cast var !!! (dynamic_cast, strcmp'ye birden çok çağrı alır). Bu LANG=Cve ile olur LANG=en_CA.UTF-8.

    Ben std :: string dışında bir RangeT kullanarak test etmedi. Belki diğer formto_upper_copy daha iyi optimize eder, ancak bence her zaman new/ mallockopya için alan olacak , bu yüzden test etmek daha zor. Belki yaptığım bir şey normal kullanım durumundan farklıdır ve belki normalde durdurulan g ++, yerel ayar öğelerini karakter başına döngüden kaldırabilir. Bir döngüden okumaya std::stringve yazmaya kadar olan char dstbuf[4096]döngüm test için mantıklı.

  • döngü çağrısı glibc toupper: 6.67s (yine intde potansiyel çok baytlık UTF-8 için sonucu kontrol etmiyor . Bu, Türkçe için önemli.)

  • Yalnızca ASCII döngüsü: 8.79s (aşağıdaki sonuçlar için temel sürümüm .) Görünüşe göre tablo araması,cmov , yine de tablo sıcak.
  • Yalnızca ASCII otomatik vektörize: 2.51s . (120 karakter en kötü durum ve en iyi durum arasında yarı yolda, aşağıya bakınız)
  • Yalnızca ASCII manuel olarak vektörleştirildi: 1.35s

Bir yerel ayar ayarlandığında Windows'ta yavaş olma ile ilgili bu soruyatoupper() da bakın .


Boost'un diğer seçeneklerden daha yavaş bir büyüklük sırası olduğu için şok oldum. -O3Etkinleştirdiğimi iki kez kontrol ettim ve hatta ne yaptığını görmek için asma tek adım attı. Clang ++ 3.8 ile neredeyse aynı hızda. Karakter başına döngü içinde büyük bir ek yüke sahiptir. perf record/ report(İçin sonuç cyclesperf etkinliği) aşağıdaki gibidir:

  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 ve clang döngüleri sadece döngüden önce yineleme sayısı bilindiğinde otomatik olarak vektörleştirir. (ör. düz-C uygulaması gibi arama döngüleri strlenotomatik vektörleştirilmez.)

Bu nedenle, önbelleğe sığacak kadar küçük dizeler için, strlenilk önce 128 128 karakter uzunluğunda dizeler için önemli bir hız kazanırız . Bu, açık uzunluklu dizeler için gerekli değildir (C ++ gibi 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;
}

Herhangi bir iyi libc verimli bir strlen , bir seferde bir baytı döngüye sokmaktan çok daha hızlı , bu nedenle ayrı vektörize strlen ve toupper döngüleri daha hızlıdır.

Temel: anında 0 sonlandırmasını kontrol eden bir döngü.

Core2 (Merom) 2.4GHz'de 40M yineleme zamanı. gcc 5.2 -O3 -march=native. (Ubuntu 15.10). dst != src(böylece bir kopya oluştururuz), ancak örtüşmezler (ve yakında değildir). Her ikisi de hizalanır.

  • 15 karakter dizesi: taban çizgisi: 1.08s. autovec: 1.34s
  • 16 karakter dizesi: taban çizgisi: 1.16s. autovec: 1.52s
  • 127 karakter dizesi: taban çizgisi: 8.91s. autovec: 2.98s // vektör olmayan temizleme işleminde 15 karakter var
  • 128 karakter dizesi: taban çizgisi: 9.00s. autovec: 2.06s
  • 129 karakter dizesi: taban çizgisi: 9.04s. autovec: 2.07s // vektör olmayan temizleme işleminde 1 karakter var

Bazı sonuçlar clang ile biraz farklıdır.

İşlevi çağıran mikrobenchmark döngüsü ayrı bir dosyadadır. Aksi takdirde, satır içi ve strlen()döngüden dışarı çıkar ve çok daha hızlı çalışır, esp. 16 karakter dizisi için (0.187s).

Bu, gcc'nin onu herhangi bir mimari için otomatik olarak vektörleştirebilmesi gibi büyük bir avantaja sahiptir, ancak genellikle yaygın olan küçük dizeler için daha yavaş olmasının en büyük dezavantajı.


Yani büyük hızlanmalar var, ancak derleyici otomatik vektörleştirme büyük kod yapmıyor, esp. son 15 karaktere kadar temizlik için.

SSE intrinsikleri ile manuel vektörleştirme:

Benim dayanarak harf çevirme fonksiyonu her alfabetik karakterin halinde tersine çevirir. low < a && a <= highAralık kayması ile tek bir imzasız karşılaştırma ile yapabileceğiniz "işaretsiz karşılaştırma hilesi" avantajından yararlanır , böylece daha küçük herhangi lowbir değer, daha büyük bir değere sarılır high. (Bu çok uzak değilse lowve highçok uzak değilse işe yarar .)

SSE'nin yalnızca işaretli bir karşılaştırması daha büyüktür, ancak yine de işaretsiz aralığın altına kaydırılarak "işaretsiz karşılaştırma" hilesini kullanabiliriz: 'a' + 128'i çıkarın, böylece alfabetik karakterler -128 ile -128 arasında değişir +25 (-128 + 'z' - 'a')

128 ekleme ve 128 çıkarma işleminin 8 bit tamsayılar için aynı şey olduğunu unutmayın. Taşıma için gidecek yer yok, bu yüzden sadece xor (taşımasız ekleme), yüksek biti çeviriyor.

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

Bir vektör için çalışan bu işlev göz önüne alındığında, bir dizenin tamamını işlemek için bir döngüde çağırabiliriz. Zaten SSE2'yi hedeflediğimiz için, aynı zamanda vektörize bir dize sonu kontrolü yapabiliriz.

Ayrıca, 16B vektörleri yaptıktan sonra kalan son 15 baytın "temizlenmesi" için çok daha iyi yapabiliriz: üst kasa idempotenttir, bu nedenle bazı giriş baytlarının yeniden işlenmesi iyidir. Kaynağın son 16B'sinin hizasız bir yükünü yapıyoruz ve bunu döngüden son 16B deposuyla örtüşen dest tamponuna kaydediyoruz.

Bu işe yaramayan tek zaman, tüm dize 16B'nin altında olduğunda: dst=srcAtomik olmayan okuma-değiştirme-yazma özelliği olmasa bile tüm bazı bayt dokunmadan değil aynı şey ve çok iş parçacıklı kodunu bozabilir.

Bunun için bir skaler döngü var ve aynı zamanda srchizalanmak için. Sonlandırma 0'ın nerede olacağını bilmediğimizden, hizalanmamış bir yük srcsonraki sayfaya ve segfault'a geçebilir. Hizalanmış bir 16B yığınında herhangi bir bayta ihtiyaç duyarsak, tüm hizalanmış 16B yığınını yüklemek her zaman güvenlidir.

Tam kaynak: bir github özünde .

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

Core2 (Merom) 2.4GHz'de 40M yineleme zamanı. gcc 5.2 -O3 -march=native. (Ubuntu 15.10). dst != src(böylece bir kopya oluştururuz), ancak örtüşmezler (ve yakında değildir). Her ikisi de hizalanır.

  • 15 karakter dizesi: taban çizgisi: 1.08s. autovec: 1.34s. manuel: 1.29s
  • 16 karakter dizesi: taban çizgisi: 1.16s. autovec: 1.52s. manuel: 0.335s
  • 31 karakter dizesi: manuel: 0.479s
  • 127 karakter dizesi: taban çizgisi: 8.91s. autovec: 2.98s. manuel: 0.925s
  • 128 karakter dizesi: taban çizgisi: 9.00s. autovec: 2.06s. manuel: 0.931s
  • 129 karakter dizesi: taban çizgisi: 9.04s. autovec: 2.07s. manuel: 1.02s

(Aslında adrese göre ayarlanmış olsa bile storeu Merom'da daha yavaş olduğu _mm_storeiçin döngüde zamanlanmış _mm_storeu. Nehalem ve daha sonra iyi. Kod kopyalama hatasını düzeltmek yerine şimdilik olduğu gibi bıraktım bazı durumlarda sonlandırma 0, çünkü her şeyi yeniden zamanlamak istemiyorum.)

Bu nedenle, 16B'den uzun kısa dizeler için bu, otomatik vektörleştirmekten çok daha hızlıdır. Bir vektör genişliğinden daha küçük uzunluklar sorun oluşturmaz. Mağazaya yönelik bir durak nedeniyle yerinde çalışırken bir sorun olabilir. (Ancak, toupper idempotent olduğundan, orijinal girdiden ziyade kendi çıktımızı işlemenin hala iyi olduğunu unutmayın).

Çevredeki kodun ne istediğine ve hedef mikro mimariye bağlı olarak bunu farklı kullanım durumları için ayarlamak için çok fazla alan vardır. Derleyicinin temizleme kısmı için güzel kod yayınlaması zor. Kullanmak ffs(3)(hangi bsf veya tzcnt için x86 üzerinde derler) iyi gibi görünüyor, ama bu cevabın çoğunu yazdıktan sonra bir hata fark beri açıkçası bu biraz yeniden düşünmek gerekiyor (FIXME yorumlarına bakın).

Daha küçük dizeler için vektör hızlandırmaları movqveya movdyükler / depolar ile elde edilebilir . Kullanım durumunuz için gerektiği şekilde özelleştirin.


UTF-8:

Vektörünüzün yüksek bit setiyle herhangi bir bayt içerdiğini tespit edebiliriz ve bu durumda o vektör için skaler utf-8-farkında bir döngüye geri döneriz. dstNokta farklı bir miktarda ilerletebilir srcpointer, ama biz geri bir hizalanmış gidince srcpointer, hala sadece karşı unaligned vektör depolarını yapacağız dst.

UTF-8 olan ancak çoğunlukla UTF-8'in ASCII alt kümesinden oluşan metin için, bu iyi olabilir: tüm durumlarda doğru davranışla ortak durumda yüksek performans. ASCII olmayan bir sürü olduğunda, muhtemelen her zaman skaler UTF-8 farkında döngüde kalmaktan daha kötü olacaktır.

Dezavantajı önemliyse, İngilizceyi diğer diller pahasına daha hızlı yapmak geleceğe dönük bir karar değildir.


Yerel ayara duyarlı:

Türk yerelinde ( tr_TR), doğru sonuç toupper('i')ise 'İ'(U0130) değil, 'I'(ASCII). Martin Bonner'ıntolower() Windows'ta yavaş olma hakkındaki bir soruya bakın .

Ayrıca, çok baytlık UTF8 giriş karakterleri gibi, bir istisna listesi ve skalerlere geri dönüş olup olmadığını kontrol edebiliriz.

Bu kadar karmaşıklıkla, SSE4.2 PCMPISTRMya da bir şey tek seferde birçok kontrolümüzü yapabilir.


20

Dizelerde ASCII veya Uluslararası karakterler var mı?

Eğer ikinci durum söz konusuysa, "büyük harf" o kadar basit değildir ve kullanılan alfabeye bağlıdır. Bikameral ve tek kamaralı alfabe vardır. Sadece bikameral alfabelerde büyük ve küçük harfler için farklı karakterler bulunur. Ayrıca, başlık büyük / küçük harfini kullanan Latince büyük harf 'DZ' (\ u01F1 'DZ') gibi kompozit karakterler de vardır . Bu, yalnızca ilk karakterin (D) değiştiği anlamına gelir.

YBÜ'ye bakmanızı ve Basit ve Tam Vaka Eşlemeleri arasındaki farkı öneririm . Bu yardımcı olabilir:

http://userguide.icu-project.org/transforms/casemappings


7
Ya da Yunan harf beta'ya benzeyen ve "ss" anlamına gelen Alman eszet (sp?). Büyük harf eşdeğeri olan "SS" anlamına gelen tek bir Alman karakteri yoktur. Almanca "sokak" kelimesi, büyük harflerle yazıldığında bir karakter daha uzar.
David Thornley

6
Başka bir özel durum, bir kelimenin sonunda (ς) olup olmamasına bağlı olarak (σ) iki küçük harf içeren Yunanca sigma (Σ) harfidir. Ve sonra Türkçenin I↔ı ve İ↔i vaka eşlemesine sahip olması gibi dile özgü kurallar vardır.
dan04

1
"Uppercasing", kasa katlama olarak adlandırılır.
Columbo

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

   return p;
}

Veya,

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

    return strToConvert;
}

4
ikinci çözümü artırmak için erişiminiz yoksa, muhtemelen elde edebileceğiniz en iyisidir. **İlk çözümdeki parametrelerden sonra yıldızlar ne yapar?
Sam Brinck

1
Eminim **bir yazım hatası kod sözdiziminde kalın yazı tipini kullanmaya çalışıyorum.
MasterHD

1
toupperNegatif sayılarla çağrıldığında bu kod tanımsız davranışı başlatır .
Roland Illig

17

Aşağıdakiler benim için çalışıyor.

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

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

Std :: transform'un <algoritma>
edj

Evet. bu # içerme gereklidir, #include <algoritma>
Pabitra Dash

1
toupperNegatif sayılarla çağrıldığında bu kod tanımsız davranışı başlatır .
Roland Illig

user648545 - -1
Piotr Dobrogost

@PiotrDobrogost user648545 tarafından verilen cevap hakkında hiçbir fikrim yok. İki yöntem karşılaştırdığımda, her iki işlev de kitaplık işlev dönüşümünü kullanmasına rağmen yöntem imzası tamamen farklı.
Pabitra Dash

13

Bir lambda kullanın.

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, diğer yorumlar için endişelenme. Eski soruları yaptığınız gibi yeni (modern) çözümle cevaplamak oldukça iyidir.
Kyberias

13

Yalnızca ASCII karakterlerini kullanırsanız daha hızlı olanı :

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

Bu kodun daha hızlı çalıştığını, ancak yalnızca ASCII üzerinde çalıştığını ve "soyut" bir çözüm olmadığını lütfen unutmayın .

UNICODE çözümlerine veya daha geleneksel ve soyut çözümlere ihtiyacınız varsa, diğer yanıtları alın ve C ++ dizeleri yöntemleriyle çalışın.


1
Soru şu şekilde etiketlenmiştir C++, ancak Cburaya bir cevap yazdınız. (Ben downvoters değilim.)
hkBattousai

6
Ben bir C cevabı yazdım ve burada bir C ++ cevabı yazdım çünkü C ++ C kaynaklarıyla tamamen uyumlu olacak şekilde yazılmıştır, bu yüzden herhangi bir C çözümü de C ++ doğru bir çözümdür
Luca C.

Ancak C ++ yoluna saygılı bir cevap vermek çok daha iyidir.
Dmitriy Yurchenko

Standart c ++ yolu, todper ile std :: transform kullanmaktır. Bu daha az kod ve kesinlikle taşınabilir. Bu kod, sistemin karakter kodlama mekanizması olarak ascii'yi kullanacağı "gerçeğe" dayanır. Tüm sistemlerin bu kodlamaya dayandığından ve bu nedenle taşınabilir olduğundan emin değilsiniz.
AlexTheo

1
Neden ekteki karakterler yerine ASCII kodlarını kullanmaya karar verdiniz '?
HolyBlackCat

11

Yalnızca ASCII ile iyi olduğunuz ve RW belleğine geçerli bir işaretçi sağlayabildiğiniz sürece, C'de basit ve çok etkili bir astar vardır:

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

Bu, özellikle aynı karakter durumuna normalleştirmek istediğiniz ASCII tanımlayıcıları gibi basit dizeler için iyidir. Ardından std: string örneği oluşturmak için buffer'ı kullanabilirsiniz.


Bir cevap bu cevap bir std :: string yerine ac string içindir
EvilTeach

Bu bariz bir doğal güvenlik açığına sahiptir. Bunu yapmam.
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 (), std :: size_t türünde olup, uygulamaya bağlı olarak AFAIK çok iyi imzasız olabilir
odinthenerd

Ben std :: string :: size sonucu imzalanmış herhangi bir modern uygulamalar olduğunu sanmıyorum. Semantik ve pratik olarak, negatif bir boyut diye bir şey olmadığı göz önüne alındığında, size_t en az 32 bit işaretsiz bir tam sayı olacak şekilde gideceğim.
user1329482

Yazmamak için hiçbir sebep yok for (size_t i = 0 .... Okumayı zorlaştırmak için iyi bir neden de yok. Bu ayrıca önce dizeyi kopyalar ve sonra döngüyü kopyalar. @ Luke'un yanıtı, 'a'karakter sabitlerinden faydalanmamak dışında, bazı açılardan daha iyidir .
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());

Bu, global toupper işlevini kullanan tüm cevaplardan daha iyi performans gösterecektir ve muhtemelen boost :: to_upper'ın altında yaptığı şeydir.

Bunun nedeni :: toupper'ın yerel ayarı araması gerektiğidir - çünkü farklı bir iş parçacığı tarafından değiştirilmiş olabilir - her çağrı için, burada yalnızca locale () çağrısı bu cezaya sahiptir. Ve yerele bakmak genellikle bir kilit almayı içerir.

Bu, otomatik olarak değiştirildikten, yeni const olmayan str.data () yöntemini kullandıktan ve şablon kapanmasını (">>" - ">>") bu şekilde kırmak için bir boşluk ekledikten sonra C ++ 98 ile de çalışır:

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

zaten uzunluğu bildiğiniz gibi bir back_inserter tavsiye etmem; std :: string sonucunu kullanın (src.size ()); std :: transform (src.begin (), src.end (), sonuç.begin (), up_char);
Viktor Sehr

Bunu bildiğine eminim.
Viktor Sehr

@Viktor Sehr, @bayda: Bunun 2 yaşında olduğunu biliyorum, ama neden her iki dünyanın en iyisini elde edemiyoruz. Kullanım reserveve back_inserter(dizi sadece bir kez kopyalanır böylece yapma). 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);

toupperNegatif sayılarla çağrıldığında bu kod tanımsız davranışı başlatır .
Roland Illig

2

toupper()( #include <ctype.h>) işlevini deneyin . karakterleri bağımsız değişken olarak kabul eder, dizeler karakterlerden oluşur, bu nedenle bir araya getirildiğinde dizeyi oluşturan her bir karakter üzerinde yineleme yapmanız gerekir


Bu öneri toupper, negatif sayılarla çağrıldığında tanımlanmamış davranışı başlatır . Gerekli oyuncu kadrosundan bahsetmeliydin unsigned char.
Roland Illig

2

İşte C ++ 11 ile en son kod

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

toupperNegatif sayılarla çağrıldığında bu kod tanımsız davranışı başlatır .
Roland Illig

1

Unicode metin için çalışacak Boost.Text özelliğini kullanma

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

Cevap ait @dirkgently çok ilham veriyor ama aşağıda gösterildiği gibi bağlı endişe vurgulamak istiyoruz

Diğer tüm işlevler gibi, bağımsız değişkenin değeri işaretsiz karakter olarak temsil edilemez veya EOF'a eşit değilse std :: toupper davranışı tanımlanmamıştır. Bu işlevleri düz karakterlerle (veya imzalı karakterlerle) güvenle kullanmak için, bağımsız değişken önce işaretsiz karaktere dönüştürülmelidir
Reference : std :: toupper

doğru kullanımı std::toupper:

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

Çıktı:

Hello world!
HELLO WORLD!

0

yerleşik bir fonksiyon olduğundan emin değilim. Bunu dene:

Önişlemci yönergelerinin bir parçası olarak ctype.h OR cctype kitaplıklarının yanı sıra stdlib.h dosyasını da ekleyin.

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 () 'unsigned int' türünde değil
malat

toupperNegatif sayılarla çağrıldığında bu kod tanımsız davranışı başlatır .
Roland Illig

0

Benim çözümüm (alfa için 6. biti temizleme):

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

toupperNegatif sayılarla çağrıldığında bu kod tanımsız davranışı başlatır .
Roland Illig

Hayır ... Lütfen indirmeden hemen önce olduğunuzu kontrol edin. Islower sadece negatif olmayan değerler üzerinde çalışacaktı ...
Antonin GAVREL

-1

Bu sayfadaki TÜM çözümlerin TÜMÜ olması gerekenden daha zordur.

Bunu yap

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

RegNamesenin string. Dize boyutunuzu alın, string.size()gerçek test cihazınız olarak kullanmayın , çok dağınık ve sorunlara neden olabilir. sonra. en temel fordöngü.

unutmayın string boyutu sınırlayıcıyı da döndürür, bu yüzden döngü testinizde <= değil <= kullanın.

çıktı: dönüştürülmesini istediğiniz bazı dize


4
Bunun boost :: toupper çözümünden nasıl daha basit olduğunu görmüyorum. Detaylandırabilir misin?
tr9sh

2
Zaten çok sayıda basit tolowerdöngü var ve bunların çoğu igarip değil, standart döngü değişken adları kullanıyor forLoop.
Peter Cordes

-1

Herhangi bir kütüphane kullanmadan:

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

Yukarıdaki kod yalnızca ASCII uyumlu kodlamalar için çalışır. Cevabınızdaki soru da bu kısıtlamadan bahsetmiyor. Onlardan biri olmalı.
Roland Illig

-1

Sadece 8 bit karakterlerle ilgileniyorsanız (Milan Babuškov hariç tüm diğer cevaplar da varsayar) derleme zamanında metaprogramlama kullanarak bir arama tablosu oluşturarak en yüksek hızı elde edebilirsiniz. İdeone.com'da bu, kütüphane işlevinden 7 kat, elle yazılmış bir sürümden 3 kat daha hızlı çalışır ( http://ideone.com/sb1Rup ). Ayrıca, yavaşlama olmadan özellikler ile özelleştirilebilir.

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

kullanım durumda:

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

Nasıl çalıştığına dair derinlemesine (birçok sayfa) bir açıklama için blogumu utanmadan takmama izin verin: 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

Bu c ++ işlevi her zaman büyük harf dizesini döndürür ...

#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

Bu çözümü kullanıyorum. Bu veri alanını değiştirmek zorunda değilsiniz biliyorum .... ama bence çoğunlukla tampon taşması hatalar ve boş karakter .... üst kasa şeyler aynı değil.

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- hangi veri alanını değiştirmemeniz gerekiyor?
user93353

3
Geç oldu, peki ya dünyada? Bu çılgın çizgi str[i] = toupper(str[i]);mükemmel bir şekilde değiştirilebilir (iyi, mükemmel değil , ama çoğu şeyi yanlış düzeltir).
chris
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.