วิธีการแปลง std :: string เป็นตัวเล็ก?


777

ฉันต้องการแปลง a เป็นstd::stringตัวพิมพ์เล็ก ฉันรู้ถึงฟังก์ชั่นtolower()นี้ แต่ในอดีตที่ฉันเคยมีปัญหากับฟังก์ชั่นนี้และมันก็แทบจะไม่เหมาะอย่างยิ่งถ้าใช้กับ a std::stringจะต้องวนซ้ำตัวละครแต่ละตัว

มีทางเลือกอื่นที่ใช้งานได้ 100% หรือไม่


34
คุณจะแปลงองค์ประกอบของรายการอะไรเป็นอย่างอื่นโดยไม่ต้องวนซ้ำทุกรายการ สตริงเป็นเพียงรายการของตัวละครหากคุณต้องการใช้ฟังก์ชั่นบางอย่างกับตัวละครแต่ละตัวคุณจะต้องทำซ้ำผ่านสตริง ไม่มีทางรอบนั้น

14
ทำไมคำถามนี้ถึงให้เรตติ้ง? ฉันไม่มีปัญหากับการวนซ้ำผ่านสายอักขระของฉัน แต่ฉันกำลังถามว่ามีฟังก์ชั่นอื่น ๆ นอกเหนือจาก tolower (), toupper () ฯลฯ หรือไม่
Konrad

3
หากคุณมีอาร์เรย์ถ่านสไตล์ C ฉันคิดว่าคุณสามารถเพิ่ม ox20202020 ให้กับแต่ละบล็อกของตัวละคร 4 ตัว (หากพวกเขาเป็นตัวพิมพ์ใหญ่ทั้งหมดแล้ว) เพื่อแปลง 4 ตัวอักษรให้เป็นตัวพิมพ์เล็กในแต่ละครั้ง

13
@ ด่าน: หากพวกเขาอาจเป็นตัวพิมพ์เล็กอยู่แล้ว แต่เป็น AZ หรือ az แน่นอนคุณสามารถหรือด้วย 0x20 แทนที่จะเพิ่ม optimisations หนึ่งในนั้นที่สมาร์ท it's-อาจใบ้ที่เกือบจะไม่คุ้มค่า ...
สตีฟเจสซอพ

4
ฉันไม่รู้ว่าทำไมมันถึงได้ถูกโหวต ... แน่นอนว่ามันพูดแปลก ๆ นิดหน่อย (เพราะคุณต้องทำซ้ำทุก ๆ ไอเทม) แต่มันเป็นคำถามที่ถูกต้อง
วอร์เรน

คำตอบ:


905

ที่ดัดแปลงมาจากคำถามที่พบไม่บ่อย :

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

คุณจะไม่หนีไปโดยไม่ทำซ้ำตัวละครแต่ละตัว ไม่มีทางที่จะรู้ได้ว่าตัวละครนั้นเป็นตัวพิมพ์เล็กหรือตัวพิมพ์ใหญ่

หากคุณเกลียดจริง ๆtolower()นี่เป็นทางเลือกเฉพาะ ASCII ที่ฉันไม่แนะนำให้คุณใช้:

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

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

โปรดทราบว่าtolower()สามารถทำการทดแทนอักขระต่อไบต์เดียวซึ่งไม่เหมาะสมสำหรับสคริปต์จำนวนมากโดยเฉพาะอย่างยิ่งหากใช้การเข้ารหัสหลายไบต์เช่น UTF-8


25
(เก่าอาจเป็นไปได้ว่าอัลกอริทึมที่เป็นปัญหาเปลี่ยนไปเล็กน้อย) @Stefan Mai: "โอเวอร์เฮดทั้งจำนวนมาก" ในการเรียกอัลกอริธึม STL แบบไหน? ฟังก์ชั่นค่อนข้างเอนตัว (เช่นง่ายสำหรับลูป) และมักจะอินไลน์เนื่องจากคุณไม่ค่อยมีการเรียกไปยังฟังก์ชั่นเดียวกันจำนวนมากที่มีพารามิเตอร์เทมเพลตเดียวกันในหน่วยคอมไพล์เดียวกัน
eq-

