Qt wiki will be updated on October 12th 2023 starting at 11:30 AM (EEST) and the maintenance will last around 2-3 hours. During the maintenance the site will be unavailable.
Strings and encodings in Qt/ko
This article may require cleanup to meet the Qt Wiki's quality standards. Reason: Auto-imported from ExpressionEngine. Please improve this article if you can. Remove the {{cleanup}} tag and add this page to Updated pages list after it's clean. |
Qt 에서의 문자열과 인코딩
이 문서는 인코딩에 대한 간단한 소개의 글이며, Qt 내에서(대개 C/C+프로젝트에서) 옳바로 사용하는 방법에 대한 내용을 다룬다.
아직 원본편집/번역작업중 입니다. 이 문서에 대한 논의는 여기 에서 볼 수 있습니다.
간단 설명
- 간단한 텍스트라는 건 없다!(There Ain't No Such Thing As Plain Text)
- 소스코드의 인코딩을 UTF-8로 하자(Encode your source code files using UTF-8). (편집기등) 모든 자신이 사용하는 도구들을 UTF-8 파일을 읽고 쓰도록 한다. 모든 코드베이스에 대해 일단 이렇게 해 놓으면, 다른 이들도 여기에 동참할 수 밖에 없을 것이다.
- 컴파일러로 하여금 UTF-8 파일을 읽도록 하자. 로케일을 UTF-8로 하면 충분하다.(만일 아니라면, 컴파일러 매뉴얼을 참조해야 할 것이다. 역자주: VC에 대해서는 여기 를 참조).
- 코드내 모든 문자열 리터럴에 대해 *QString::fromUtf8()*을, 그리고 사용자가 보게될 문자열에 대해서는 trUtf8() / QT_TR_UTF8() 등을 사용한다.
- QString(const char *) 생성자[1]를 사용하고 싶다면, QTextCodec::setCodecForCStrings() 에 UTF-8 텍스트코덱(QTextCodec::codecForName("UTF-8")로 얻어진…)을 전달하면서 호출한다. 이는 응용프로그램 전체에 대한 설정이므로, 해당 프로그램에서 사용하는 (Qt)플러그인이나 3rdparty Qt라이브러리에 대해서도 이 내용이 설정된다. 이는 때때로, 잘못 작성된 플러그인이나 라이브러리코드의 오동작을 유발하기도 한다.
상세한 설명
C/C에서의 인코딩
C/C+ 에서 문자열 리터럴의 인코딩이란?
우선, C/C++ 사양은 프로그램내에서 문자열 리터럴, 또는 소스코드 자체의 인코딩에 대해 그리 많은 것을 다루지는 않고 있다. 그저 *입력 문자셋" 과 실행 문자셋 에 대한 요구사항만 몇개 다루고 있다.
그럼?
컴파일러는 이 문제를 다루기 위해 몇가지 기법을 수용하고 있다. 우선, 소스파일의 각 바이트들을 적절히 디코딩해 C/C++ 프로그램을 만들 수 있어야 한다(파서가 프로그램을 파싱할 수 있도록). 이는 대개 현재의 로케일 설정을 사용해 이루어지거나, 아무 지시사항이 없다면, UTF-8을(ASCII호환성이 있다) 사용하게 된다. GCC를 사용한면, -findput-charset= 커맨드라인 옵션을 사용해 입력파일(대개 소스코드) 인코딩을 지정할 수 있다.
그런 다음?
일단 소스파일이 파싱되고 나면, 컴파일러는 자신만의 표현방식으로 문자열 리터럴을 저장하게 된다. 컴파일러를 쓰는 사람의 입장에서는 그저 이 표현방식이 알파벳을 사용해 그 어떤 심벌도 표현할 수 있다는 정도만 알고 있으면 되겠다. 사실, 이 표현방식은 유니코드 인코딩이다.
최종적으로, 컴파일러는 문자열에 대한 이진 바이트들의 시퀀스로 이루어진 어셈블리코드를 만든다. 그러므로, 컴파일러는 문자열을 실행 인코딩을 사용해 인코딩해야 한다. GCC를 사용한다면, -fexec-charset= 및 -fwide-exec-charset= 커맨드라인 옵션을 사용해 char[] 리터럴이나, wchar_t[] 리터럴에 대한 인코딩 방식을 각각 지정할 수 있다.[2]
이런 커맨드라인 옵션의 적용범위는 어디까지 인가?
"컴파일러를 한번 실행 할 때마다", 즉 "하나의 번역단위(translation unit)"이다. 입력 및 실행 문자셋을 모든 번역단위별로 다 다르게도 할 수 있는 것이다.
잠시만… 그럼 는 건 정확히 먼가?
The escape sequence inside a string literal encodes the code point U+XXXX using the execution character set (similarly, the encodes the code point U+XXXXXXXX). Note that s, in general, a prefix for an universal character, and you're allowed to use it even outside string literals. 이스케이프 시퀀스는 문자열 리터럴내에서 U+XXXX 코드 포인트를 실행 문자셋을 사용해 인코딩한다. (비숫하게, 코드 포인트 U+XXXXXXXX를 인코딩한다). 유의할 점은, 대개, 유니버설 문자에 대한 첨두문자(prefix)이며, 문자열 밖에서 이를 사용할 수 있다는 점이다(역자주: 즉, 인용부호 밖에서).
Qt(이를 테면, QString)는 바이트 배열(char*)의 인코딩을 어떻게 알아내는가?
이 질문에 대한 답은 간단하다. Qt는 모른다*는 것이다. 예를 들면, QString(const char*) 생성자는 QTextCodec::codecForCStrings()가 호출되어 설정된 코덱이 있다면 그것을 써서, 그렇지 않다면, Latin-1 코덱을 사용해 넘어온 데이터를 인코딩하게 된다. 이 부분은 때론 심각한 문제를 야기한다.
- 정적 QString::fromLatin1() 메소드는 Latin-1 인코딩된 데이터로 부터 문자열을 생성한다.
- 정적 QString::fromUtf8() 메소드는 UTF-8 인코딩된 데이터로 부터 문자열을 생성한다.
- tr() 메소드는 QTextCodec::codecForTr()이 호출되어 설정된 코덱이 있다면 그것을 써서, 그렇지 않다면 Latin-1 을 사용해 입력 데이터로 부터 문자열을 생성한다.
- lupdate 도구는 .pro 파일내에 CODECFORTR 및 CODECFORSRC 로 지정된 설정으로 부터 tr()내의 문자열과 소스파일의 인코딩을 결정한다.
- 다른 인코딩 스킴을 위해서는 적절한 QTextCodec을 생성해야만 한다(QTextCodec::codecForName() 또는 이와 유사한 메소드를 사용해). 그런다음 toUnicode() 메소드를 사용하여 문자열 변환을 할 수 있다.
QString
QString 은 무엇인가?
QString 은 재진입가능한, 암시적으로 공유된 UTF-16 으로 인코딩된 문자열의 저장소이다. 이 말은…
- 반복자(iterator)뿐 아니라 인덱스(index)를 받는 모든 메소드들은 코드 포인트가 아닌 코드 단위(code units) 로 동작한다
- QString 은 무효한 데이터를 포함하고 있을 수 도 있다(예를 들면, surrogate쌍이 깨진것과 같은…)
- …
QString(const char *) 생성자는 무슨 문제가 있나?
fromAscii() 함수를 사용해 전달된 문자열의 바이트를 디코딩하면, — 이름과는 달리 — QTextCodec::codecForCString() 에 의해 반환된 코덱을 사용한다(사용자가 아무런 관련 설정을 하지 않았다면, 기본값인 Latin-1 이 사용된다). 따라서, 단순히 UTF-8 데이터를 전달하면 QString 은 전달된 데이터를 적절히 디코딩하지 못하게 된다.
Note also the codec set is application-wide — once you set it, you change the behaviour of fromAscii() in your code, in libraries you are using, in plugins you may have loaded, etc. If they're very poorly coded you may even break them by doing so (BT;DT). 코덱셋은 응용프로그램 전역적임에 유의하자. — 일단 설정되고 나면, 코드 내의 모든 fromAscii() 호출은 응용프로그램 코드, 라이버리, 플러그인 코드 모드에서 동일하게 적용된다. 이는 때때로, 잘못 작성된 플러그인이나 라이브러리코드의 오동작을 유발하기도 한다. (BT;DT).
그럼 무엇을 사용해야 하는가?
방법은 많다.
- 모든 코드 편집기의 설정을 UTF-8 로 바꾸고, 모든 코드베이스도 마찬가지로 변경한다(iconv(1) 와 같은 도구를 사용해).
컴파일러의 입력 및 실행 문자셋을 UTF-8 로 변경한 다음, 코드에서 QString::fromUtf8(), trUtf8(), 등을 사용한다. 이런 식으로 하면,
QString str = QString::fromUtf8("ßàéöø")<code> 과 같은 코드가 제대로 동작한다.
* 만일 소스코드가 7-bit 아스키여야만 한다면, 두자를 사용하거나, 수작업으로 인코딩을 수행하거나, 이스케이프 시퀀스를 사용해 데이터를 구성한 다음 QString::fromUtf8() 을 사용할 수 도 있겠다. 예를 들면,
QString str = QString::fromUtf8("3\xb7"); // str is "÷"
- 만일, 소스코드가 Latin-1 이고, 이를 건드리고 싶지도 않고, 다른 사람이 codecForCStrings() 설정을 코드내에서 수정한 것에도 영향받고 싶지 않다면, 정적 메소드인 QString::fromLatin1() 을 사용할 수 있다.
- 위와 같은 경우 QLatin1String (Latin-1 인코딩된 char* 문자열에 대한 랩퍼(Wrapper)) 를 쓰면 편하다.
다른 언어로 번역할 일이 없는 데도, tr() 이나 trUtf() 을 써야만 할까?
그렇다. 예를 들어, 복수형(plural forms)을 제대로 다루어야만 하는 경우 혹은 바퀴재발명을 하지 않으려면…
A small digression — what's an encoding?
In information theory, a code is nothing more than a rule — a function — to translate a discrete piece of information (usually, symbols from a source alphabet) into another format of representation (symbols or sequence of symbols of a target alphabet).
The process of converting a symbol from the source alphabet into its associated sequence of symbols of the target alphabet is called encoding; the opposite process is called decoding. We can naturally extend the encoding and the decoding definitions to sequence of symbols of the source alphabet (that is, a string of the source alphabet) into the corresponding sequence made of (sequence of) symbols of the target alphabet.
For instance, a very famous code is the Morse code, which translates letters in the Roman alphabet, Arabic numerals (digits) and some punctuation characters into sequences of dots and dashes.
{background:#009900}. |. Symbol |. Morse encoding | | A | . — | | B | — . . . | | 9 | — — — — . |
There are a certain number of properties that we'd like a code to have: its rule must be an injection, because we don't want to map two different pieces of information into the same representation (we wouldn't know how to translate it back); it should not be unnecessarily verbose ; it could add some error detection and/or error correction schemes ; etc.
A character encoding is no different: it's a rule to translate symbols from (usually) a human alphabet into sequences of bits or bytes[3]. The most famous character encoding is probably US-ASCII, which maps Roman letters, Arabic numerals, punctuation and some control characeters to the numbers in the range 0—127, and thus, requiring 7 bits in binary.
Usually an ASCII-encoded character is encoded in a full byte with the most significant bit is set to 0, thus allowing us to index symbols in a ASCII-encoded string by simply indexing bytes.[4]
As you may have guessed, US-ASCII is not suitable for anything but American English — it lacks all additional characters needed to deal with other languages. The ISO/IEC 8859 series of standards extended US-ASCII by encoding additional 128 symbols with the numbers in the range 128—155, that is, using the other half of values available inside a byte.
Many other encodings exist out there , and writing down a comprehensive list of them is almost impossible. Moreover, anyone can invent his own encoding for an arbitrary set of symbols. This has simply led to many problems when a piece of software had to deal with multiple writing systems (for instance, any browser).
Enter Unicode
Unicode is a industry standard that tries to cover as many writing systems as possible in a unified manner, not only in terms of supported symbols, but also in terms of rules for character normalization, character decomposion, string collation (i.e. ordering), bidirectional writing, and so on.
All the symbols inside the Unicode standard are called the the Universal Character Set (UCS) and they are defined by the ISO/IEC 10646 standard. Every symbol has a number in the range from 0x000000 to 0x10FFFF[5], which is called a code point; the standard notation for naming Unicode code points is U+XXXX (where XXXX are hex digits).
What's so great about Unicode is that you can stop caring about all the details of the various writing systems. Just use an Unicode-compliant library and let it do all the harsh work for you. All in all
Unicode encodings
A code point is still a virtual entity; it's not an encoding. The Unicode standard defines several standard encodings for code points.
Notes
- ↑ You don't. Disable those constructors by defining QT_NO_CAST_FROM_ASCII and QT_NO_CAST_TO_ASCII in your codebase.
- ↑ This whole story isn't 100% accurate — as Thiago Macieira kindly pointed out in his blog entry , if you don't specify any encoding option GCC actually outputs the same bytes it read from the source file.
- ↑ … or octets, but for brevity's sake let's just assume that a byte is always 8 bits.
- ↑ Other uses have been suggested for the remaining bit in a byte, for instance parity check.
- ↑ Not all numbers in the range are used, though.