Làm cách nào để chuyển std :: string thành chữ thường?


777

Tôi muốn chuyển đổi một std::stringchữ thường. Tôi nhận thức được chức năng này tolower(), tuy nhiên trong quá khứ tôi đã gặp vấn đề với chức năng này và dù sao nó cũng khó lý tưởng vì sử dụng với std::stringyêu cầu lặp lại qua từng ký tự.

Có một sự thay thế hoạt động 100% thời gian?


34
Làm thế nào khác bạn sẽ chuyển đổi từng yếu tố của một danh sách bất cứ điều gì sang một thứ khác, mà không lặp qua danh sách? Một chuỗi chỉ là một danh sách các ký tự, nếu bạn cần áp dụng một số chức năng cho mỗi ký tự, bạn sẽ phải lặp qua chuỗi. Không có cách nào xung quanh đó.

14
Tại sao chính xác câu hỏi này mert xuống đánh giá? Tôi không gặp vấn đề với việc lặp qua chuỗi của mình, nhưng tôi hỏi liệu có các chức năng nào khác ngoài tolower (), toupper (), v.v.
Konrad

3
Nếu bạn có mảng char kiểu C, thì tôi đoán bạn có thể thêm ox20202020 vào mỗi khối gồm 4 ký tự (với điều kiện là TẤT CẢ đã viết hoa) để chuyển đổi 4 ký tự thành chữ thường một lần.

13
@Dan: Nếu chúng có thể là chữ thường, nhưng chắc chắn là AZ hoặc az, bạn có thể HOẶC với 0x20 thay vì thêm. Một trong những điều tối ưu rất thông minh - có lẽ là ngu ngốc gần như không bao giờ có giá trị ...
Steve Jessop

4
Tôi không biết lý do tại sao nó lại bị bỏ phiếu ... chắc chắn là nó hơi kỳ quặc (bởi vì bạn phải lặp đi lặp lại qua mọi mục) bằng cách nào đó), nhưng đó là một câu hỏi hợp lệ
warren

Câu trả lời:


905

Chuyển thể từ những câu hỏi không thường gặp :

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

Bạn thực sự sẽ không thoát khỏi mà không lặp qua từng nhân vật. Không có cách nào để biết nhân vật là chữ thường hay chữ hoa.

Nếu bạn thực sự ghét tolower(), đây là một giải pháp thay thế chỉ dành riêng cho ASCII mà tôi không khuyên bạn nên sử dụng:

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

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

Xin lưu ý rằng tolower()chỉ có thể thực hiện thay thế một ký tự một byte, không phù hợp với nhiều tập lệnh, đặc biệt là nếu sử dụng mã hóa nhiều byte như UTF-8.


25
(Có thể cũ, các thuật toán trong câu hỏi đã thay đổi rất ít) @Stefan Mai: Loại "toàn bộ chi phí" nào có trong việc gọi các thuật toán STL? Các hàm khá nạc (nghĩa là đơn giản cho các vòng lặp) và thường được nội tuyến vì bạn hiếm khi có nhiều cuộc gọi đến cùng một hàm với cùng tham số mẫu trong cùng một đơn vị biên dịch.
eq-

