문자열에서 사용하는 문자 셀의 수
UTF-8 문자열을 사용하여 텍스트 표를 출력하는 프로그램이 있는데 문자열에서 사용하는 단좌표 문자 셀의 수를 측정해야 제대로 정렬할 수 있습니다.가능하다면 표준 기능으로 하고 싶습니다.
UTF-8 및 Unix/Linux용 Unicode FAQ:
를 사용하여 휴대용 방식으로 C로 문자 수를 셀 수 있습니다.
mbstowcs(NULL,s,0)
. 이것은 적절한 로케일이 선택되어 있는 한 다른 지원되는 인코딩과 마찬가지로 UTF-8에서도 작동합니다.UTF-8 문자열의 문자 수를 세는 유선 연결된 기술은 0x80 ~ 0xBF 범위의 바이트를 제외한 모든 바이트를 세는 것입니다. 이는 고유의 문자가 아닌 연속 바이트일 뿐이기 때문입니다.그러나 문자 수를 셀 필요성은 애플리케이션에서 놀라울 정도로 거의 발생하지 않습니다.
UTF-8 호환 strlen(3) 기능을 사용할 수도 있고 사용할 수도 없습니다.그러나 작업을 빠르게 수행할 수 있는 간단한 C 기능들이 있습니다.
효율적인 C 솔루션은 연속 바이트를 건너뛸 수 있도록 캐릭터의 시작을 검사합니다.간단한 코드(위 링크에서 참조)는
int my_strlen_utf8_c(char *s) {
int i = 0, j = 0;
while (s[i]) {
if ((s[i] & 0xc0) != 0x80) j++;
i++;
}
return j;
}
더 빠른 버전은 동일한 기술을 사용하지만 데이터를 프리페치하고 멀티바이트를 비교하므로 상당한 속도가 향상됩니다.그러나 코드는 더 길고 더 복잡합니다.
아무도 이런 얘기를 하지 않았다는 게 충격적입니다. 그래서 여기 기록을 남깁니다.
단말기에서 텍스트를 정렬하려면 POSIX 기능과 를 사용해야 합니다.다음은 문자열의 화면 길이를 찾는 올바른 프로그램입니다.
#define _XOPEN_SOURCE
#include <wchar.h>
#include <stdio.h>
#include <locale.h>
#include <stdlib.h>
int measure(char *string) {
// allocate enough memory to hold the wide string
size_t needed = mbstowcs(NULL, string, 0) + 1;
wchar_t *wcstring = malloc(needed * sizeof *wcstring);
if (!wcstring) return -1;
// change encodings
if (mbstowcs(wcstring, string, needed) == (size_t)-1) return -2;
// measure width
int width = wcswidth(wcstring, needed);
free(wcstring);
return width;
}
int main(int argc, char **argv) {
setlocale(LC_ALL, "");
for (int i = 1; i < argc; i++) {
printf("%s: %d\n", argv[i], measure(argv[i]));
}
}
실행 예는 다음과 같습니다.
$ ./measure hello 莊子 cAb
hello: 5
莊子: 4
cAb: 4
두 개의 문자 "莊子"와 세 개의 문자 "cAb"(두 배 너비 A 참조)가 모두 4열인 방법을 주목합니다.
utf8everywhere.org 의 표현에 따르면,
화면에 나타나는 문자열의 크기는 문자열의 코드 포인트 수와 무관합니다.이를 위해서는 렌더링 엔진과 통신해야 합니다.코드 포인트는 단일 공간 글꼴 및 터미널에서도 하나의 열을 차지하지 않습니다.POSIX는 이를 고려했습니다.
Windows에 내장된 기능이 없습니다.wcwidth
콘솔 출력을 위한 함수. Windows 콘솔에서 다중 열 문자를 지원하려는 경우
당신은 휴대 가능한 구현을 찾아야 합니다. 윈도우 콘솔은 미친 해킹 없이는 유니코드를 지원하지 않기 때문에 포기합니다.wcwidth
타사 라이브러리를 사용할 수 있는 경우 IBM의 ICU 라이브러리를 살펴보십시오.
다음 코드는 잘못된 바이트 시퀀스를 고려합니다.문자열 데이터의 예는 ""표 3-8"에서 가져온 것입니다. 유니코드 표준 6.3의 UTF-8 Conversion"에서 U+FFFD 사용.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#define is_trail(c) (c > 0x7F && c < 0xC0)
#define SUCCESS 1
#define FAILURE -1
int utf8_get_next_char(const unsigned char*, size_t, size_t*, int*, unsigned int*);
int utf8_length(unsigned char*, size_t);
void utf8_print_each_char(unsigned char*, size_t);
int main(void)
{
unsigned char *str;
str = (unsigned char *) "\x61\xF1\x80\x80\xE1\x80\xC2\x62\x80\x63\x80\xBF\x64";
size_t str_size = strlen((const char*) str);
puts(10 == utf8_length(str, str_size) ? "true" : "false");
utf8_print_each_char(str, str_size);
return EXIT_SUCCESS;
}
int utf8_length(unsigned char *str, size_t str_size)
{
int length = 0;
size_t pos = 0;
size_t next_pos = 0;
int is_valid = 0;
unsigned int code_point = 0;
while (
utf8_get_next_char(str, str_size, &next_pos, &is_valid, &code_point) == SUCCESS
) {
++length;
}
return length;
}
void utf8_print_each_char(unsigned char *str, size_t str_size)
{
int length = 0;
size_t pos = 0;
size_t next_pos = 0;
int is_valid = 0;
unsigned int code_point = 0;
while (
utf8_get_next_char(str, str_size, &next_pos, &is_valid, &code_point) == SUCCESS
) {
if (is_valid == true) {
printf("%.*s\n", (int) next_pos - (int) pos, str + pos);
} else {
puts("\xEF\xBF\xBD");
}
pos = next_pos;
}
}
int utf8_get_next_char(const unsigned char *str, size_t str_size, size_t *cursor, int *is_valid, unsigned int *code_point)
{
size_t pos = *cursor;
size_t rest_size = str_size - pos;
unsigned char c;
unsigned char min;
unsigned char max;
*code_point = 0;
*is_valid = SUCCESS;
if (*cursor >= str_size) {
return FAILURE;
}
c = str[pos];
if (rest_size < 1) {
*is_valid = false;
pos += 1;
} else if (c < 0x80) {
*code_point = str[pos];
*is_valid = true;
pos += 1;
} else if (c < 0xC2) {
*is_valid = false;
pos += 1;
} else if (c < 0xE0) {
if (rest_size < 2 || !is_trail(str[pos + 1])) {
*is_valid = false;
pos += 1;
} else {
*code_point = ((str[pos] & 0x1F) << 6) | (str[pos + 1] & 0x3F);
*is_valid = true;
pos += 2;
}
} else if (c < 0xF0) {
min = (c == 0xE0) ? 0xA0 : 0x80;
max = (c == 0xED) ? 0x9F : 0xBF;
if (rest_size < 2 || str[pos + 1] < min || max < str[pos + 1]) {
*is_valid = false;
pos += 1;
} else if (rest_size < 3 || !is_trail(str[pos + 2])) {
*is_valid = false;
pos += 2;
} else {
*code_point = ((str[pos] & 0x1F) << 12)
| ((str[pos + 1] & 0x3F) << 6)
| (str[pos + 2] & 0x3F);
*is_valid = true;
pos += 3;
}
} else if (c < 0xF5) {
min = (c == 0xF0) ? 0x90 : 0x80;
max = (c == 0xF4) ? 0x8F : 0xBF;
if (rest_size < 2 || str[pos + 1] < min || max < str[pos + 1]) {
*is_valid = false;
pos += 1;
} else if (rest_size < 3 || !is_trail(str[pos + 2])) {
*is_valid = false;
pos += 2;
} else if (rest_size < 4 || !is_trail(str[pos + 3])) {
*is_valid = false;
pos += 3;
} else {
*code_point = ((str[pos] & 0x7) << 18)
| ((str[pos + 1] & 0x3F) << 12)
| ((str[pos + 2] & 0x3F) << 6)
| (str[pos + 3] & 0x3F);
*is_valid = true;
pos += 4;
}
} else {
*is_valid = false;
pos += 1;
}
*cursor = pos;
return SUCCESS;
}
UTF-8의 코드를 작성하면 "표 3-7"이 나타납니다.유니코드 표준 6.3의 "잘 형성된 UTF-8 바이트 시퀀스".
Code Points First Byte Second Byte Third Byte Fourth Byte
U+0000 - U+007F 00 - 7F
U+0080 - U+07FF C2 - DF 80 - BF
U+0800 - U+0FFF E0 A0 - BF 80 - BF
U+1000 - U+CFFF E1 - EC 80 - BF 80 - BF
U+D000 - U+D7FF ED 80 - 9F 80 - BF
U+E000 - U+FFFF EE - EF 80 - BF 80 - BF
U+10000 - U+3FFFF F0 90 - BF 80 - BF 80 - BF
U+40000 - U+FFFFF F1 - F3 80 - BF 80 - BF 80 - BF
U+100000 - U+10FFFF F4 80 - 8F 80 - BF 80 - BF
UTF-8을 다룰 때 당신의 삶을 훨씬 더 쉽게 해주는 glib을 사용할 수도 있습니다.
언급URL : https://stackoverflow.com/questions/5117393/number-of-character-cells-used-by-string
'source' 카테고리의 다른 글
@ngrx/store로 상태 개체의 현재 값을 가져오는 방법은 무엇입니까? (0) | 2023.10.19 |
---|---|
Oracle이 데이터베이스 테이블의 행에 NaN을 추가하는 시기/이유 (0) | 2023.10.19 |
WooCommerce 가변상품 공지사항 이슈 - 상품 옵션을 선택해주세요 (0) | 2023.10.14 |
WooCommerce에서 모든 주문 상태의 민달팽이와 이름을 얻는 방법은? (0) | 2023.10.14 |
glibc의 fclose(NULL)이 오류를 반환하지 않고 세그먼트화 오류를 발생시키는 이유는 무엇입니까? (0) | 2023.10.14 |