Programing

std :: string을 소문자로 변환하는 방법?

lottogame 2020. 9. 30. 08:38
반응형

std :: string을 소문자로 변환하는 방법?


a std::string를 소문자 로 변환하고 싶습니다 . 나는 기능을 알고 tolower()있지만 과거에는이 기능에 문제가 있었으며 a와 함께 사용하면 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()멀티 바이트 인코딩 UTF-8 등을 사용하여 특히, 단지 많은 스크립트 맞지되는 당 단일 바이트 문자 대체를 할 수 있습니다.


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

tl; dr

ICU 라이브러리를 사용합니다 .


먼저 질문에 답해야합니다. 인코딩무엇입니까 std::string? ISO-8859-1입니까? 아니면 ISO-8859-8일까요? 아니면 Windows Codepage 1252? 대문자를 소문자로 변환하는 데 사용하는 것이 무엇이든 알고 있습니까? (아니면 캐릭터가 비참하게 실패 0x7f합니까?)

std::string컨테이너로 UTF-8 (8 비트 인코딩 중 유일한 올바른 선택)을 사용 하는 경우 컨테이너에 멀티 바이트 문자 시퀀스를 저장하고 있기 때문에 여전히 사물을 제어하고 있다고 믿도록 속이는 것입니다. 멀티 바이트 개념을 알지 못합니다. .substr()똑딱 거리는 시한 폭탄 처럼 단순한 것조차 . (멀티 바이트 시퀀스를 분할하면 유효하지 않은 (서브) 문자열이 생성되기 때문입니다.)

그리고 어떤 인코딩 에서든std::toupper( 'ß' ) , 같은 것을 시도하자마자 심각한 문제에 봉착합니다. (그것은 단지 제공 할 수있는 표준 라이브러리이 "오른쪽", 할 단순히 불가능하기 때문에 하나 개의 결과를 문자가 아닌 필요한 여기를.) [1] 또 다른 예는 것 다른 결과가 산출해야하는, 로케일에 따라 . 독일에서는 정확합니다. 터키에서는 (LATIN SMALL LETTER DOTLESS I)이 예상되는 결과입니다 (다시 말하지만 UTF-8 인코딩에서 1 바이트 이상임)."SS"std::tolower( 'I' )'i''ı'

그렇다면 표준 라이브러리가 소프트웨어가 실행중인 컴퓨터에서 지원 되는 로케일에 따라 달라진다는 점이 있습니다. 그렇지 않은 경우 어떻게합니까?

그래서 당신이 정말로 찾고있는 것은이 모든 것을 정확하게 다룰 수있는 문자열 클래스이며 , 그것은 어떠한 변형도 아닙니다std::basic_string<> .

(C ++ 11 참고 : std::u16stringand std::u32stringare better , but still not perfect. C ++ 20 bring std::u8string, but these do is that are specified the encoding. 다른 많은 측면에서 그들은 정규화, 데이터 정렬, .. .)

Boost 멋지게 보이지만 API는 현명하지만 Boost.Locale은 기본적으로 ICU를 둘러싼 래퍼 입니다. 경우 부스트가되어 컴파일 된 ICU 지원, 그렇지 않은 경우는 ... Boost.Locale는 표준 라이브러리의 컴파일 된 로케일 지원 제한됩니다.

그리고, 날 믿어 지고 ICU 때로는 진짜 고통이 될 수 컴파일하는 부스트. (Windows 용으로 미리 컴파일 된 바이너리가 없으므로 애플리케이션과 함께 제공해야합니다. 그러면 완전히 새로운 웜 캔 열립니다 ...)

그래서 개인적으로 말의 입에서 바로 완전한 유니 코드 지원을 받고 ICU 라이브러리를 직접 사용하는 것이 좋습니다 .

#include <unicode/unistr.h>
#include <unicode/ustream.h>
#include <unicode/locid.h>

#include <iostream>

int main()
{
    char const * someString = "Eidenges\xe4\xdf";
    icu::UnicodeString someUString( someString, "ISO-8859-1" );
    // Setting the locale explicitly here for completeness.
    // Usually you would use the user-specified system locale.
    std::cout << someUString.toLower( "de_DE" ) << "\n";
    std::cout << someUString.toUpper( "de_DE" ) << "\n";
    return 0;
}

컴파일 (이 예에서는 G ++ 사용) :

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

이것은 다음을 제공합니다.

eidengesäß
EIDENGESÄSS