257
Mỗi khi bạn cho rằng các nhân vật là ASCII, Chúa sẽ giết một con mèo con. :(
Brian Gordon

13
Ví dụ đầu tiên của bạn có khả năng có hành vi không xác định (chuyển charđến ::tolower(int).) Bạn cần đảm bảo bạn không vượt qua giá trị âm.
juanchopanza

37
-1 việc sử dụng này ::tolowercó thể gặp sự cố, đó là UB cho đầu vào không phải ASCII.
Chúc mừng và hth. - Alf

7
:: là cần thiết trước khi dung sai để chỉ ra rằng nó nằm trong không gian tên ngoài cùng. Nếu bạn sử dụng mã này trong một không gian tên khác, có thể có một định nghĩa khác (có thể không liên quan) về dung sai mà cuối cùng sẽ được chọn mà không có ::.
Charles Ofria

320

Boost cung cấp một thuật toán chuỗi cho việc này :

#include <boost/algorithm/string.hpp>

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

Hoặc, đối với không tại chỗ :

#include <boost/algorithm/string.hpp>

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

2
Tôi cho rằng điều này không có vấn đề tương tự như dung sai với đầu vào ASCII?
paulm

19
Thất bại cho phi ASCII-7.
DevSolar 27/2/2015

1
Có một phiên bản không tại chỗ của điều này?
Ray

5
@Ray, vâng,to_lower_copy
smac89

234

tl; dr

Sử dụng thư viện ICU . Nếu bạn không, thói quen chuyển đổi của bạn sẽ âm thầm phá vỡ các trường hợp mà bạn có thể không biết thậm chí còn tồn tại.


Đầu tiên bạn phải trả lời một câu hỏi: Mã hóa của bạn là std::stringgì? Có phải ISO-8859-1 không? Hoặc có lẽ ISO-8859-8? Hoặc Windows Codepage 1252? Có bất cứ điều gì bạn đang sử dụng để chuyển đổi chữ hoa sang chữ thường không? (Hay nó thất bại thảm hại cho các nhân vật hơn 0x7f?)

Nếu bạn đang sử dụng UTF-8 (sự lựa chọn lành mạnh duy nhất trong số các mã hóa 8 bit) với std::stringdạng container, bạn đã tự lừa dối mình khi tin rằng bạn vẫn đang kiểm soát mọi thứ, bởi vì bạn đang lưu trữ một chuỗi ký tự đa nhân trong một container đó là không nhận thức được khái niệm đa bào. Ngay cả một cái gì đó đơn giản như .substr()là một khung thời gian tích tắc. (Vì việc tách chuỗi đa chuỗi sẽ dẫn đến chuỗi (phụ) không hợp lệ.)

Và ngay khi bạn thử một cái gì đó như std::toupper( 'ß' ), trong bất kỳ mã hóa nào , bạn sẽ gặp rắc rối sâu sắc. (Bởi vì đơn giản là không thể thực hiện "quyền" này với thư viện chuẩn, chỉ có thể cung cấp một ký tự kết quả, không "SS"cần thiết ở đây.) [1] Một ví dụ khác sẽ std::tolower( 'I' )mang lại kết quả khác nhau tùy theo miền địa phương . Ở Đức, 'i'sẽ đúng; ở Thổ Nhĩ Kỳ, 'ı'(LATIN SMALL LETTER DOTLESS I) là kết quả mong đợi (một lần nữa, nhiều hơn một byte trong mã hóa UTF-8). Một ví dụ khác là Sigma Hy Lạp , chữ hoa '∑', chữ thường 'σ'... ngoại trừ ở cuối từ, nó ở đâu 'ς'.

Vì vậy, bất kỳ chuyển đổi trường hợp nào hoạt động trên một ký tự tại một thời điểm, hoặc tệ hơn, một byte tại một thời điểm, đều bị phá vỡ bởi thiết kế.

Sau đó là điểm mà các thư viện chuẩn, cho những gì nó có khả năng làm, là tùy thuộc vào miền địa phương được hỗ trợ trên máy phần mềm của bạn đang chạy trên ... và bạn sẽ làm gì nếu nó không phải là?

Vì vậy, những gì bạn đang thực sự tìm kiếm là một lớp chuỗi có khả năng xử lý tất cả điều này một cách chính xác, và đó không phải là bất kỳ std::basic_string<>biến thể nào .

(C ++ 11 lưu ý: std::u16stringstd::u32stringtốt hơn ., Nhưng vẫn không hoàn thiện C ++ 20 mang std::u8string, nhưng tất cả những việc phải làm là xác định mã hóa Trong nhiều khía cạnh khác mà họ vẫn không biết gì về cơ khí Unicode, như bình thường, đối chiếu, ... .)

Mặc dù Boost có vẻ đẹp, API thông minh, Boost.Locale về cơ bản là một trình bao bọc xung quanh ICU . Nếu Boost được biên dịch với hỗ trợ ICU ... nếu không, Boost.Locale bị giới hạn ở hỗ trợ ngôn ngữ được biên dịch cho thư viện chuẩn.

Và tin tôi đi, đôi khi việc Boost được biên dịch với ICU có thể là một nỗi đau thực sự. (Không có nhị phân được biên dịch sẵn cho Windows, vì vậy bạn phải cung cấp chúng cùng với ứng dụng của mình và điều đó sẽ mở ra một hộp giun hoàn toàn mới ...)

Vì vậy, cá nhân tôi khuyên bạn nên nhận hỗ trợ Unicode đầy đủ trực tiếp từ miệng ngựa và sử dụng thư viện ICU trực tiếp:

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

Biên dịch (với G ++ trong ví dụ này):

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

Điều này mang lại:

ὀδυσσεύς

Lưu ý rằng chuyển đổi Σ <-> in ở giữa từ và chuyển đổi Σ <-> ở cuối từ. Không có <algorithm>giải pháp dựa trên có thể cung cấp cho bạn điều đó.


. ). Ví dụ đẹp của tôi, đã bị lỗi thời bởi quyết định của ủy ban ...