257
ทุกครั้งที่คุณคิดว่าตัวละครเป็น ASCII พระเจ้าจะฆ่าลูกแมว :(
Brian Gordon

13
ตัวอย่างแรกของคุณอาจมีพฤติกรรมที่ไม่ได้กำหนด (ผ่านcharไป::tolower(int).) คุณต้องให้แน่ใจว่าคุณไม่ผ่านค่าลบ
juanchopanza

37
-1 การใช้งาน::tolowerอาจผิดพลาดเป็น UB สำหรับอินพุตที่ไม่ใช่ ASCII
ไชโยและ hth - Alf

7
กระบวนการ :: เป็นสิ่งจำเป็นก่อน tolower เพื่อบ่งชี้ว่ามันอยู่ใน namespace นอกสุด หากคุณใช้รหัสนี้ในเนมสเปซอื่นอาจมีข้อกำหนดที่แตกต่างกัน (อาจไม่เกี่ยวข้อง) ของ tolower ซึ่งจะเป็นการเลือกที่ดีกว่าโดยไม่ต้อง ::
Charles Ofria

320

Boost ให้อัลกอริทึมสตริงสำหรับสิ่งนี้ :

#include <boost/algorithm/string.hpp>

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

หรือสำหรับผู้ที่ไม่ได้อยู่ในสถานที่ :

#include <boost/algorithm/string.hpp>

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

2
ฉันคิดว่าสิ่งนี้ไม่ได้มีปัญหาเดียวกันกับ tolower ที่มีอินพุต ASCII หรือไม่
paulm

19
ล้มเหลวสำหรับ non-ASCII-7
DevSolar

1
มีรุ่นที่ไม่ใช้แทนหรือไม่?
เรย์

5
@ เรย์ใช่to_lower_copy
smac89

234

TL; DR

ใช้ห้องสมุดห้องไอซียู หากคุณไม่ทำขั้นตอนการแปลงของคุณจะหยุดลงอย่างเงียบ ๆ ในบางกรณีที่คุณอาจไม่ทราบด้วยซ้ำว่ามีอยู่จริง


ก่อนอื่นคุณต้องตอบคำถาม: การเข้ารหัสของคุณstd::stringคืออะไร? เป็น ISO-8859-1 หรือไม่ หรือบางที ISO-8859-8 หรือ Windows Codepage 1252 สิ่งที่คุณใช้ในการแปลงบนเป็นตัวเล็กรู้หรือไม่ (หรือมันล้มเหลวอย่างน่าสมเพชสำหรับตัวละครมากกว่า0x7f?)

หากคุณใช้ UTF-8 (ตัวเลือกที่มีเหตุผลเพียงอย่างเดียวในการเข้ารหัส 8 บิต) พร้อมกับstd::stringคอนเทนเนอร์คุณกำลังหลอกตัวเองให้เชื่อว่าคุณยังควบคุมสิ่งต่าง ๆ ได้เนื่องจากคุณกำลังเก็บลำดับอักขระหลายไบต์ในคอนเทนเนอร์ ที่ไม่ทราบถึงแนวคิดของมัลติไบต์ แม้แต่บางสิ่งที่เรียบง่ายเช่นเดียวกับ.substr()ช่วงเวลาที่ฟ้องร้อง (เนื่องจากการแยกลำดับหลายไบต์จะส่งผลให้เกิดสตริงที่ไม่ถูกต้อง (sub-))

และเร็วที่สุดเท่าที่คุณจะลองสิ่งที่ต้องการstd::toupper( 'ß' )ในใด ๆเข้ารหัสคุณอยู่ในปัญหาลึก (เพราะมันก็ไม่ได้ที่จะทำเช่นนี้ "สิทธิ" กับห้องสมุดมาตรฐานซึ่งสามารถส่งมอบหนึ่งในตัวละครที่ผลไม่"SS"จำเป็นที่นี่.) [1] อีกตัวอย่างหนึ่งจะstd::tolower( 'I' )ซึ่งจะให้ผลลัพธ์ที่แตกต่างกันขึ้นอยู่กับสถานที่เกิดเหตุ ในประเทศเยอรมนี'i'จะถูกต้อง; ในตุรกี'ı'(LATIN เล็กตัวอักษร DOTLESS I) เป็นผลลัพธ์ที่คาดหวัง (ซึ่งอีกครั้งคือมากกว่าหนึ่งไบต์ในการเข้ารหัส UTF-8) อีกตัวอย่างหนึ่งคือกรีกSigma , พิมพ์ใหญ่'∑'พิมพ์เล็ก'σ'... 'ς'ยกเว้นตอนท้ายของคำที่ว่ามันอยู่ที่ไหน

ดังนั้นการแปลงกรณีใด ๆที่ทำงานกับตัวละครในแต่ละครั้งหรือแย่กว่านั้นเป็นไบต์ในแต่ละครั้งจะถูกทำลายโดยการออกแบบ

จากนั้นก็มีจุดที่ห้องสมุดมาตรฐานสำหรับสิ่งที่มันเป็นความสามารถในการทำขึ้นอยู่กับสถานที่ซึ่งได้รับการสนับสนุนบนเครื่องซอฟต์แวร์ของคุณกำลังทำงานอยู่บน ... และสิ่งที่คุณทำถ้ามันไม่ได้?

ดังนั้นสิ่งที่คุณกำลังจริงๆมองหาเป็นชั้นสตริงที่มีความสามารถในการจัดการกับทั้งหมดนี้ได้อย่างถูกต้องและนั่นคือไม่ได้ใด ๆ ของstd::basic_string<>สายพันธุ์

(หมายเหตุ C ++ 11: std::u16stringและstd::u32stringยังดีกว่าแต่ก็ยังไม่สมบูรณ์แบบ C ++ 20 ที่นำมาstd::u8stringแต่สิ่งเหล่านี้จะระบุการเข้ารหัสในส่วนอื่น ๆ อีกมากมายพวกเขายังคงหลงลืมกลไก Unicode เช่นการปรับมาตรฐานการเรียงใหม่ .. .)

ในขณะที่การเพิ่มลักษณะดี API ฉลาด Boost.Locale เป็นพื้นห่อหุ้มรอบห้องไอซียู ถ้า Boost ถูกคอมไพล์ด้วยการสนับสนุน ICU ... หากไม่ได้บูสต์ Boost.Locale จะถูก จำกัด เฉพาะการสนับสนุนโลแคลที่รวบรวมไว้สำหรับไลบรารีมาตรฐาน

และเชื่อฉันเถอะการได้รับ Boost เพื่อคอมไพล์ด้วย ICU อาจเป็นความเจ็บปวดที่แท้จริง (ไม่มีไบนารีที่คอมไพล์แล้วสำหรับ Windows ดังนั้นคุณต้องจัดส่งพร้อมกับแอปพลิเคชันของคุณและจะเปิดเวิร์มกระป๋องใหม่ทั้งหมด ... )

ดังนั้นโดยส่วนตัวฉันขอแนะนำให้รับ Unicode อย่างเต็มที่จากปากม้าและใช้ห้องสมุดICUโดยตรง:

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

คอมไพล์ (กับ G ++ ในตัวอย่างนี้):

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

สิ่งนี้ให้:

ὀδυσσεύς

โปรดทราบว่าการแปลงΣ <-> σอยู่ตรงกลางของคำและการแปลงΣ <-> at ที่ท้ายคำ <algorithm>วิธีการแก้ปัญหาที่ไม่มีพื้นฐานสามารถให้คุณได้ว่า


[1] ในปี 2560 สภาการันต์เยอรมันได้ตัดสินว่า "ẞ" U + 1E9E LATIN CAPT LATTER SHARP S สามารถใช้งานได้อย่างเป็นทางการเป็นตัวเลือกข้างการแปลง "SS" แบบดั้งเดิมเพื่อหลีกเลี่ยงความคลุมเครือเช่นในหนังสือเดินทาง ) ตัวอย่างที่สวยงามของฉันทำให้ล้าสมัยจากการตัดสินใจของคณะกรรมการ ...