[1] 2017 년에 독일 정사 협의회는 "ẞ"U + 1E9E 라틴어 대문자 샤프 S를 공식적으로 사용할 수 있다고 판결했습니다. 예를 들어 여권 (이름이 대문자 인 경우)의 모호성을 피하기 위해 전통적인 "SS"변환 옆의 옵션으로 ). 위원회의 결정에 의해 쓸모 없게 된 나의 아름다운 모범 사례 ...


문자열에 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를


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

이것은 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;
}

참조 변수가있는 범위 기반 for 루프를 사용하는 또 다른 방법

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

cout<<test<<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를 사용할 것이므로 실제로 내 머신에서 테스트 할 필요가 없습니다.


std 네임 스페이스를 신경 쓰지 않고 문자열을 소문자로 변환하는 가장 간단한 방법은 다음과 같습니다.

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 ++ 지역화 라이브러리에서이 작업을 올바르게 수행합니다. 다음은 아래 참조 페이지 에서 추출한 예입니다 .

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

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

if tests를 수행하지 않고 대문자를 소문자로 변환하는 방법이 있으며 매우 간단합니다. isupper () 함수 / 매크로의 clocale.h 사용은 위치와 관련된 문제를 처리해야하지만 그렇지 않은 경우 언제든지 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로 변환하는 데 사용되는 것과 동일한 방법으로 인식 할 수 있습니다.


간단한 것을 원한다면 매크로 기술이 있습니다.

#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 문자가 아닌 작업을 수행하는 경우에는 여전히 중요합니다.


답변 중에 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>및 필수 범위 헤더를 잊지 마십시오 .)

참고 : unsigned char람다에 대한 인수로 의 사용은 cppreference 에서 영감을 얻었습니다 .

의 다른 모든 함수와 마찬가지로 인수의 값이로 표현할 수 없거나 같지 않은 경우 <cctype>의 동작 std::tolower은 정의되지 않습니다 . 이러한 함수를 일반 s (또는 s) 와 함께 안전하게 사용하려면 먼저 인수를 다음 으로 변환해야합니다 .unsigned charEOFcharsigned charunsigned char

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

마찬가지로 반복기의 값 유형이 char또는 인 경우 표준 알고리즘과 함께 직접 사용해서는 안됩니다 signed char. 대신 값을 unsigned charfirst 로 변환하십시오 .

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

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


100 % 작동하는 대안이 있습니까?

아니

소문자 방법을 선택하기 전에 스스로에게 물어볼 몇 가지 질문이 있습니다.

  1. 문자열은 어떻게 인코딩됩니까? 일반 ASCII? UTF-8? 확장 ASCII 레거시 인코딩의 어떤 형태?
  2. 어쨌든 소문자는 무엇을 의미합니까? 대소 문자 매핑 규칙은 언어마다 다릅니다! 사용자 로케일로 지역화 된 것을 원하십니까? 소프트웨어가 실행되는 모든 시스템에서 일관되게 작동하는 것을 원하십니까? ASCII 문자를 소문자로하고 다른 모든 것을 통과 시키시겠습니까?
  3. 어떤 라이브러리를 사용할 수 있습니까?

이러한 질문에 대한 답을 얻으면 필요에 맞는 솔루션을 찾을 수 있습니다. 모든 곳에서 모든 사람에게 적합한 하나의 크기는 없습니다!


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

코드 스 니펫

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

fplus :: to_lower_case ()를 사용하십시오.

(fplus : https://github.com/Dobiasd/FunctionalPlus .

http://www.editgym.com/fplus-api-search/ 에서 'to_lower_case'검색 )

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

  1. range_declaration: auto& c
    여기서 자동 지정 자는 자동 유형 추론에 사용됩니다. 따라서 유형은 변수 이니셜 라이저에서 공제됩니다.

  2. range_expression: test
    이 경우 범위는 문자열의 문자입니다 test.

문자열의 문자는 test식별자를 통해 for 루프 내에서 참조로 사용할 수 있습니다 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;
}

문자열에 대한 문자 기반 연산의 경우 : 문자열의 모든 문자에 대해


대 / 소문자를 수행하는 나만의 템플릿 기능.

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

이것은 대문자를 소문자로 또는 그 반대로 변환하는 또 다른 간단한 버전 일 수 있습니다. 이 소스 코드를 컴파일하기 위해 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;
}

//You can really just write one on the fly whenever you need one.
#include <string>
void _lower_case(std::string& s){
for(unsigned short l = s.size();l;s[--l]|=(1<<5));
}
//Here is an example.
//http://ideone.com/mw2eDK

참고 URL : https://stackoverflow.com/questions/313970/how-to-convert-stdstring-to-lower-case

반응형