วิธีหนึ่งสามารถแปลงสตริงเป็นตัวพิมพ์ใหญ่ ตัวอย่างที่ฉันพบจาก googling มีเพียงการจัดการกับตัวอักษร
วิธีหนึ่งสามารถแปลงสตริงเป็นตัวพิมพ์ใหญ่ ตัวอย่างที่ฉันพบจาก googling มีเพียงการจัดการกับตัวอักษร
คำตอบ:
เพิ่มอัลกอริธึมสตริง:
#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");
std::string newstr(boost::to_upper_copy<std::string>("Hello World"));
#include <algorithm>
#include <string>
std::string str = "Hello World";
std::transform(str.begin(), str.end(),str.begin(), ::toupper);
toupper()
สามารถนำมาใช้เป็นมาโครได้ นี่อาจทำให้เกิดปัญหา
toupper
รอบคัดเลือกก่อน ความคิดใด ๆ
วิธีแก้ปัญหาแบบสั้นโดยใช้ C ++ 11 และ toupper ()
for (auto & c: str) c = toupper(c);
c
เป็นconst char
ประเภท (จากauto
) ถ้าเป็นเช่นนั้นคุณจะไม่สามารถกำหนด (เพราะconst
บางส่วน) toupper(c)
กับสิ่งที่ถูกส่งกลับโดย
c
ต้องทำการ cast เพื่อunsigned char
ให้ corred นี้
struct convert {
void operator()(char& c) { c = toupper((unsigned char)c); }
};
// ...
string uc_str;
for_each(uc_str.begin(), uc_str.end(), convert());
หมายเหตุ:สองสามปัญหาเกี่ยวกับวิธีแก้ปัญหายอดนิยม:
21.5 ยูทิลิตี้ลำดับต่อเนื่องที่สิ้นสุดลง
เนื้อหาของส่วนหัวเหล่านี้จะเหมือนกับส่วนหัวของ Standard C Library <ctype.h>, <wctype.h>, <string.h>, <wchar.h> และ <stdlib.h> [... ]
ซึ่งหมายความว่าcctype
สมาชิกอาจเป็นมาโครที่ไม่เหมาะสำหรับการบริโภคโดยตรงในอัลกอริทึมมาตรฐาน
อีกปัญหาหนึ่งที่มีตัวอย่างเดียวกันคือมันไม่ได้ทำการโต้แย้งหรือตรวจสอบว่านี่ไม่ใช่เชิงลบ สิ่งนี้เป็นอันตรายอย่างยิ่งสำหรับระบบที่char
ลงชื่อแบบธรรมดา (เหตุผลคือ: หากสิ่งนี้ถูกนำมาใช้เป็นมาโครอาจจะใช้ตารางการค้นหาและดัชนีอาร์กิวเมนต์ของคุณลงในตารางนั้นดัชนีเชิงลบจะให้ UB แก่คุณ)
ปัญหานี้สามารถเวกเตอร์ได้ด้วย SIMDสำหรับชุดอักขระ ASCII
การทดสอบเบื้องต้นด้วย x86-64 gcc 5.2 -O3 -march=native
บน Core2Duo (Merom) สตริงเดียวกันของอักขระ 120 ตัว (ตัวพิมพ์เล็กผสมและ ASCII ไม่ใช่ตัวพิมพ์เล็ก), แปลงเป็นลูป 40M ครั้ง (โดยไม่มีการอินไลน์ cross-file ดังนั้นคอมไพเลอร์ไม่สามารถปรับให้เหมาะสมหรือยกออกจากลูปได้) บัฟเฟอร์ต้นทางและปลายทางเดียวกันดังนั้นจึงไม่มีโอเวอร์เฮดของ Malloc หรือเอฟเฟกต์หน่วยความจำ / แคช: ข้อมูลมีความร้อนในแคช L1 ตลอดเวลาและเราใช้ CPU ล้วนๆ
boost::to_upper_copy<char*, std::string>()
: 198.0s ใช่ Boost 1.58 บน Ubuntu 15.10 ช้ามาก ฉันทำประวัติและก้าว asm ใน debugger และมันก็แย่จริงๆ : มี dynamic_cast ของตัวแปร locale ที่เกิดขึ้นต่อตัวละคร !!! (dynamic_cast ใช้การเรียกหลายครั้งไปยัง strcmp) สิ่งนี้เกิดขึ้นกับLANG=C
และด้วยLANG=en_CA.UTF-8
และมีการ
ฉันไม่ได้ทดสอบการใช้ RangeT อื่นนอกเหนือจาก std :: string บางทีรูปแบบอื่นของการto_upper_copy
เพิ่มประสิทธิภาพที่ดีขึ้น แต่ฉันคิดว่ามันจะnew
/ malloc
พื้นที่สำหรับคัดลอกเสมอจึงยากที่จะทดสอบ บางทีสิ่งที่ฉันทำแตกต่างจากกรณีการใช้งานปกติและบางทีการหยุด g ++ สามารถยกการตั้งค่าโลแคลออกจากลูปต่ออักขระได้ ลูปของฉันอ่านจากstd::string
และเขียนไปยังchar dstbuf[4096]
เหมาะสมสำหรับการทดสอบ
การเรียกลูป glibc toupper
: 6.67s (ไม่ได้ตรวจสอบint
ผลลัพธ์สำหรับ UTF-8 หลายไบต์ที่เป็นไปได้ แต่สิ่งนี้สำคัญสำหรับตุรกี)
cmov
โดยที่ตารางนั้นร้อนใน L1 อยู่ดีดูคำถามนี้เกี่ยวกับtoupper()
การช้าบน Windows เมื่อมีการตั้งค่าภาษา
ฉันตกใจที่ Boost เป็นลำดับความสำคัญช้ากว่าตัวเลือกอื่น ๆ ฉันตรวจสอบอีกครั้งว่าฉัน-O3
เปิดใช้งานแล้วและแม้แต่ asm ก็ก้าวเดียวเพื่อดูว่ามันกำลังทำอะไรอยู่ มันเกือบจะเหมือนกับความเร็วด้วย clang ++ 3.8 มันมีค่าใช้จ่ายมากในวงต่อตัวละคร perf record
/ report
ผล (สำหรับcycles
เหตุการณ์ perf) เป็น:
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% ...
Gcc และ clang จะทำการวนซ้ำอัตโนมัติเวกเตอร์เมื่อทราบจำนวนการวนซ้ำก่อนลูป (นั่นคือลูปการค้นหาเช่นการใช้ C ธรรมดาstrlen
จะไม่ทำให้เป็นอัตโนมัติ)
ดังนั้นสำหรับสตริงที่มีขนาดเล็กพอที่จะใส่ในแคชเราจะได้รับการเร่งความเร็วอย่างมีนัยสำคัญสำหรับสตริง ~ 128 ตัวอักษรให้ยาวstrlen
ก่อน สิ่งนี้ไม่จำเป็นสำหรับสตริงที่มีความยาวอย่างชัดเจน (เช่น 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;
}
libc ที่เหมาะสมจะมีประสิทธิภาพstrlen
ที่เร็วกว่าการวนลูปทีละครั้งดังนั้นการแยกเวกเตอร์ strlen และลูปลูปให้เร็วขึ้น
Baseline: การวนซ้ำที่ตรวจสอบการยกเลิก 0 ในทันที
คูณ 40M ซ้ำบน Core2 (Merom) 2.4GHz GCC -O3 -march=native
5.2 (Ubuntu 15.10) dst != src
(ดังนั้นเราจึงทำสำเนา) แต่จะไม่ทับซ้อนกัน (และไม่ใกล้เคียง) ทั้งสองอยู่ในแนวเดียวกัน
ผลลัพธ์บางอย่างมีความแตกต่างเล็กน้อยกับเสียงดังกราว
ลูป microbenchmark ที่เรียกใช้ฟังก์ชันอยู่ในไฟล์แยกต่างหาก ไม่เช่นนั้นจะอินไลน์และstrlen()
ดึงออกจากลูปและจะทำงานเร็วขึ้นอย่างมากโดยเฉพาะ สำหรับ 16 อักขระสตริง (0.187s)
นี่เป็นข้อได้เปรียบที่สำคัญที่ gcc สามารถทำให้เป็นเวกเตอร์อัตโนมัติสำหรับสถาปัตยกรรมใด ๆ ก็ได้ แต่ข้อเสียเปรียบหลักที่ทำให้ช้าลงสำหรับกรณีที่พบบ่อยของสตริงขนาดเล็ก
ดังนั้นการเร่งความเร็วครั้งยิ่งใหญ่ แต่คอมไพเลอร์อัตโนมัติ - การทำให้เป็นเวกเตอร์ไม่ได้สร้างโค้ดที่ดี สำหรับการล้างข้อมูลอักขระสูงสุดถึง 15 ตัวสุดท้าย
ขึ้นอยู่กับฟังก์ชั่นตัวพิมพ์ใหญ่ - เล็กของฉันที่พลิกกรณีของตัวอักษรทุกตัว มันต้องใช้ประโยชน์จาก "เคล็ดลับเปรียบเทียบไม่ได้ลงนาม" ซึ่งคุณสามารถทำได้low < a && a <= high
ด้วยการเปรียบเทียบที่ไม่ได้ลงชื่อเดียวโดยช่วงการขยับเพื่อให้ค่าใด ๆ ที่น้อยกว่าการตัดค่าที่เป็นมากกว่าlow
high
(ใช้งานได้หากlow
และhigh
อยู่ไม่ห่างกัน)
SSE มีเพียงการเปรียบเทียบที่ได้รับการลงนามแล้วเท่านั้น แต่เรายังคงสามารถใช้เคล็ดลับ "การเปรียบเทียบที่ไม่ได้ลงชื่อ" โดยการเลื่อนช่วงไปทางด้านล่างของช่วงที่เซ็นชื่อ: ลบ 'a' + 128 ดังนั้นอักขระที่เป็นตัวอักษรจะอยู่ในช่วง -128 ถึง -128 +25 (-128 + 'z' - 'a')
โปรดทราบว่าการเพิ่ม 128 และการลบ 128 เป็นสิ่งเดียวกันสำหรับจำนวนเต็ม 8 บิต ไม่มีที่ใดที่จะนำติดตัวไปได้ดังนั้นจึงเป็นเพียงแค่แฮคเกอร์
#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
}
เนื่องจากฟังก์ชั่นนี้ใช้งานได้กับเวกเตอร์หนึ่งตัวเราสามารถเรียกมันเป็นลูปเพื่อประมวลผลสตริงทั้งหมด เนื่องจากเรากำหนดเป้าหมายไปที่ SSE2 แล้วเราสามารถทำการตรวจสอบจุดสิ้นสุดของสตริงแบบเวกเตอร์ได้ในเวลาเดียวกัน
นอกจากนี้เรายังสามารถทำได้ดีกว่าสำหรับ "การล้างข้อมูล" ของไบต์สูงสุด 15 ไบต์สุดท้ายที่เหลือหลังจากทำเวกเตอร์ 16B: ปลอกด้านบนเป็น idempotent ดังนั้นการประมวลผลอินพุตไบต์บางครั้งจึงไม่เป็นผล เราทำการโหลดที่ไม่ตรงแนวของ 16B สุดท้ายของแหล่งที่มาและเก็บไว้ในบัฟเฟอร์ปลายทางทับซ้อนที่เก็บ 16B สุดท้ายจากลูป
ครั้งเดียวที่ใช้งานไม่ได้คือเมื่อสตริงทั้งหมดมีค่าต่ำกว่า 16B: แม้ว่าdst=src
การอ่าน - แก้ไข - เขียนที่ไม่ใช่อะตอมไม่ใช่สิ่งเดียวกับที่ไม่ได้สัมผัสบางไบต์เลยและสามารถทำลายรหัสแบบมัลติเธรดได้
เรามีสเกลาร์วนรอบสำหรับสิ่งนั้นและเพื่อให้ได้src
แนวเดียวกัน เนื่องจากเราไม่ทราบว่าจุดสิ้นสุดของ 0 จะเป็นอย่างไรการโหลดที่ไม่ได้แนวจากsrc
อาจข้ามไปยังหน้าถัดไปและ segfault หากเราต้องการไบต์ใด ๆ ในก้อนขนาด 16B ที่เรียงกันจะปลอดภัยเสมอที่จะโหลดก้อนขนาด 16B ที่เรียงกันทั้งหมด
แหล่งที่มาเต็มรูปแบบ: ในส่วนสำคัญ 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;
}
คูณ 40M ซ้ำบน Core2 (Merom) 2.4GHz GCC -O3 -march=native
5.2 (Ubuntu 15.10) dst != src
(ดังนั้นเราจึงทำสำเนา) แต่จะไม่ทับซ้อนกัน (และไม่ใกล้เคียง) ทั้งสองอยู่ในแนวเดียวกัน
(หมดเวลาจริง_mm_store
ในวงไม่_mm_storeu
เพราะ storeu ช้าลงใน Merom แม้ว่าที่อยู่จะถูกจัดตำแหน่งมันใช้ได้กับ Nehalem และใหม่กว่าฉันยังทิ้งรหัสตามเดิมไว้ตอนนี้แทนที่จะแก้ไขความล้มเหลวในการคัดลอก 0 ที่ยุติในบางกรณีเพราะฉันไม่ต้องการเวลาทุกอย่างอีกครั้ง)
ดังนั้นสำหรับสตริงสั้น ๆ ที่ยาวกว่า 16B จะเร็วกว่าการปรับเวกเตอร์อัตโนมัติอย่างมาก ความยาวหนึ่งน้อยกว่าความกว้างเวกเตอร์ไม่แสดงปัญหา พวกเขาอาจมีปัญหาเมื่อใช้งานในสถานที่เนื่องจากแผงลอยส่งต่อร้านค้า (แต่โปรดทราบว่าการประมวลผลเอาต์พุตของเราเองนั้นยังดีกว่าอินพุตดั้งเดิมเพราะ toupper เป็น idempotent)
มีขอบเขตจำนวนมากสำหรับการปรับจูนนี้สำหรับการใช้งานที่แตกต่างกันขึ้นอยู่กับสิ่งที่ต้องการโดยรอบโค้ดและสถาปัตยกรรมไมโครเป้าหมาย รับคอมไพเลอร์ที่จะปล่อยรหัสที่ดีสำหรับส่วนการทำความสะอาดเป็นเรื่องยุ่งยาก การใช้ffs(3)
(ซึ่งคอมไพล์กับ bsf หรือ tzcnt บน x86) ดูเหมือนว่าจะดี แต่เห็นได้ชัดว่าบิตต้องคิดใหม่เนื่องจากฉันสังเกตเห็นข้อผิดพลาดหลังจากเขียนคำตอบส่วนใหญ่ (ดูความคิดเห็น FIXME)
speedups เวกเตอร์สำหรับสตริงแม้มีขนาดเล็กสามารถรับกับmovq
หรือmovd
โหลด / ร้านค้า ปรับแต่งตามความจำเป็นสำหรับการใช้งานของคุณ
เราสามารถตรวจจับได้เมื่อเวกเตอร์ของเรามีไบต์ใด ๆ ที่มีชุดบิตสูงและในกรณีนั้นกลับไปเป็นวนรอบสเกลาร์ UTF-8 ที่ทราบสำหรับเวกเตอร์นั้น dst
จุดสามารถล่วงหน้าตามจำนวนเงินที่แตกต่างจากsrc
ตัวชี้ แต่เมื่อเราได้รับกลับไปชิดsrc
ชี้เราจะยังคงเป็นเพียงทำร้านค้าเวกเตอร์ unaligned dst
ไป
สำหรับข้อความที่เป็น UTF-8 แต่ส่วนใหญ่ประกอบด้วยชุดย่อย ASCII ของ UTF-8 สิ่งนี้อาจดี: ประสิทธิภาพสูงในกรณีทั่วไปที่มีพฤติกรรมที่ถูกต้องในทุกกรณี เมื่อมี non-ASCII จำนวนมากมันอาจจะแย่กว่าการอยู่ใน scal วน UTF-8 ที่รู้อยู่ตลอดเวลา
การทำให้ภาษาอังกฤษเร็วขึ้นด้วยค่าใช้จ่ายของภาษาอื่นไม่ใช่การตัดสินใจในอนาคตหากข้อเสียมีความสำคัญ
ในโลแคลภาษาตุรกี ( tr_TR
) ผลที่ถูกต้องtoupper('i')
คือ'İ'
(U0130) ไม่ใช่'I'
(ASCII ธรรมดา) ดูความคิดเห็นของ Martin Bonnerเกี่ยวกับคำถามtolower()
ว่า Windows ทำงานช้า
นอกจากนี้เรายังสามารถตรวจสอบรายการยกเว้นและย้อนกลับไปยังเซนต์คิตส์และเนวิสที่นั่นเช่นอักขระอินพุตหลายไบต์ UTF8
ด้วยความซับซ้อนที่มากนี้ SSE4.2 PCMPISTRM
หรือบางสิ่งบางอย่างอาจทำให้เช็คของเรามากมายในครั้งเดียว
คุณมีอักขระ ASCII หรืออักขระสากลหรือไม่?
หากเป็นกรณีหลัง "ตัวพิมพ์ใหญ่" นั้นไม่ใช่เรื่องง่ายและขึ้นอยู่กับตัวอักษรที่ใช้ มีตัวอักษรสองส่วนและตัวเดียว เฉพาะตัวอักษรแบบสองส่วนเท่านั้นที่มีอักขระต่างกันสำหรับตัวพิมพ์ใหญ่และตัวพิมพ์เล็ก นอกจากนี้ยังมีอักขระผสมเช่นอักษรละตินตัวพิมพ์ใหญ่ 'DZ' (\ u01F1 'DZ') ซึ่งใช้ชื่อตัวพิมพ์ใหญ่ ซึ่งหมายความว่าเฉพาะอักขระตัวแรก (D) เท่านั้นที่ได้รับการเปลี่ยนแปลง
ฉันขอแนะนำให้คุณดูเป็นICUและความแตกต่างระหว่างการแมปแบบง่ายและแบบเต็ม สิ่งนี้อาจช่วย:
string StringToUpper(string strToConvert)
{
for (std::string::iterator p = strToConvert.begin(); strToConvert.end() != p; ++p)
*p = toupper(*p);
return p;
}
หรือ,
string StringToUpper(string strToConvert)
{
std::transform(strToConvert.begin(), strToConvert.end(), strToConvert.begin(), ::toupper);
return strToConvert;
}
**
หลังจากพารามิเตอร์ในการแก้ปัญหาแรกทำอะไร
**
เป็นตัวพิมพ์ที่หลงเหลืออยู่จากการพยายามใช้แบบอักษรตัวหนาในไวยากรณ์ของรหัส
toupper
ถูกเรียกด้วยจำนวนลบ
งานต่อไปนี้สำหรับฉัน
#include <algorithm>
void toUpperCase(std::string& str)
{
std::transform(str.begin(), str.end(), str.begin(), ::toupper);
}
int main()
{
std::string str = "hello";
toUpperCase(&str);
}
toupper
ถูกเรียกด้วยจำนวนลบ
ใช้แลมบ์ดา
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);
เร็วกว่าถ้าคุณใช้อักขระ ASCII เท่านั้น :
for(i=0;str[i]!=0;i++)
if(str[i]<='z' && str[i]>='a')
str[i]-=32;
โปรดทราบว่ารหัสนี้ทำงานได้เร็วขึ้น แต่ใช้ได้กับ ASCII เท่านั้นและไม่ใช่วิธีการแก้ปัญหา "นามธรรม"
หากคุณต้องการโซลูชัน UNICODE หรือโซลูชันทั่วไปและนามธรรมให้ไปที่คำตอบอื่น ๆ และทำงานกับวิธีการของสตริง C ++
C++
แต่คุณเขียนC
คำตอบที่นี่ (ฉันไม่ได้หนึ่งใน downvoters ได้.)
'
?
ตราบใดที่คุณยังใช้งานได้กับ ASCII เท่านั้นและคุณสามารถให้ตัวชี้ที่ถูกต้องไปยังหน่วยความจำ RW มีหนึ่งซับในที่ง่ายและมีประสิทธิภาพมากใน C:
void strtoupper(char* str)
{
while (*str) *(str++) = toupper((unsigned char)*str);
}
สิ่งนี้เป็นสิ่งที่ดีโดยเฉพาะอย่างยิ่งสำหรับสตริงอย่างง่ายเช่นตัวระบุ ASCII ซึ่งคุณต้องการทำให้ปกติเป็นตัวอักษรตัวพิมพ์ใหญ่ จากนั้นคุณสามารถใช้บัฟเฟอร์เพื่อสร้างอินสแตนซ์ของสตริง:
//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;
}
for (size_t i = 0 ...
มีเหตุผลที่จะไม่ที่จะเขียน ไม่มีเหตุผลที่ดีที่จะอ่านยาก สิ่งนี้จะคัดลอกสตริงก่อนแล้วจึงวนซ้ำมัน @ คำตอบของลุคนั้นดีกว่าในบางวิธียกเว้นการไม่ใช้ประโยชน์จาก'a'
ค่าคงที่ของตัวละคร
#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());
สิ่งนี้จะทำงานได้ดีกว่าคำตอบทั้งหมดที่ใช้ฟังก์ชัน toupper ทั่วโลกและน่าจะเป็นสิ่งที่ boost :: to_upper ทำงานภายใต้
นี่เป็นเพราะ :: toupper ต้องค้นหาโลแคล - เนื่องจากอาจมีการเปลี่ยนแปลงโดยเธรดที่แตกต่างกัน - สำหรับการเรียกใช้ทุกครั้งในขณะที่การเรียกไปยังโลแคล () มีโทษเท่านั้น และการค้นหาสถานที่โดยทั่วไปเกี่ยวข้องกับการล็อค
นอกจากนี้ยังใช้งานได้กับ C ++ 98 หลังจากที่คุณแทนที่รถยนต์การใช้ str.data () ที่ไม่ใช่ const ใหม่ () และเพิ่มพื้นที่เพื่อหยุดการปิดเทมเพลต (">>" เป็น ">>") ดังนี้:
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());
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 );
reserve
และback_inserter
(ทำให้สตริงถูกคัดลอกเพียงครั้งเดียว) 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; }
std::string value;
for (std::string::iterator p = value.begin(); value.end() != p; ++p)
*p = toupper(*p);
toupper
ถูกเรียกด้วยจำนวนลบ
ลองใช้toupper()
ฟังก์ชัน ( #include <ctype.h>
) มันยอมรับอักขระเป็นอาร์กิวเมนต์สตริงประกอบด้วยอักขระดังนั้นคุณจะต้องวนซ้ำอักขระแต่ละตัวที่เมื่อรวมเข้าด้วยกันจะประกอบด้วยสตริง
toupper
ถูกเรียกด้วยจำนวนลบ unsigned char
คุณควรจะได้กล่าวถึงความจำเป็นที่จะต้องโยน
นี่คือรหัสล่าสุดที่มี C ++ 11
std::string cmd = "Hello World";
for_each(cmd.begin(), cmd.end(), [](char& in){ in = ::toupper(in); });
toupper
ถูกเรียกด้วยจำนวนลบ
ใช้ Boost.Text ซึ่งจะทำงานกับข้อความ 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();
คำตอบของ@dirkgentlyเป็นอย่างมากสร้างแรงบันดาลใจ แต่ผมอยากจะเน้นว่าเนื่องจากความกังวลเป็นที่แสดงด้านล่าง
เช่นเดียวกับฟังก์ชั่นอื่น ๆ จากพฤติกรรมของ std :: toupper นั้นไม่ได้ถูกกำหนดถ้าค่าของอาร์กิวเมนต์ไม่สามารถแทนค่าได้เป็น char ที่ไม่ได้ลงนามหรือเท่ากับ EOF หากต้องการใช้ฟังก์ชันเหล่านี้อย่างปลอดภัยด้วยตัวอักษรธรรมดา (หรือตัวอักษรที่ลงนาม) อาร์กิวเมนต์ควรถูกแปลงเป็นถ่านที่ไม่ได้ลงชื่อก่อน
อ้างอิง : std :: toupper
การใช้งานที่ถูกต้องของ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;
}
เอาท์พุท:
Hello world!
HELLO WORLD!
ไม่แน่ใจว่ามีฟังก์ชั่นในตัว ลองสิ่งนี้:
รวมไลบรารี ctype.h OR cctype รวมถึง stdlib.h เป็นส่วนหนึ่งของคำสั่งตัวประมวลผลล่วงหน้า
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
}
toupper
ถูกเรียกด้วยจำนวนลบ
โซลูชันของฉัน (การล้างบิตที่ 6 สำหรับอัลฟา):
#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++;
}
}
toupper
ถูกเรียกด้วยจำนวนลบ
โซลูชันทั้งหมดในหน้านี้ยากกว่าที่ควรจะเป็น
ทำเช่นนี้
RegName = "SomE StRing That you wAnt ConvErTed";
NameLength = RegName.Size();
for (int forLoop = 0; forLoop < NameLength; ++forLoop)
{
RegName[forLoop] = tolower(RegName[forLoop]);
}
RegName
string
เป็นของคุณ รับขนาดสตริงของคุณไม่ได้ใช้string.size()
เป็นผู้ทดสอบที่แท้จริงของคุณยุ่งมากและอาจทำให้เกิดปัญหา แล้วก็ for
วง พื้นฐานที่สุด
โปรดจำไว้ว่าขนาดสตริงส่งคืนตัวคั่นด้วยดังนั้นให้ใช้ <และไม่ใช่ <= ในการทดสอบลูปของคุณ
เอาต์พุตจะเป็น: สตริงที่คุณต้องการแปลง
tolower
ลูปและส่วนใหญ่ของพวกเขาใช้ชื่อตัวแปรห่วงมาตรฐานเช่นไม่แปลกi
forLoop
โดยไม่ต้องใช้ไลบรารีใด ๆ :
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;
}
หากคุณกังวลกับตัวอักษร 8 บิตเท่านั้น (ซึ่งคำตอบอื่น ๆ ทั้งหมดยกเว้น Milan Babuškovถือว่าดี) คุณสามารถรับความเร็วที่เร็วที่สุดโดยสร้างตารางค้นหาในเวลารวบรวมโดยใช้เมตาโพแกรม บน ideone.com สิ่งนี้จะทำงานเร็วกว่าฟังก์ชั่นไลบรารี 7x และเร็วกว่ารุ่นเขียนด้วยมือ 3 เท่า ( http://ideone.com/sb1Rup ) นอกจากนี้ยังสามารถปรับแต่งได้ตามลักษณะโดยไม่ช้าลง
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);
}
กับกรณีการใช้งาน:
std::transform(in.begin(),in.end(),out.begin(),tableToUpper);
สำหรับการตัดสินใจเชิงลึก (หลายหน้า) ว่ามันทำงานอย่างไรให้ฉันเสียบบล็อกของฉันอย่างไร้ยางอาย: http://metaporky.blogspot.de/2014/07/part-4-generating-look-up-tables-at.html
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;
}
ฟังก์ชัน c ++ นี้ส่งคืนสตริงตัวพิมพ์ใหญ่เสมอ ...
#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;
}
ฉันใช้วิธีนี้ ฉันรู้ว่าคุณไม่ควรที่จะแก้ไขพื้นที่ข้อมูลนั้น .... แต่ฉันคิดว่าส่วนใหญ่สำหรับบัฟเฟอร์ที่มีข้อบกพร่องมากเกินไปและตัวละครที่เป็นโมฆะ
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
- พื้นที่ข้อมูลใดที่คุณไม่ควรแก้ไข
str[i] = toupper(str[i]);
ดีอย่างสมบูรณ์ ( ดีไม่ดีอย่างสมบูรณ์แต่มันแก้ไขส่วนใหญ่ของสิ่งที่ผิด)
::toupper
เป็นไปได้มากที่สุดที่จะสมมติว่า ASCII