19
นี่คือคำตอบที่ถูกต้องในกรณีทั่วไป มาตรฐานไม่ได้ให้อะไรสำหรับการจัดการอะไรนอกจาก "ASCII" ยกเว้นการโกหกและการหลอกลวง มันทำให้คุณคิดว่าคุณสามารถจัดการกับ UTF-16 ได้ แต่คุณทำไม่ได้ ดังที่คำตอบนี้บอกว่าคุณไม่สามารถรับความยาวอักขระที่เหมาะสม (ไม่ใช่ความยาวไบต์) ของสตริง UTF-16 โดยไม่ทำการจัดการ Unicode ของคุณเอง หากคุณต้องจัดการกับข้อความจริงให้ใช้ห้องไอซียู ขอขอบคุณ @DevSolar
การชดเชยอย่าง จำกัด

ICU พร้อมใช้งานเป็นค่าเริ่มต้นใน Ubuntu / Windows หรือจำเป็นต้องติดตั้งแยกต่างหากหรือไม่? นอกจากนี้ยังมีวิธีการเกี่ยวกับคำตอบนี้: stackoverflow.com/a/35075839/207661 ?
Shital Shah

1
เฮ้ดูคำตอบที่แท้จริง! ขอบคุณที่ชี้ให้ฉันในทางที่ถูกต้อง DevSolar
Dan Bechard

