Nasıl std :: string küçük harfe dönüştürmek için?


777

std::stringA'yı küçük harfe dönüştürmek istiyorum . İşlevin farkındayım tolower(), ancak geçmişte bu işlevle ilgili sorunlar yaşadım ve std::stringher karakterin yinelenmesini gerektireceği için zaten ideal değil .

Zamanın% 100'ünde çalışan bir alternatif var mı?


34
Başka bir şey listesinin her bir öğesini, listeyi yinelemeden başka bir şeye nasıl dönüştürürsünüz? Bir dize sadece bir karakter listesidir, eğer her karaktere bir işlev uygulamanız gerekiyorsa, dize boyunca yineleme yapmanız gerekir. Bunun hiçbir yolu yok.

14
Bu soru neden derecelendirmeyi tam olarak düşürüyor? Dize aracılığıyla yineleme ile ilgili bir sorunum yok, ama tolower (), toupper () vb. Dışında başka işlevler olup olmadığını soruyorum
Konrad

3
Bir C stili karakter dizisi varsa, o zaman 4 karakter küçük harf her seferinde küçük harf dönüştürmek için (hepsi zaten büyük harf olması koşuluyla) ox20202020 eklemek mümkün olabilir sanırım.

13
@ Dan: Zaten küçük harf olabilirler, ancak kesinlikle AZ veya az ise, eklemek yerine VEYA 0x20 ile yapabilirsiniz. Neredeyse hiç buna değmeyecek kadar akıllı-muhtemelen-aptal optimizasyonlardan biri ...
Steve Jessop

4
Neden aşağı oy verildiğini bilmiyorum ... kesinlikle biraz garip bir şekilde ifade edildi (çünkü bir şekilde her öğeyi tekrarlamanız gerekiyor), ama geçerli bir soru
warren

Yanıtlar:


905

Çok Sık Sorulan Sorular'dan Uyarlanmıştır :

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

Her karakteri tekrar etmeden gerçekten kaçamayacaksın. Karakterin küçük harf mi yoksa büyük harf mi olduğunu bilmenin bir yolu yoktur.

Gerçekten nefret ederseniz tolower(), işte kullanmanızı önermediğim yalnızca ASCII'ye özel bir alternatif:

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

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

Unutmayın tolower()çok bayt-kodlayan UTF-8 gibi kullanarak, özellikle sadece çok senaryo için uymayan bir başına tek bayt-karakteri ikame, yapabilirsiniz.


25
(Eski olabilir, söz konusu algoritmalar çok az değişti) @Stefan Mai: STL algoritmalarını çağırırken ne tür bir "tepegöz" var? İşlevler oldukça yalın (yani döngüler için basittir) ve genellikle aynı derleme biriminde aynı şablon parametreleriyle aynı işleve çok sayıda çağrınız olduğu için eğiktir.
eq-