19
Đây là câu trả lời đúng trong trường hợp chung. Tiêu chuẩn không đưa ra bất cứ điều gì để xử lý bất cứ điều gì ngoại trừ "ASCII" ngoại trừ dối trá và lừa dối. Nó khiến bạn nghĩ rằng bạn có thể đối phó với có thể UTF-16, nhưng bạn không thể. Như câu trả lời này nói, bạn không thể có được độ dài ký tự phù hợp (không phải độ dài byte) của chuỗi UTF-16 mà không thực hiện xử lý unicode của riêng bạn. Nếu bạn phải xử lý văn bản thực, hãy sử dụng ICU. Cảm ơn, @DevSolar
Chuộc tội có giới hạn

ICU có sẵn theo mặc định trên Ubuntu / Windows hay cần được cài đặt riêng? Ngoài ra làm thế nào về câu trả lời này: stackoverflow.com/a/35075839/207661 ?
Shital Shah

1
Này, nhìn này, một câu trả lời thực sự! Cảm ơn đã chỉ cho tôi đúng hướng, DevSolar.
Dan Bechard

2
@DevSolar Đồng ý! Khái niệm về độ dài khá vô nghĩa trên văn bản (chúng ta có thể thêm chữ ghép vào danh sách những người phạm tội). Điều đó nói rằng, vì mọi người đã quen với các tab và kiểm soát các ký tự chiếm một đơn vị độ dài, điểm mã sẽ là thước đo trực quan hơn. Ồ, và cảm ơn vì đã đưa ra câu trả lời chính xác, thật buồn khi thấy nó quá xa :-(
masaers

3
@LF Hoàn toàn tốt hơn. Nhưng rất nhiều thứ vẫn chưa được đề cập: touppertolowervẫn hoạt động trên các ký tự đơn. Lớp chuỗi vẫn không có khái niệm về chuẩn hóa (ví dụ: "ü" được mã hóa thành "u với diaeresis" hay "u + kết hợp diaeresis") hoặc trong đó một chuỗi có thể hoặc không thể tách rời. Danh sách cứ kéo dài. u8 chuỗi là (giống như các lớp chuỗi tiêu chuẩn khác) thích hợp cho "đi qua". Nhưng nếu bạn muốn xử lý Unicode, bạn cần ICU.
DevSolar

36

Sử dụng phạm vi dựa trên phạm vi cho vòng lặp của C ++ 11, một mã đơn giản hơn sẽ là:

#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
Tuy nhiên, trên máy tiếng Pháp, chương trình này không chuyển đổi các ký tự không phải ASCII được phép bằng ngôn ngữ tiếng Pháp. Ví dụ, chuỗi 'Kiểm tra String123. É Ï \ n 'sẽ được chuyển đổi thành:' chuỗi thử nghiệm123. É Ï \ n 'mặc dù các ký tự É và chữ thường viết hoa' é 'và' ï ', được cho phép bằng tiếng Pháp. Dường như không có giải pháp nào cho điều đó được cung cấp bởi các thông điệp khác của chủ đề này.
sinh

Tôi nghĩ bạn cần đặt một địa điểm thích hợp cho việc đó.
dùng1095108

@incises, sau đó ai đó đã đăng câu trả lời về ICU và đó chắc chắn là con đường để đi. Dễ dàng hơn hầu hết các giải pháp khác sẽ cố gắng hiểu miền địa phương.
Alexis Wilke

Cá nhân tôi không muốn sử dụng các thư viện bên ngoài khi có thể.
kayleeFrye_onDeck


15

Đây là phần tiếp theo cho phản hồi của Stefan Mai: nếu bạn muốn đặt kết quả của chuyển đổi trong một chuỗi khác, bạn cần phân bổ trước không gian lưu trữ của nó trước khi gọi std::transform. Vì STL lưu trữ các ký tự được chuyển đổi ở trình lặp đích (tăng nó ở mỗi lần lặp của vòng lặp), chuỗi đích sẽ không được tự động thay đổi kích thước và bạn có nguy cơ dậm bộ nhớ.

#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
Điều này đã không thay đổi kích thước Ä thành ä cho tôi
Purefan

Cũng có thể sử dụng một trình lặp lại bộ chèn ở đây thay vì thay đổi kích thước thủ công.
ớt

11

Một cách tiếp cận khác sử dụng phạm vi dựa trên vòng lặp với biến tham chiếu

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

cout<<test<<endl;

6

Theo như tôi thấy các thư viện Boost thực sự kém hiệu năng. Tôi đã thử nghiệm unordered_map của họ với STL và nó chậm hơn trung bình 3 lần (trường hợp tốt nhất 2, tệ nhất là 10 lần). Ngoài ra thuật toán này có vẻ quá thấp.

Sự khác biệt quá lớn đến nỗi tôi chắc chắn rằng bất kỳ sự bổ sung nào bạn sẽ cần phải làm tolowerđể làm cho nó bằng với việc tăng "cho nhu cầu của bạn" sẽ nhanh hơn so với tăng.

Tôi đã thực hiện các thử nghiệm này trên Amazon EC2, do đó hiệu suất thay đổi trong quá trình thử nghiệm nhưng bạn vẫn có ý tưởng.

./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 làm cho nó như thế này:

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

Nguồn:

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

Tôi đoán tôi nên thử nghiệm trên một máy chuyên dụng nhưng tôi sẽ sử dụng EC2 này vì vậy tôi không thực sự cần phải thử nghiệm trên máy của mình.


1
Bạn đã mở các tùy chọn tối ưu hóa khi biên dịch nó? Tôi nghĩ rằng thư viện tăng cường nặng STL nên chạy tốt hơn với mức tối ưu hóa cao.
Bài hát Wei

1
Tôi đã sử dụng -O2 trong một trong các thử nghiệm, và không có gì khác.
Etherealone

2
Hiệu suất của unordered_map phụ thuộc vào thuật toán băm kết hợp với dữ liệu bạn đang sử dụng. Không có thuật toán băm ma thuật nào hoạt động cho tất cả và mọi dữ liệu để làm cho unordered_map nhanh nhất có thể. Điểm chuẩn và thử những thứ khác nhau. Lý do bạn đang có hiệu suất kém hơn là vì với hàm băm bạn đang sử dụng, bạn đang gặp rất nhiều va chạm, điều này về cơ bản gây ra việc tra cứu trong danh sách. Kiểm tra trang web này để biết thêm thông tin: fgda.pl/post/7/gcc-hash-map-vs-unordered-map Với mục đích của tôi, chức năng được cung cấp tại liên kết đã giảm va chạm và do đó rất nhanh.
leetNightshade

6

Cách đơn giản nhất để chuyển đổi chuỗi thành loweercase mà không cần bận tâm về không gian tên std như sau

1: chuỗi có / không có dấu cách

#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: chuỗi không có dấu cách

#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()từ thư viện Bản địa hóa C ++ chuẩn sẽ thực hiện chính xác điều này cho bạn. Dưới đây là một ví dụ được trích xuất từ trang tham chiếu khoan dung

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

Đẹp, miễn là bạn có thể chuyển đổi các nhân vật tại chỗ. Nếu chuỗi nguồn của bạn là constgì? Điều đó dường như làm cho nó trở nên lộn xộn hơn một chút (ví dụ như nó không giống như bạn có thể sử dụng f.tolower()), vì bạn cần đặt các ký tự trong một chuỗi mới. Bạn sẽ sử dụng transform()và một cái gì đó như std::bind1st( std::mem_fun() )cho các nhà điều hành?
quazar 17/8/2016

Đối với một chuỗi const, chúng ta chỉ cần tạo một bản sao cục bộ và sau đó chuyển đổi nó tại chỗ.
Sameer

Vâng, mặc dù, làm cho một bản sao thêm chi phí.
quazar

Bạn có thể sử dụng std :: Transform với phiên bản ctype :: tolower không mất con trỏ. Sử dụng bộ điều hợp lặp lặp bên trong và bạn thậm chí không cần phải lo lắng về việc định cỡ trước chuỗi đầu ra của mình.
ớt

Tuyệt vời, đặc biệt là bởi vì trong libstdc ++ 's tolowervới localetham số, cuộc gọi ngầm đến use_facetdường như là một nút cổ chai hiệu suất. Một trong những đồng nghiệp của tôi đã đạt được tốc độ tăng 100% bằng cách thay thế boost::iequals(có vấn đề này) bằng một phiên bản use_facetchỉ được gọi một lần bên ngoài vòng lặp.
Arne Vogel

3

Một thay thế cho Boost là POCO (pocoproject.org).

POCO cung cấp hai biến thể:

  1. Biến thể đầu tiên tạo một bản sao mà không thay đổi chuỗi gốc.
  2. Biến thể thứ hai thay đổi chuỗi gốc tại chỗ.
    Các phiên bản "In Place" luôn có tên "InPlace".

Cả hai phiên bản được trình bày dưới đây:

#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

Có một cách để chuyển đổi chữ hoa thành chữ thường mà KHÔNG thực hiện nếu kiểm tra , và nó khá đơn giản. Việc sử dụng hàm / macro của isupper () của clocale.h sẽ giải quyết các vấn đề liên quan đến vị trí của bạn, nhưng nếu không, bạn luôn có thể điều chỉnh UtoL [] theo nội dung trái tim của bạn.

Cho rằng các ký tự của C thực sự chỉ là số nguyên 8 bit (bỏ qua các bộ ký tự rộng trong thời điểm này), bạn có thể tạo một mảng 256 byte chứa một bộ ký tự thay thế và trong hàm chuyển đổi sử dụng các ký tự trong chuỗi của bạn làm các ký tự vào mảng chuyển đổi.

Thay vì ánh xạ 1 trên 1, hãy cung cấp cho các thành viên mảng chữ hoa các giá trị int BYTE cho các ký tự chữ thường. Bạn có thể thấy islower () và isupper () hữu ích ở đây.

nhập mô tả hình ảnh ở đây

Mã trông như thế này ...

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

Cách tiếp cận này đồng thời sẽ cho phép bạn sắp xếp lại bất kỳ nhân vật nào khác mà bạn muốn thay đổi.

Cách tiếp cận này có một lợi thế rất lớn khi chạy trên các bộ xử lý hiện đại, không cần phải dự đoán nhánh vì không có nếu các thử nghiệm bao gồm phân nhánh. Điều này giúp tiết kiệm logic dự đoán nhánh của CPU cho các vòng lặp khác và có xu hướng ngăn chặn các quầy hàng đường ống.

Một số người ở đây có thể nhận ra cách tiếp cận này giống như cách tiếp cận được sử dụng để chuyển đổi EBCDIC sang ASCII.


2
"Có một cách để chuyển đổi chữ hoa thành chữ thường mà KHÔNG làm nếu kiểm tra" bạn đã từng nghe về bảng tra cứu chưa?
Gábor Buella

1
Hành vi không xác định cho ký tự âm.
Roland Illig

CPU hiện đại bị tắc nghẽn trong bộ nhớ không phải CPU. Điểm chuẩn sẽ rất thú vị.
Contango

3

Vì không có câu trả lời nào đề cập đến thư viện Ranges sắp tới, có sẵn trong thư viện tiêu chuẩn kể từ C ++ 20, và hiện có sẵn riêng biệt trên GitHubrange-v3, tôi muốn nói thêm một cách để thực hiện chuyển đổi này sử dụng nó.

Để sửa đổi chuỗi tại chỗ:

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

Để tạo một chuỗi mới:

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

(Đừng quên #include <cctype>và các tiêu đề Phạm vi yêu cầu.)

Lưu ý: việc sử dụng unsigned charlàm đối số cho lambda được lấy cảm hứng từ cppreference , trong đó nêu rõ:

Giống như tất cả các hàm khác từ <cctype>, hành vi của std::tolowerkhông được xác định nếu giá trị của đối số không thể biểu thị unsigned charbằng hoặc không bằng EOF. Để sử dụng các hàm này một cách an toàn với char( signed charcác) đơn giản, trước tiên, đối số nên được chuyển đổi thành unsigned char:

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

Tương tự, chúng không nên được sử dụng trực tiếp với các thuật toán tiêu chuẩn khi loại giá trị của trình lặp là charhoặc signed char. Thay vào đó, chuyển đổi giá trị thành unsigned charđầu tiê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

Các hàm mẫu của riêng tôi thực hiện chữ hoa / chữ thường.

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

Đây là những gì tôi cần. Tôi chỉ sử dụng các towlowerký tự rộng hỗ trợ UTF-16.
Juv

2

Đây là một kỹ thuật vĩ mô nếu bạn muốn một cái gì đó đơn giản:

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

Tuy nhiên, lưu ý rằng nhận xét của @ AndreasSpindler về câu trả lời này vẫn là một sự cân nhắc quan trọng, tuy nhiên, nếu bạn đang làm việc trên một cái gì đó không chỉ là các ký tự ASCII.


1
Tôi đang hạ thấp điều này vì đã cho các macro khi có một giải pháp hoàn toàn tốt - bạn thậm chí còn đưa ra các giải pháp đó.
Rõ ràng hơn

2
Kỹ thuật vĩ mô có nghĩa là ít gõ mã cho một cái gì đó mà người ta thường sử dụng nhiều trong lập trình. Tại sao không sử dụng? Nếu không, tại sao có macro ở tất cả?
Volomike

3
Macro là một di sản từ C đang được làm việc chăm chỉ để loại bỏ. Nếu bạn muốn giảm số lượng gõ, hãy sử dụng hàm hoặc lambda. void strtoupper(std::string& x) { std::transform (x.begin(), x.end(), x.begin(), ::toupper); }
Rõ ràng hơn

1
@Clearer Vì tôi muốn trở thành một lập trình viên giỏi hơn, bạn có thể cung cấp cho tôi bất kỳ liên kết tài liệu ANSI nào trong đó bất kỳ ủy ban ANSI C ++ nào nói về tác dụng của "Chúng ta cần gọi một cuộc họp để loại bỏ macro khỏi C ++" không? Hoặc một số lộ trình khác?
Volomike

2
Không, tôi không thể. Quan điểm của Bjarne về chủ đề này đã được thể hiện khá rõ ràng trong một số trường hợp. Bên cạnh đó, có rất nhiều lý do để không sử dụng macro trong C cũng như C ++. xcó thể là một biểu thức hợp lệ, chỉ xảy ra để biên dịch chính xác nhưng sẽ cho kết quả hoàn toàn không có thật vì các macro.
Rõ ràng

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

Để biết thêm thông tin: http://www.cplusplus.com/reference/locale/tolower/


2

Có một sự thay thế hoạt động 100% thời gian?

Không

Có một số câu hỏi bạn cần phải tự hỏi mình trước khi chọn một phương pháp hạ thấp.

  1. Chuỗi được mã hóa như thế nào? ASCII đơn giản? UTF-8? một số hình thức mã hóa di sản ASCII mở rộng?
  2. Bạn có ý nghĩa gì bởi chữ thường? Quy tắc ánh xạ trường hợp khác nhau giữa các ngôn ngữ! Bạn có muốn một cái gì đó được bản địa hóa cho miền địa phương người dùng? Bạn có muốn một cái gì đó hoạt động nhất quán trên tất cả các hệ thống mà phần mềm của bạn chạy không? Bạn chỉ muốn viết thường các ký tự ASCII và vượt qua mọi thứ khác?
  3. Những thư viện nào có sẵn?

Một khi bạn có câu trả lời cho những câu hỏi đó, bạn có thể bắt đầu tìm kiếm một giải pháp phù hợp với nhu cầu của bạn. Không có một kích thước phù hợp với tất cả mọi thứ hoạt động cho tất cả mọi người ở khắp mọi nơi!


2

Hãy thử chức năng này :)

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

Trên các nền tảng microsoft, bạn có thể sử dụng nhóm strlwrchức năng: http://msdn.microsoft.com/en-us/l 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

Đoạn mã

#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

Sao chép vì nó không được phép cải thiện câu trả lời. Cảm ơn


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

Giải trình:

for(auto& c : test)là một phạm vi dựa trên vòng lặp của loại :
for (range_declaration:range_expression)loop_statement

  1. range_declaration: auto& c
    Ở đây bộ xác định tự động được sử dụng để khấu trừ loại tự động. Vì vậy, loại được khấu trừ từ bộ khởi tạo biến.

  2. range_expression: test
    Phạm vi trong trường hợp này là các ký tự của chuỗi test.

Các ký tự của chuỗi testcó sẵn như là một tham chiếu bên trong vòng lặp for thông qua định danh c.


Hãy làm rõ nơi bạn đã sao chép câu trả lời của bạn từ.
bfontaine

0

C ++ không có các phương thức khoan dung hoặc toupper được triển khai cho chuỗi, nhưng nó có sẵn cho char. Người ta có thể dễ dàng đọc từng char của chuỗi, chuyển đổi nó thành trường hợp cần thiết và đặt lại thành chuỗi. Mã mẫu mà không sử dụng bất kỳ thư viện bên thứ ba nào:

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

Đối với hoạt động dựa trên ký tự trên chuỗi: Đối với mỗi ký tự trong chuỗi


-1

Đây có thể là một phiên bản đơn giản khác để chuyển đổi chữ hoa thành chữ thường và ngược lại. Tôi đã sử dụng phiên bản cộng đồng VS2017 để biên dịch mã nguồn này.

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

Lưu ý: nếu có các ký tự đặc biệt thì cần phải xử lý bằng cách sử dụng kiểm tra điều kiện.


-8

Tôi đã thử std :: Transform, tất cả những gì tôi nhận được là lỗi biên dịch stl criptic đáng ghê tởm mà chỉ có druids từ 200 năm trước mới có thể hiểu được (không thể chuyển đổi từ cúm flibidi flabidi)

cái này hoạt động tốt và có thể dễ dàng điều chỉnh

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;
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.