std::string
을 소문자 로 변환하고 싶습니다 . 나는 그 기능을 알고 tolower()
있지만, 과거에는이 기능에 문제가 있었고 어쨌든 std::string
각 문자를 반복해야하므로 이상적인 것은 아닙니다 .
100 % 효과가있는 대안이 있습니까?
std::string
을 소문자 로 변환하고 싶습니다 . 나는 그 기능을 알고 tolower()
있지만, 과거에는이 기능에 문제가 있었고 어쨌든 std::string
각 문자를 반복해야하므로 이상적인 것은 아닙니다 .
100 % 효과가있는 대안이 있습니까?
답변:
자주 묻는 질문 (FAQ) 에서 적응 :
#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()
1 바이트 문자로만 대체 할 수 있다는 점에 유의하십시오 . 이는 특히 UTF-8과 같은 멀티 바이트 인코딩을 사용하는 경우 많은 스크립트에 적합하지 않습니다.
char
로를 ::tolower(int)
.) 당신은 당신이 음의 값을 전달하지 않도록해야합니다.
::tolower
은 잘 충돌 할 수 있습니다. 비 ASCII 입력의 경우 UB입니다.
#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);
to_lower_copy
tl; dr
ICU 라이브러리를 사용하십시오 . 그렇지 않은 경우 기존 루틴조차 모르는 경우 변환 루틴이 자동으로 중단됩니다.
먼저 질문에 대답해야합니다 : 인코딩 은 무엇입니까 std::string
? ISO-8859-1입니까? 아니면 ISO-8859-8입니까? 아니면 Windows Codepage 1252? 대문자에서 소문자로 변환하는 데 사용하는 것이 무엇인지 알고 있습니까? (또는 이상 문자에 대해 비참하게 실패 0x7f
합니까?)
std::string
컨테이너로 UTF-8 (8 비트 인코딩 중 유일한 선택)을 사용 하는 경우 컨테이너에 멀티 바이트 문자 시퀀스를 저장하기 때문에 여전히 사물을 제어하고 있다고 믿지 않습니다. 그것은 멀티 바이트 개념을 인식하지 못합니다. .substr()
시한 폭탄 처럼 단순한 것조차도 . (멀티 바이트 시퀀스를 분할하면 유효하지 않은 (하위) 문자열이됩니다.)
이 같은 노력으로 그리고 즉시 std::toupper( 'ß' )
에, 어떤 인코딩을, 당신은 곤경에 있습니다. 표준 라이브러리로는이 "올바른"작업을 수행 할 수 없기 때문에 여기 에는 필요 하지 않은 하나의 결과 문자 만 제공 할 수 "SS"
있습니다. [1] 로케일에 따라std::tolower( 'I' )
다른 결과를 얻을 수있는 또 다른 예가 있습니다 . 독일에서는 정확할 것입니다. 터키에서는 (LATIN SMALL LETTER DOTLESS I)이 예상되는 결과입니다 (UTF-8 인코딩에서 1 바이트 이상임). 또 다른 예로는 그리스어 시그마 , 대문자 , 소문자 등 이 있습니다.'i'
'ı'
'∑'
'σ'
'ς'
따라서 한 번에 한 문자 또는 한 번에 한 바이트 씩 작동하는 모든 경우 변환 은 설계에 의해 중단됩니다.
그런 다음 지점이 그것이 무엇을위한 표준 라이브러리 이며 , 로케일이되는에 따라되는 일을 할 수있는 지원 소프트웨어가 실행중인 컴퓨터에를 ... 그리고, 그렇지 않은 경우 당신은 무엇을해야합니까?
그래서 당신이 정말 찾고 올바르게 모든 처리 할 수있는 캐릭터 클래스는, 그는 없는 임의의 std::basic_string<>
변형 .
(C ++ 11 참고 : std::u16string
하고 std::u32string
있습니다 더 나은 .하지만 아직 완벽하지 C ++ (20)는 가져왔다 std::u8string
,하지만이 모든 할 일이 인코딩을 지정이다 다른 많은면에서 그들은 여전히 정상화, 정렬과 같은 유니 코드 역학의 무지 남아 ... .)
Boost 는 API와 비슷하게 보이지만 Boost.Locale은 기본적으로 ICU를 감싸는 래퍼 입니다. 경우 부스트가되어 컴파일 된 ICU 지원, 그렇지 않은 경우는 ... Boost.Locale는 표준 라이브러리의 컴파일 된 로케일 지원 제한됩니다.
그리고 ICU로 컴파일하도록 Boost를 얻는 것은 때때로 고통 스러울 수 있습니다. (Windows 용 사전 컴파일 된 바이너리는 없으므로 응용 프로그램과 함께 제공해야 하며 완전히 새로운 웜 캔 을 엽니 다 ...)
따라서 개인적으로 말의 입에서 직접 유니 코드를 완벽하게 지원하고 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
이것은 다음을 제공합니다.
ὀδυσσεύς
단어 중간의 Σ <-> σ 변환 및 단어 끝의 Σ <-> ς 변환에 유의하십시오. 어떤 <algorithm>
기반 솔루션도 당신에게 줄 수 없습니다 .
[1] 2017 년 독일 정형 외과위원회는 "+"U + 1E9E 라틴 대문자 레터 샤프 S를 공식적으로 사용할 수 있으며, 예를 들어 여권 (예 : 이름이 대문자 인 경우)에서 모호함을 피하기 위해 전통적인 "SS"변환 옆의 옵션으로 사용할 수 있다고 판결했습니다. ). 위원회 결정에 의해 쓸모없는 나의 아름다운 예가 ...
toupper
그리고 tolower
여전히 단일 문자 작동합니다. 문자열 클래스에는 여전히 정규화 개념이 없습니다 (예 : "ü"가 "u with diaeresis"또는 "u + Combine diaeresis"로 인코딩되는지 여부) 또는 문자열이 분리되거나 분리되지 않을 수 있습니다. 목록은 계속됩니다. u8string은 (다른 표준 문자열 클래스와 마찬가지로) "통과"에 적합합니다. 그러나 유니 코드 를 처리 하려면 ICU 가 필요합니다 .
C ++ 11의 범위 기반 for 루프를 사용하면 더 간단한 코드는 다음과 같습니다.
#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);
}
문자열에 ASCII 범위를 벗어난 UTF-8 문자가 포함되어 있으면 boost :: algorithm :: to_lower가 해당 문자를 변환하지 않습니다. UTF-8이 관련된 경우 boost :: locale :: to_lower를 사용하는 것이 좋습니다. http://www.boost.org/doc/libs/1_51_0/libs/locale/doc/html/conversions.html을 참조 하십시오
이것은 Stefan Mai의 응답에 대한 후속 조치입니다. 변환 결과를 다른 문자열에 배치하려면을 호출하기 전에 저장 공간을 미리 할당해야합니다 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;
}
내가 아는 한 Boost 라이브러리는 실제로 성능면에서 좋지 않습니다. 나는 unorder_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 : 공백이 있거나없는 문자열
#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;
}
std::ctype::tolower()
표준 C ++ Localization 라이브러리에서 올바르게 수행합니다. 다음은 참조 페이지 에서 추출한 예입니다 .
#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() )
?
tolower
과 locale
매개 변수에 대한 암시 적 호출 use_facet
이 나타납니다이 성능 병목합니다. 동료 중 한 명이 루프 외부에서 한 번만 호출 boost::iequals
되는 버전으로 (이 문제가있는) 대체하여 100 % 속도가 몇 배 증가했습니다 use_facet
.
Boost의 대안은 POCO (pocoproject.org)입니다.
POCO는 두 가지 변형을 제공합니다.
두 버전 모두 아래에 설명되어 있습니다.
#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);
if tests를 수행하지 않고 대문자를 소문자로 변환하는 방법이 있으며 매우 간단합니다. clocale.h를 사용하는 isupper () 함수 / 매크로는 사용자의 위치와 관련된 문제를 처리해야하지만, 그렇지 않은 경우 언제든지 UtoL []을 마음의 내용으로 조정할 수 있습니다.
C의 문자는 실제로 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;
}
이 방법을 사용하면 변경하려는 다른 문자를 다시 매핑 할 수 있습니다.
이 접근 방식은 최신 프로세서에서 실행할 때 큰 이점이 있으며 분기를 포함하는 if 테스트가 없으므로 분기 예측을 수행 할 필요가 없습니다. 이는 다른 루프에 대한 CPU의 분기 예측 로직을 저장하고 파이프 라인 중단을 방지하는 경향이 있습니다.
여기서 일부는이 방법을 EBCDIC을 ASCII로 변환하는 데 사용 된 것과 동일한 방식으로 인식 할 수 있습니다.
답변 중에 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 에서 영감을 얻었습니다 .
from의 다른 모든 함수와 마찬가지로 인수 값이로 표현할 수 없거나 같지 않은 경우
<cctype>
동작std::tolower
은 정의되지 않습니다 . 이러한 함수를 plain 또는 s 와 함께 안전하게 사용하려면 먼저 인수를 다음 으로 변환해야합니다 .unsigned char
EOF
char
signed char
unsigned char
char my_tolower(char ch) { return static_cast<char>(std::tolower(static_cast<unsigned char>(ch))); }
마찬가지로 반복자의 값 유형이
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; }
대문자 / 소문자를 수행하는 자체 템플릿 기능.
#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을 지원하는 와이드 문자에 방금 사용했습니다 .
간단한 것을 원한다면 매크로 기술이 있습니다.
#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 문자가 아닌 작업을 수행하는 경우 중요한 고려 사항입니다.
void strtoupper(std::string& x) { std::transform (x.begin(), x.end(), x.begin(), ::toupper); }
x
올바른 식일 수 있습니다.이 식은 올바르게 컴파일되지만 매크로 때문에 완전히 잘못된 결과를 제공합니다.
// 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;
}
100 % 효과가있는 대안이 있습니까?
아니
소문자 방법을 선택하기 전에 스스로에게 물어볼 몇 가지 질문이 있습니다.
이러한 질문에 대한 답변이 있으면 필요에 맞는 soloution을 찾을 수 있습니다. 어느 곳에서나 모든 사람에게 적합한 크기는 없습니다!
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 );
}
fplus :: to_lower_case ()를 사용하십시오.
(fplus : https://github.com/Dobiasd/FunctionalPlus .
에서 검색 'to_lower_case' http://www.editgym.com/fplus-api-search/ )
fplus::to_lower_case(std::string("ABC")) == std::string("abc");
답변을 개선 할 수 없었기 때문에 복사하십시오. 감사합니다
string test = "Hello World";
for(auto& c : test)
{
c = tolower(c);
}
설명:
for(auto& c : test)
종류 의 범위 기반 for 루프 입니다 .
for (
range_declaration
:
range_expression
)
loop_statement
range_declaration
: auto& c
여기서 자동 지정 자는 자동 유형 공제에 사용됩니다. 따라서 변수 초기화 프로그램에서 유형이 차감됩니다.
range_expression
: test
이 경우의 범위는 문자열의 문자입니다 test
.
문자열의 문자 test
는 for loop through identifier 내부의 참조로 사용 가능 c
합니다.
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;
}
문자열에서 문자 기반 작업의 경우 : 문자열의 모든 문자
이것은 대문자를 소문자로 또는 그 반대로 변환하는 또 다른 간단한 버전 일 수 있습니다. 이 소스 코드를 컴파일하기 위해 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;
}
참고 : 특수 문자가있는 경우 조건 확인을 사용하여 처리해야합니다.
나는 std :: transform을 시도했는데, 200 년 전에 만 드루이드 만 이해할 수있는 가증스러운 stl criptic 컴파일 오류입니다 (flibidi flabidi flu로 변환 할 수 없음)
이것은 잘 작동하고 쉽게 조정할 수 있습니다
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;
}