257
Karakterlerin ASCII olduğunu her varsaydığınızda, Tanrı bir yavru kedi öldürür. :(
Brian Gordon

13
İlk örnek, potansiyel olarak sahip tanımsız davranış (geçen charüzere ::tolower(int).) Eğer negatif bir değer geçemiyor emin olmanız gerekir.
juanchopanza

37
-1 bu kullanımı ::toloweriyi çökebilir, ASCII olmayan giriş için UB.
Şerefe ve s. - Alf

7
::, en dıştaki ad alanında olduğunu belirtmek için hazırlayıcıdan önce gereklidir. Bu kodu başka bir ad alanında kullanırsanız, :: olmadan tercihli olarak seçilecek farklı bir (muhtemelen ilgisiz) tanımlayıcı tanımı olabilir.
Charles Ofria

320

Boost bunun için bir dize algoritması sağlar :

#include <boost/algorithm/string.hpp>

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

Veya yerinde olmayanlar için :

#include <boost/algorithm/string.hpp>

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

2
Bunun ASCII girişi ile tolower ile aynı sorunları olmadığını varsayalım?
paulm

19
ASCII-7 olmayanlar için başarısız.
DevSolar

1
Bunun yerinde olmayan bir sürümü var mı?
Ray

5
@Ray, evet,to_lower_copy
smac89

234

tl; Dr.

ICU kitaplığını kullanın . Bunu yapmazsanız, dönüşüm rutininiz muhtemelen varlığınızın farkında bile olmadığınız durumlarda sessizce bozulur.


Nedir: Önce bir soruya cevap vermek zorunda kodlama , aramalarınızdan std::string? ISO-8859-1 mi? Ya da belki ISO-8859-8? Veya Windows Codepage 1252? Büyük harfe küçük harfe dönüştürmek için ne kullandığınızı biliyor musunuz? (Yoksa karakterler bittiğinde perişan bir şekilde başarısız oluyor 0x7fmu?)

std::stringKapsayıcı olarak UTF-8 (8 bit kodlamalar arasında tek aklı başında seçim) kullanıyorsanız , zaten bir şeyleri kontrol ettiğinizi düşünmeye kendinizi aldatırsınız, çünkü bir kapta çok baytlı bir karakter dizisi saklıyorsunuz bu çokbaytlı kavramın farkında değil. .substr()Bir saatli bomba gibi basit bir şey bile . (Çünkü çok baytlı bir dizinin bölünmesi geçersiz (alt) bir dizeyle sonuçlanacaktır.)

Eğer böyle bir şey deneyin ve en kısa sürede std::toupper( 'ß' )de, herhangi bir kodlama, derin belada. (Çünkü bu "doğru" sadece burada gerekli değil, sadece bir sonuç karakteri verebilen standart kütüphane ile yapmak mümkün değildir "SS".) [1] Yerel ayara bağlı olarakstd::tolower( 'I' ) farklı sonuçlar vermesi gereken başka bir örnek olacaktır . Almanya'da doğru olur; Türkiye'de (LATIN KÜÇÜK MEKTUP DOTLESS I) beklenen sonuçtur (yine UTF-8 kodlamasında birden fazla bayttır). Yine başka bir örnek, Yunan Sigma , büyük harf , küçük harf ... bir kelimenin sonu hariç .'i''ı''∑''σ''ς'

Bu nedenle, bir seferde bir karakter üzerinde veya daha da kötüsü, bir seferde bir bayt üzerinde çalışan herhangi bir vaka dönüşümü tasarım tarafından kırılır.

Sonra standart kütüphanenin, yapabileceği şey için , yazılımınızın çalıştığı makinede hangi yerel ayarların desteklendiğine bağlı olduğu nokta var ... ve değilse ne yaparsınız?

Peki edilmektedir gerçekten aradığınız doğru bütün bunlarla başa çıkabilen bir dize sınıftır, ve olmasıdır değil herhangi birinin std::basic_string<>varyantları .

(C ++ 11 not: std::u16stringve std::u32stringdaha iyi , ama yine de mükemmel değil. C ++ 20 getirdi std::u8string, ancak bunların hepsi kodlama belirtmek olduğunu. Diğer birçok açıdan hala Unicode mekaniği, hala normalleştirme, harmanlama, gibi cahil kalır .. .)

Boost iken görünüyor akıllıca bir güzel, API, Boost.Locale temelde etrafında sarıcı YBÜ . Eğer Boost edilir derlenmiş yoğun bakım desteği ile Değilse ..., Boost.Locale standart kütüphane için derlenmiş yerel destek ile sınırlıdır.

Ve inan bana, ICU ile derlemek için Boost yapmak bazen gerçek bir acı olabilir. (Windows için önceden derlenmiş ikili dosyalar yoktur, bu nedenle bunları uygulamanızla birlikte sağlamanız gerekir ve bu da yepyeni bir solucan kutusu açar ...)

Şahsen tam at ağzından tam Unicode desteği almanızı ve doğrudan YBÜ kütüphanesini kullanmanızı tavsiye ederim :

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

Derleme (bu örnekte G ++ ile):

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

Bu şunları verir:

ὀδυσσεύς

Kelimenin ortasındaki Σ <-> σ dönüşümünün ve kelimenin sonundaki Σ <-> ς dönüşümüne dikkat edin. Hiçbir <algorithm>tabanlı çözüm size bunu veremez.


[1] 2017 yılında Alman Ortografya Konseyi "ẞ" U + 1E9E LATIN SERMAYE MEKTUBU SHARP S'nin pasaportlarda (adların büyük harfle yazıldığı yerlerde) belirsizliği önlemek için bir seçenek olarak resmi olarak kullanılabileceğine karar verdi. ). Komite kararıyla geçersiz kılınan güzel go-to örneğim ...


19
Genel durumda doğru cevap budur. Standart, yalanlar ve aldatma dışında "ASCII" dışında hiçbir şey için hiçbir şey vermez. Bu yapar düşünmek belki belki UTF-16 ile başa çıkabilirim ama bunu yapamazsınız. Bu cevabın dediği gibi, kendi unicode işlemeyi yapmadan UTF-16 dizesinin uygun karakter uzunluğunu (bayt uzunluğu değil) elde edemezsiniz. Gerçek metinle uğraşmak zorundaysanız, YBÜ kullanın. Teşekkürler, @DevSolar
Sınırlı Kefaret

ICU, Ubuntu / Windows'ta varsayılan olarak kullanılabilir mi veya ayrı olarak yüklenmesi mi gerekiyor? Ayrıca bu yanıta ne dersiniz: stackoverflow.com/a/35075839/207661 ?
Shital Shah

1
Hey, bak, gerçek bir cevap! Beni doğrudan doğruya yönlendirdiğiniz için teşekkürler DevSolar.
Dan Bechard

2
@DevSolar Kabul Edildi! Uzunluk kavramı metinde oldukça anlamsızdır (suçluların listesine bitişik harfler ekleyebiliriz). Bununla birlikte, insanlar bir uzunluk birimi alan sekmeleri ve karakterleri kontrol etmek için kullanıldığından, kod noktaları daha sezgisel bir ölçüm olacaktır. Oh, ve doğru cevabı verdiğiniz için teşekkürler, şimdiye kadar gördüğümüz için üzgünüm :-(
masaers

3
@LF Marjinal olarak daha iyi. Ama pek çok şey hala ele alınmıyor: toupperve tolowerhala tek karakterler üzerinde çalışıyor. String sınıfının hala normalleştirme fikri yoktur (örneğin, bir "ü" nin "diyaerez ile u" veya "u + bir araya diyaerezi" olarak kodlanıp kodlanmadığı) veya bir ipin ayrılıp ayrılmayacağı. Liste devam ediyor. u8string (diğer standart dize sınıfları gibi) "içinden geçmek" için uygundur. Ancak Unicode'u işlemek istiyorsanız ICU'ya ihtiyacınız var .
DevSolar

36

C ++ 11 döngüsü için aralık tabanlı kullanmak daha basit bir kod olacaktır:

#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
Ancak, bir Fransız makinesinde, bu program Fransızca dilinde izin verilen ASCII olmayan karakterleri dönüştürmez. Örneğin 'Test Dizesi123 dizesi. É Ï \ n 'şu biçime dönüştürülecektir:' test string123. É Ï \ n 'olmasına rağmen, É Ï karakterleri ve küçük harfli' é 've' ï 'karakterlerine izin verilir. Bu konunun diğer mesajları tarafından bunun için hiçbir çözüm sağlanmadığı anlaşılıyor.
incises

Bunun için uygun bir yerel ayar yapmanız gerektiğini düşünüyorum.
user1095108

@incises, bu daha sonra yoğun bakım hakkında bir cevap gönderdi ve bu kesinlikle gitmek için bir yol. Yerel ayarı anlamaya çalışan diğer birçok çözümden daha kolay.
Alexis Wilke

Mümkün olduğunca kişisel kütüphaneleri kullanmamayı tercih ederim.
kayleeFrye_onDeck


15

Bu Stefan Mai'nin yanıtının bir devamıdır: dönüşümün sonucunu başka bir dizeye yerleştirmek istiyorsanız, aramadan önce depolama alanını önceden ayırmanız gerekir std::transform. STL dönüştürülmüş karakterleri hedef yineleyicide sakladığından (döngünün her yinelemesinde artırılarak), hedef dize otomatik olarak yeniden boyutlandırılmaz ve belleğin durması riskiyle karşı karşıya kalırsınız.

#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
Bu benim için Ä içine yeniden boyutlandırmadı
Purefan

Manuel yeniden boyutlandırma yerine burada arka ekleyici yineleyici de kullanılabilir.
biber

11

Referans değişkenli döngü için aralık kullanan bir başka yaklaşım

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

cout<<test<<endl;

6

Gördüğüm kadarıyla, Boost kütüphaneleri performans açısından gerçekten kötü. Onların unordered_map STL test ettik ve ortalama 3 kat daha yavaş (en iyi durumda 2, en kötü 10 kez oldu). Ayrıca bu algoritma çok düşük görünüyor.

Aradaki fark o kadar büyük ki, emin olmak toloweriçin "ihtiyaçlarınız için" artırmak için yapmanız gereken ne olursa olsun , artırmaktan daha hızlı olacaktır eminim .

Bu testleri bir Amazon EC2'de yaptım, bu nedenle test sırasında performans değişti, ancak yine de fikri anladınız.

./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 şöyle yaptı:

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

Kaynak:

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

Sanırım özel bir makinedeki testleri yapmalıyım ama bu EC2'yi kullanacağım, bu yüzden makinemde gerçekten test etmeme gerek yok.


1
Derleme sırasında optimizasyon seçeneklerini açtınız mı? STL ağır artırma kütüphanesinin yüksek optimizasyon seviyesiyle daha iyi çalışması gerektiğini düşünüyorum.
Wei Song

1
Testlerden birinde -O2 kullandım ve başka bir şey kullanmadım.
Etherealone

2
Unordered_map'in performansı, kullandığınız verilerle birleştirilmiş karma algoritmaya bağlıdır. Unordered_map'i olabildiğince hızlı hale getirmek için tüm ve herhangi bir veri için çalışan sihirli bir karma algoritması yoktur. Kıyaslayın ve farklı şeyler deneyin. Daha kötü performans almanızın nedeni, kullandığınız karma ile çok sayıda çarpışma yaşamanızdır, bu da temel olarak listede arama yapılmasına neden olur. Daha fazla bilgi için bu siteye göz atın: fgda.pl/post/7/gcc-hash-map-vs-unordered-map Benim amacım için, bağlantıda sağlanan işlev çarpışmaları azalttı ve bu nedenle çok hızlı oldu.
leetNightshade

6

Std ad alanı hakkında rahatsız etmeden dizeyi loweercase'e dönüştürmenin en basit yolu aşağıdaki gibidir

1: boşluklu / boşluksuz dize

#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: boşluksuz dize

#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()standart C ++ Yerelleştirme kitaplığından bunu sizin için doğru şekilde yapar. İşte tolower referans sayfasından bir örnek

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

Güzel, sürece karakterleri dönüştürebilirsiniz. Kaynak dizeniz ise ne olacak const? f.tolower()Karakterleri yeni bir dizeye koymanız gerektiğinden, bu biraz daha dağınık görünüyor (örneğin, kullanabileceğiniz gibi görünmüyor ). Operatör için bir transform()şey ister misiniz std::bind1st( std::mem_fun() )?
quazar

Bir const dizesi için, yerel bir kopya oluşturabilir ve sonra onu yerinde dönüştürebiliriz.
Sameer

Evet, yine de, bir kopya yapmak daha fazla yük getirir.
quazar

İşaretçi almayan ctype :: tolower sürümüyle std :: transform kullanabilirsiniz. Bir arka yerleştirici yineleyici adaptörü kullanın ve çıkış dizenizi önceden boyutlandırma konusunda endişelenmenize bile gerek yok.
biber

Büyük, özellikle çünkü içinde libstdc ++ 'ın tolowersahip localeparametre, örtülü çağrı use_facetgörünmeden bir performans darboğazı olmak. İş arkadaşlarımdan biri, döngünün dışında bir kez çağrılan boost::iequalsbir sürümle değiştirerek (bu sorunu olan) birkaç% 100 hız artışı sağladı use_facet.
Arne Vogel

3

Boost'a bir alternatif POCO'dur (pocoproject.org).

POCO iki varyant sağlar:

  1. İlk varyant, orijinal dizgiyi değiştirmeden bir kopya oluşturur.
  2. İkinci varyant orijinal dizgiyi değiştirir.
    "Yerinde" sürümler her zaman adında "InPlace" içerir.

Her iki versiyon da aşağıda gösterilmiştir:

#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

Testleri OLMADAN büyük harfleri alta çevirmenin bir yolu vardır ve oldukça basittir. İsupper () işlevi / makronun clocale.h kullanımı, konumunuzla ilgili sorunlara dikkat etmelidir, ancak değilse, UtoL [] öğesini her zaman kalbinizin içeriğinde değiştirebilirsiniz.

C'nin karakterlerinin gerçekten sadece 8 bitlik bir sayı olduğu göz önüne alındığında (şu an için geniş karakter kümelerini göz ardı ederek) alternatif bir karakter kümesi tutan 256 baytlık bir dizi oluşturabilirsiniz ve dönüştürme işlevinde dizenizdeki karakterleri dönüşüm dizisi.

1'e 1 eşleme yerine, büyük harfli dizi üyelerine küçük harfli karakterler için BYTE int değerleri verin. Sen bulabilirsiniz islower () ve isupper () burada yararlıdır.

resim açıklamasını buraya girin

Kod şuna benzer ...

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

Bu yaklaşım, aynı zamanda, değiştirmek istediğiniz diğer karakterleri yeniden eşleştirmenize izin verir.

Modern işlemcilerle çalışırken bu yaklaşımın büyük bir avantajı vardır, dallanma içeren testler olmadığından dal tahmini yapmaya gerek yoktur. Bu, CPU'nun şube tahmin mantığını diğer döngüler için kaydeder ve boru hattı duraklarını önleme eğilimindedir.

Bazıları bu yaklaşımı EBCDIC'yi ASCII'ye dönüştürmek için kullanılanla aynı şekilde tanıyabilir.


2
Arama tabloları hiç duymadım "YAPMADAN YAPMADAN" büyük harf düşük yapmak için bir yolu var mı?
Gábor Buella

1
Negatif grafikler için tanımlanmamış davranış.
Roland Illig

Modern CPU'lar CPU'da değil bellekte tıkanır. Kıyaslama ilginç olurdu.
Contango

3

Yanıtların hiçbiri, C ++ 20'den beri standart kitaplıkta bulunan ve şu anda GitHub'da ayrı olarak bulunan yaklaşan Aralık kütüphanesinden bahsetmediğinden, range-v3bu dönüşümü kullanarak bunu gerçekleştirmek için bir yol eklemek istiyorum.

Dizeyi yerinde değiştirmek için:

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

Yeni bir dize oluşturmak için:

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

( #include <cctype>Gerekli Aralık aralıkları ve unutmayın .)

Not: lambda'yaunsigned char argüman olarak kullanımı cppreference'den esinlenmiştir , bu da şunları ifade eder:

Diğer tüm işlevler gibi , bağımsız değişkenin değeri ne kadar temsil edilebilir ne de ona eşitse <cctype>davranışı std::tolowertanımsızdır . Bu işlevleri düz s (veya s) ile güvenli bir şekilde kullanmak için , argüman önce şu biçime dönüştürülmelidir :unsigned charEOFcharsigned charunsigned char

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

Benzer şekilde, yineleyicinin değer türü charveya olduğunda standart algoritmalarla doğrudan kullanılmamalıdır signed char. Bunun yerine, değeri unsigned charönce olarak dönüştürün :

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

Büyük / küçük harf gerçekleştiren kendi şablon işlevlerim.

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

Buna ihtiyacım vardı. towlowerUTF-16'yı destekleyen geniş karakterler için kullandım .
Juv

2

Basit bir şey istiyorsanız bir makro tekniği:

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

Ancak, @ AndreasSpindler'in bu yanıtla ilgili yorumunun hala sadece ASCII karakterleri olmayan bir şey üzerinde çalışıyorsanız önemli bir husus olduğunu unutmayın.


1
Mükemmel bir çözüm olduğunda makro vermek için bunu küçümsüyorum - bu çözümleri bile veriyorsunuz.
Daha net

2
Makro tekniği, programlamada çokça kullanılan bir şey için daha az kod yazmak anlamına gelir. Neden kullanmıyorsunuz? Aksi halde, neden makrolarınız olur?
Volomike

3
Makrolar, kurtulmak için çok çalışılan C'nin mirasıdır. Yazma miktarını azaltmak istiyorsanız, bir işlev veya lambda kullanın. void strtoupper(std::string& x) { std::transform (x.begin(), x.end(), x.begin(), ::toupper); }
Daha net

1
@Clearer Daha iyi bir kodlayıcı olmak istediğim için, bana herhangi bir ANSI C ++ komitesinin "C ++ 'dan makrolardan kurtulmak için bir toplantı çağırmamız gerekiyor" etkisine bir şey söylediği herhangi bir ANSI doc bağlantısı sağlayabilir misiniz? Yoksa başka bir yol haritası mı?
Volomike

2
Hayır, yapamam. Bjarne'nin konu hakkındaki tutumu birkaç kez oldukça açıklığa kavuşturuldu. Ayrıca, C ++ ve C ++ 'da makro kullanmamanın birçok nedeni vardır. xgeçerli bir ifade olabilir, bu doğru bir şekilde derlenir, ancak makrolar nedeniyle tamamen sahte sonuçlar verir.
Daha net

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

Daha fazla bilgi için: http://www.cplusplus.com/reference/locale/tolower/


2

Zamanın% 100'ünde çalışan bir alternatif var mı?

Hayır

Bir küçük harf yöntemi seçmeden önce kendinize sormanız gereken birkaç soru vardır.

  1. Dize nasıl kodlanır? düz ASCII? UTF-8? genişletilmiş ASCII eski kodlama biçimi?
  2. Zaten küçük harfle ne demek istiyorsun? Vaka eşleme kuralları diller arasında değişiklik gösterir! Kullanıcının yerel ayarında yerelleştirilmiş bir şey mi istiyorsunuz? yazılımınızın üzerinde çalıştığı tüm sistemlerde tutarlı davranan bir şey ister misiniz? Sadece ASCII karakterlerini küçük harflerle yazmak ve diğer her şeyden geçmek istiyor musunuz?
  3. Hangi kütüphaneler mevcut?

Bu soruların yanıtlarını aldıktan sonra, ihtiyaçlarınıza uygun bir çözüm aramaya başlayabilirsiniz. Herkes için her yerde çalışan tek beden herkese uyar!


2

Bu işlevi deneyin :)

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

Microsoft platformlarında strlwrişlev ailesini kullanabilirsiniz : 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

Kod Parçacığı

#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

Cevabı iyileştirmek için izin verilmediğinden kopyalayın. Teşekkürler SO


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

Açıklama:

for(auto& c : test)tür döngü için bir aralık tabanlı :
for (range_declaration:range_expression)loop_statement

  1. range_declaration: auto& c
    Burada otomatik belirleyici , otomatik tipte kesinti için kullanılır. Böylece tür, değişken başlatıcıdan çıkarılır.

  2. range_expression: test
    Bu durumda aralık dizenin karakteridir test.

Dizenin karakterleri test, for döngüsü geçiş tanımlayıcısı içinde referans olarak kullanılabilir c.


Lütfen yanıtınızı nereden kopyaladığınızı açıklayın.
bfontaine

0

C ++ dize için daha düşük veya daha yüksek yöntemler uygulanmamış, ancak char için kullanılabilir. Bir dizenin her bir karakterini kolayca okuyabilir, gerekli duruma dönüştürebilir ve dizeye geri koyabilirsiniz. Herhangi bir üçüncü taraf kitaplığı kullanılmayan örnek kod:

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

Dizgede karakter temelli işlem için: Dizgideki her karakter için


-1

Bu, büyük harfleri küçük harfe dönüştürmek için basit bir sürüm olabilir. Bu kaynak kodunu derlemek için VS2017 topluluk sürümünü kullandım.

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

Not: özel karakterler varsa, durum kontrolü kullanılarak ele alınması gerekir.


-8

Ben std :: transform denedim, sadece i 200 yıl önce druidler anlayabiliyorum iğrenç stl criptic derleme hatası (flibidi flabidi grip dönüştürebilirsiniz)

bu iyi çalışıyor ve kolayca değiştirilebilir

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