2
@DevSolar ตกลง! แนวคิดเรื่องความยาวนั้นค่อนข้างไร้ความหมายต่อข้อความ ที่กล่าวว่าเนื่องจากผู้คนคุ้นเคยกับแท็บและตัวควบคุมที่ใช้หน่วยความยาวหนึ่งหน่วยจุดรหัสจะเป็นวิธีการวัดที่ใช้งานง่ายขึ้น โอ้และขอบคุณที่ให้คำตอบที่ถูกต้องเสียใจที่เห็นมันมาก :-(
masaers

3
@LF Marginally ดีขึ้น แต่ยังมีอีกหลายสิ่งที่ยังไม่ครอบคลุม: toupperและtolowerยังสามารถใช้อักขระเดี่ยวได้ คลาสสตริงยังคงไม่มีความคิดในการทำให้เป็นมาตรฐาน (เช่นว่า "ü" ถูกเข้ารหัสเป็น "u กับ diaeresis" หรือ "u + รวม diaeresis") หรือที่สตริงอาจหรือไม่อาจถูกแยกออก รายการดำเนินต่อไป u8string เป็น (เช่นคลาสสตริงมาตรฐานอื่น ๆ ) ที่เหมาะสมสำหรับ "การผ่าน" แต่ถ้าคุณต้องการประมวลผล Unicode คุณต้อง ICU
DevSolar

36

การใช้ range-based สำหรับ loop ของ C ++ 11 จะทำให้โค้ดง่ายขึ้น:

#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
อย่างไรก็ตามในเครื่องภาษาฝรั่งเศสโปรแกรมนี้จะไม่แปลงอักขระที่ไม่ใช่ ASCII ที่อนุญาตในภาษาฝรั่งเศส ตัวอย่างเช่นสตริง 'ทดสอบ String123 ÉÏ \ n 'จะถูกแปลงเป็น:' test string123 ÉÏ \ n 'ถึงแม้ว่าตัวละครÉÏและตัวพิมพ์เล็กของพวกเขา' é 'และ' ï 'จะเป็นภาษาฝรั่งเศส ดูเหมือนว่าไม่มีวิธีแก้ปัญหาสำหรับข้อความนี้จากข้อความอื่นของเธรดนี้
incises

ฉันคิดว่าคุณต้องตั้งค่าภาษาให้เหมาะสม
user1095108

@ เกิดขึ้นแล้วมีคนโพสต์คำตอบเกี่ยวกับห้องไอซียูและนั่นเป็นวิธีที่จะไปอย่างแน่นอน ง่ายกว่าโซลูชันอื่น ๆ ส่วนใหญ่ที่พยายามทำความเข้าใจกับสถานที่
Alexis Wilke

ฉันไม่ต้องการใช้ห้องสมุดภายนอกถ้าเป็นไปได้เป็นการส่วนตัว
kayleeFrye_onDeck


15

นี่คือการติดตามเพื่อตอบสนองต่อสเตฟานเชียงใหม่: std::transformถ้าคุณต้องการที่จะวางผลของการแปลงในสายอื่นที่คุณจะต้องเตรียมจัดสรรพื้นที่เก็บข้อมูลก่อนที่จะเรียก เนื่องจาก STL เก็บอักขระที่แปลงที่ตัววนซ้ำปลายทาง (เพิ่มขึ้นในแต่ละรอบการวนซ้ำ), สตริงปลายทางจะไม่ถูกปรับขนาดโดยอัตโนมัติและคุณเสี่ยงต่อการหน่วยความจำย่ำ

#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
สิ่งนี้ไม่ได้ปรับขนาดÄเป็นäให้ฉัน
Purefan

ยังสามารถใช้ตัวแทรกย้อนกลับที่นี่แทนการปรับขนาดด้วยตนเอง
พริก

11

อีกวิธีหนึ่งที่ใช้ช่วงสำหรับลูปพร้อมตัวแปรอ้างอิง

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

cout<<test<<endl;

6

เท่าที่ฉันเห็นห้องสมุด Boost มีประสิทธิภาพการทำงานที่ไม่ดีจริงๆ ฉันได้ทดสอบ unordered_map ของพวกเขาไปยัง STL และมันช้าลงโดยเฉลี่ย 3 ครั้ง (กรณีที่ดีที่สุด 2, แย่ที่สุดคือ 10 เท่า) อัลกอริทึมนี้ดูต่ำเกินไป

ความแตกต่างนั้นใหญ่มากจนฉันแน่ใจว่าคุณจะต้องเพิ่มอะไรเพิ่มtolowerเพื่อให้เท่ากับ "สำหรับความต้องการของคุณ" จะเร็วกว่าการเพิ่ม

ฉันได้ทำการทดสอบเหล่านี้กับ Amazon EC2 แล้วดังนั้นประสิทธิภาพจึงแตกต่างกันในระหว่างการทดสอบ แต่คุณยังคงได้รับแนวคิด

./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 ทำให้มันเป็นแบบนี้:

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

ที่มา:

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

ฉันเดาว่าควรทดสอบกับเครื่องเฉพาะ แต่ฉันจะใช้ EC2 นี้ดังนั้นฉันไม่จำเป็นต้องทดสอบกับเครื่องของฉัน


1
คุณได้เปิดตัวเลือกการเพิ่มประสิทธิภาพเมื่อรวบรวมหรือไม่ ฉันคิดว่า STL heavy boost library ควรทำงานได้ดีขึ้นด้วยการเพิ่มประสิทธิภาพระดับสูง
Wei Song

1
ฉันใช้ -O2 ในการทดสอบอย่างใดอย่างหนึ่ง
Etherealone

2
ประสิทธิภาพของ unordered_map ขึ้นอยู่กับอัลกอริทึมการแฮชรวมกับข้อมูลที่คุณกำลังใช้ ไม่มีอัลกอริทึมการแปลงแป้นวิเศษที่ทำงานได้สำหรับทุกคนและข้อมูลใด ๆ เพื่อทำให้ unordered_map เร็วที่สุดเท่าที่จะทำได้ เกณฑ์มาตรฐานและลองสิ่งต่าง ๆ เหตุผลที่ทำให้ประสิทธิภาพในการทำงานแย่ลงเนื่องจากความยุ่งเหยิงที่คุณใช้คุณได้รับการชนจำนวนมากซึ่งโดยทั่วไปทำให้เกิดการค้นหาในรายการ ลองดูที่เว็บไซต์นี้เพื่อรับข้อมูลเพิ่มเติม: fgda.pl/post/7/gcc-hash-map-vs-unordered-map สำหรับวัตถุประสงค์ของฉันฟังก์ชั่นที่มีให้ที่ลิงก์ลดการชนกันและรวดเร็วมาก
leetNightshade

6

วิธีที่ง่ายที่สุดในการแปลงสตริงให้เป็น loweercase โดยไม่ต้องกังวลเกี่ยวกับ std namespace มีดังนี้

1: สตริงที่มี / ไม่มีช่องว่าง

#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: สตริงที่ไม่มีช่องว่าง

#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()จากไลบรารีการแปล C ++ มาตรฐานจะทำสิ่งนี้ให้คุณอย่างถูกต้อง นี่คือตัวอย่างที่ดึงมาจากหน้าอ้างอิง tolower

#include <locale>
#include <iostream>

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

ดีตราบใดที่คุณสามารถแปลงตัวละครในสถานที่ เกิดอะไรขึ้นถ้าสตริงที่มาของคุณคือconstอะไร ที่ดูเหมือนว่าจะทำให้มันยุ่งมากขึ้น (เช่นมันดูไม่เหมือนที่คุณสามารถใช้f.tolower()) เนื่องจากคุณต้องใส่ตัวละครในสายอักขระใหม่ คุณจะใช้transform()และสิ่งที่ชอบstd::bind1st( std::mem_fun() )สำหรับผู้ประกอบการหรือไม่
quazar

สำหรับสตริง const เราสามารถทำสำเนาโลคัลแล้วแปลงให้อยู่ในตำแหน่งได้
Sameer

แม้ว่าการทำสำเนาจะเป็นการเพิ่มค่าใช้จ่ายให้มากขึ้น
quazar

คุณสามารถใช้ std :: transform กับเวอร์ชันของ ctype :: tolower ที่ไม่ได้ใช้พอยน์เตอร์ ใช้อะแดปเตอร์ตัวแทรกกลับด้านในและคุณไม่จำเป็นต้องกังวลเกี่ยวกับการปรับขนาดเอาต์พุตสตริงของคุณล่วงหน้า
พริก

ยอดเยี่ยมโดยเฉพาะอย่างยิ่งเนื่องจากใน libstdc ++ tolowerมีlocaleพารามิเตอร์การโทรโดยนัยที่use_facetปรากฏเป็นคอขวดของประสิทธิภาพ หนึ่งในเพื่อนร่วมงานของฉันได้รับความเร็วเพิ่มขึ้น 100% โดยการแทนที่boost::iequals(ซึ่งมีปัญหานี้) ด้วยรุ่นที่use_facetมีการเรียกเพียงครั้งเดียวนอกวง
Arne Vogel

3

อีกทางเลือกหนึ่งสำหรับ Boost คือ POCO (pocoproject.org)

POCO มีสองรูปแบบ:

  1. ตัวแปรแรกสร้างสำเนาโดยไม่เปลี่ยนสตริงเดิม
  2. ชุดที่สองเปลี่ยนสายอักขระเดิมแทน
    รุ่น "In Place" มี "InPlace" ในชื่อเสมอ

ทั้งสองรุ่นแสดงให้เห็นด้านล่าง:

#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

มีวิธีการแปลงตัวพิมพ์ใหญ่เพื่อลดโดยไม่ต้องทำถ้าการทดสอบและมันค่อนข้างตรงไปข้างหน้า isupper () ฟังก์ชั่น / มาโครการใช้ clocale.h ควรดูแลปัญหาที่เกี่ยวข้องกับตำแหน่งของคุณ แต่ถ้าไม่คุณสามารถปรับแต่ง UtoL [] กับเนื้อหาในหัวใจของคุณ

เนื่องจากอักขระของ C นั้นเป็น ints 8 บิตจริง ๆ (โดยไม่สนใจชุดอักขระแบบกว้างในขณะนี้) คุณสามารถสร้างอาร์เรย์ขนาด 256 ไบต์ที่ถือชุดอักขระอื่นได้และในฟังก์ชันการแปลงจะใช้ตัวอักษรในสตริงเป็นตัวห้อยลงไป อาร์เรย์การแปลง

แทนที่จะทำแผนที่แบบ 1 ต่อ 1 ให้สมาชิกอาร์เรย์ตัวพิมพ์ใหญ่มีค่า BYTE int สำหรับอักขระตัวพิมพ์เล็ก คุณอาจพบislower () และ isupper ()มีประโยชน์ที่นี่

ป้อนคำอธิบายรูปภาพที่นี่

รหัสมีลักษณะเช่นนี้ ...

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

วิธีการนี้จะช่วยให้คุณสามารถทำการแมปตัวละครอื่น ๆ ที่คุณต้องการเปลี่ยนแปลงได้ในเวลาเดียวกัน

วิธีการนี้มีข้อดีอย่างมากเมื่อใช้กับโปรเซสเซอร์ที่ทันสมัยไม่จำเป็นต้องทำการคาดการณ์สาขาเนื่องจากไม่มีการทดสอบหากประกอบไปด้วยการแยกสาขา สิ่งนี้จะช่วยประหยัดตรรกะการคาดคะเนสาขาของ CPU สำหรับลูปอื่น ๆ และมีแนวโน้มที่จะป้องกันไม่ให้แผงลอยไปป์ไลน์

บางคนที่นี่อาจรู้จักวิธีนี้เหมือนกับวิธีที่ใช้ในการแปลง EBCDIC เป็น ASCII


2
"มีวิธีการแปลงตัวพิมพ์ใหญ่ให้ต่ำลงโดยไม่ต้องทำอะไรหากการทดสอบ" เคยได้ยินจากตารางการค้นหาหรือไม่?
Gábor Buella

1
พฤติกรรมที่ไม่ได้กำหนดสำหรับตัวอักษรเชิงลบ
Roland Illig

ซีพียูรุ่นใหม่มีปัญหาคอขวดในหน่วยความจำไม่ใช่ซีพียู การเปรียบเทียบจะน่าสนใจ
Contango

3

เนื่องจากไม่มีคำตอบที่กล่าวถึงไลบรารี Ranges ที่กำลังจะมาซึ่งมีอยู่ในไลบรารี่มาตรฐานตั้งแต่ C ++ 20 และขณะนี้มีให้บริการแยกต่างหากบน GitHubเช่นrange-v3ฉันต้องการเพิ่มวิธีในการแปลงนี้โดยใช้

ในการแก้ไขสตริงแบบแทนที่:

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

ในการสร้างสตริงใหม่:

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

(อย่าลืม#include <cctype>และส่วนหัว Ranges ที่ต้องการ)

หมายเหตุ: การใช้unsigned charอาร์กิวเมนต์เป็นแลมบ์ดาได้รับแรงบันดาลใจจากcppreferenceซึ่งระบุว่า:

เช่นเดียวกับฟังก์ชั่นอื่น ๆ ทั้งหมดจาก<cctype>พฤติกรรมของstd::tolowerจะไม่ได้กำหนดถ้าค่าอาร์กิวเมนต์คือที่แทนค่าเป็นมิได้เท่ากับunsigned char EOFในการใช้ฟังก์ชั่นเหล่านี้อย่างปลอดภัยด้วยchars (signed char ) อาร์กิวเมนต์ควรถูกแปลงเป็นunsigned char:

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

ในทำนองเดียวกันพวกเขาไม่ควรนำมาใช้โดยตรงกับขั้นตอนวิธีมาตรฐานเมื่อประเภทค่า iterator คือหรือchar signed charให้แปลงค่าเป็นunsigned charอันดับแรกแทน:

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

ฟังก์ชั่นเทมเพลตของฉันเองที่ใช้ตัวพิมพ์เล็ก / ใหญ่

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

นี่คือสิ่งที่ฉันต้องการ ฉันเพิ่งใช้towlowerสำหรับตัวกว้างที่รองรับ UTF-16
Juv

2

นี่เป็นเทคนิคมาโครหากคุณต้องการอะไรที่เรียบง่าย:

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

อย่างไรก็ตามโปรดทราบว่าความคิดเห็นของ @ AndreasSpindler เกี่ยวกับคำตอบนี้ยังคงเป็นข้อพิจารณาที่สำคัญอย่างไรก็ตามหากคุณกำลังทำงานกับสิ่งที่ไม่ใช่แค่ตัวอักษร ASCII


1
ฉันกำลังโค่นล้มสิ่งนี้เพื่อให้มาโครเมื่อมีวิธีแก้ปัญหาที่ดีอย่างสมบูรณ์ - คุณยังให้วิธีแก้ปัญหาเหล่านั้นด้วย
ชัดเจน

2
เทคนิคมาโครหมายถึงการพิมพ์รหัสน้อยลงสำหรับสิ่งที่คนทั่วไปมักใช้ในการเขียนโปรแกรม ทำไมไม่ใช้มัน มิฉะนั้นแล้วทำไมถึงมีมาโครเลย
Volomike

3
มาโครเป็นสิ่งที่สืบทอดมาจาก C ซึ่งกำลังทำงานอย่างหนักเพื่อที่จะกำจัด หากคุณต้องการลดปริมาณการพิมพ์ให้ใช้ฟังก์ชั่นหรือแลมบ์ดา void strtoupper(std::string& x) { std::transform (x.begin(), x.end(), x.begin(), ::toupper); }
ชัดเจน

1
@Clearer เนื่องจากฉันต้องการเป็น coder ที่ดีขึ้นคุณสามารถให้ลิงค์ ANSI doc ใด ๆ แก่ฉันที่คณะกรรมการ ANSI C ++ พูดอะไรบางอย่างกับผลกระทบของ "เราจำเป็นต้องเรียกประชุมเพื่อกำจัดมาโครออกจาก C ++" หรือไม่? หรือแผนงานอื่น ๆ ?
Volomike

2
ไม่ฉันไม่สามารถ ท่าทีของ Bjarne ในหัวข้อนั้นได้รับการอธิบายอย่างชัดเจนในหลาย ๆ ครั้ง นอกจากนี้ยังมีเหตุผลมากมายที่จะไม่ใช้มาโครใน C และ C ++ xอาจเป็นนิพจน์ที่ถูกต้องซึ่งเกิดขึ้นกับการคอมไพล์อย่างถูกต้อง แต่จะให้ผลลัพธ์ที่สมบูรณ์โดยสมบูรณ์เนื่องจากมาโคร
ชัดเจน

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

สำหรับข้อมูลเพิ่มเติม: http://www.cplusplus.com/reference/locale/tolower/


2

มีทางเลือกอื่นที่ใช้งานได้ 100% หรือไม่

ไม่

มีคำถามหลายข้อที่คุณต้องถามตัวเองก่อนเลือกวิธีลดขนาด

  1. สตริงมีการเข้ารหัสอย่างไร ASCII ธรรมดา? UTF-8? รูปแบบของการเข้ารหัสแบบดั้งเดิมของ ASCII เพิ่มเติมมีอะไรบ้าง
  2. คุณหมายถึงอะไรโดยตัวพิมพ์เล็ก กฎการจับคู่กรณีแตกต่างกันไประหว่างภาษา! คุณต้องการบางสิ่งที่แปลเป็นภาษาท้องถิ่นให้กับผู้ใช้หรือไม่ คุณต้องการบางสิ่งที่ทำงานอย่างสม่ำเสมอในทุกระบบที่ซอฟต์แวร์ของคุณทำงานอยู่หรือไม่? คุณเพียงต้องการลดขนาดอักขระ ASCII และส่งผ่านสิ่งอื่น ๆ ทั้งหมดหรือไม่
  3. มีห้องสมุดอะไรบ้าง

เมื่อคุณมีคำตอบสำหรับคำถามเหล่านั้นคุณสามารถเริ่มมองหาคำตอบที่เหมาะกับความต้องการของคุณ ไม่มีขนาดที่เหมาะกับทุกสิ่งที่เหมาะกับทุกคนในทุกที่!


2

ลองใช้ฟังก์ชั่นนี้ :)

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 คุณสามารถใช้strlwrตระกูลฟังก์ชัน: 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

ตัวอย่างโค้ด

#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

คัดลอกเพราะไม่ได้รับอนุญาตให้ปรับปรุงคำตอบ ขอบคุณมาก


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

คำอธิบาย:

for(auto& c : test)เป็นช่วงสำหรับวงวนชนิด:
for (range_declaration:range_expression)loop_statement

  1. range_declaration: ที่auto& c
    นี่ตัวระบุอัตโนมัติใช้สำหรับการหักประเภทอัตโนมัติ ดังนั้นประเภทจะถูกหักออกจากตัวแปรเริ่มต้น

  2. range_expression: ช่วงในกรณีนี้มีตัวอักษรของสตริงtest
    test

ตัวละครของสตริงที่มีอยู่เป็นข้อมูลอ้างอิงภายในสำหรับห่วงผ่านตัวระบุtestc


โปรดอธิบายว่าคุณคัดลอกคำตอบจากที่ไหน
bfontaine

0

C ++ ไม่มีเมธอด tolower หรือ toupper ที่ถูกนำไปใช้กับสตริง แต่มันพร้อมใช้งานสำหรับ char หนึ่งสามารถอ่านอักขระแต่ละตัวของสตริงได้อย่างง่ายดายแปลงเป็นกรณีที่ต้องการและใส่กลับเข้าไปในสตริง โค้ดตัวอย่างโดยไม่ใช้ไลบรารีบุคคลที่สาม:

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

สำหรับการดำเนินการตามอักขระบนสตริง: สำหรับอักขระทุกตัวในสตริง


-1

นี่อาจเป็นอีกเวอร์ชั่นที่ง่ายในการแปลงตัวพิมพ์ใหญ่เป็นตัวพิมพ์เล็กและในทางกลับกัน ฉันใช้ VS2017 เวอร์ชั่นชุมชนเพื่อคอมไพล์ซอร์สโค้ดนี้

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

หมายเหตุ: หากมีอักขระพิเศษคุณจะต้องจัดการโดยใช้การตรวจสอบสภาพ


-8

ฉันพยายาม std :: transform ทั้งหมดที่ฉันได้รับคือข้อผิดพลาดในการรวบรวม stl criptic ที่น่ารังเกียจที่ดรูอิดเมื่อ 200 ปีก่อนเท่านั้นที่สามารถเข้าใจได้ (ไม่สามารถแปลงจากเป็น flibidi flabidi ไข้หวัดใหญ่)

ใช้งานได้ดีและสามารถปรับแต่งได้อย่างง่ายดาย

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;
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.