LC_ALL=C 및 LC_ALL=C.utf8 정렬

LC_ALL=C 및 LC_ALL=C.utf8 정렬

Linux 정렬 명령은 C와 C.utf-8 로케일을 구별합니까?

정렬 매뉴얼에서는 LC_ALL=C를 사용하여 바이트 값별로 정렬하라고 나와 있지만 C.utf-8은 (ASCII뿐만 아니라) utf8 값도 허용한다는 것을 알 수 있습니다. 하지만 정렬 매뉴얼에서는 이 로케일 옵션을 전혀 참조하지 않습니다.

LC_ALL=C sort file.txt 및 LC_ALL=C.utf8 sort file.txt를 실행할 때 둘 사이에 차이점이 보이지 않습니다. 둘 다 파일에 utf 8 문자가 있는지 여부에 관계없이 작동하는 것 같습니다.

그럼 알려진 차이점이 있나요?

답변1

LC_ALL=C sort바이트 값으로 정렬합니다. ASCII1뿐만 아니라 모든 문자 세트로 작성된 입력을 바이트 값별로 정렬합니다.

UTF-8 인코딩에는 유니코드 코드 포인트로 정렬하는 것과 동일한 바이트 값으로 정렬하는 속성이 있습니다( memcmp()U+1234보다 큰 인코딩 또는 0x1234보다 작은 유니코드 코드 포인트를 찾을 수 있습니다).

C.utf-8또는 (내 경험상 후자가 더 일반적임) POSIX 표준화되지 않은 로케일이지만 발견되는 곳마다 문자 세트가 UTF-8이라는 점을 제외하면 C 로케일의 대부분의 속성을 갖는 로케일을 의미합니다 C.utf8.C.UTF-8

LC_ALL=C.UTF-8 sort입력은 코드 포인트를 기준으로 정렬되지만 비교하기 전에 UTF-8을 디코딩하거나 strcoll()/ strxfrm()무거운 시스템을 호출하게 되어 결국 UTF-8의 경우 를 사용하는 것만으로도 memcmp()충분하므로 노력이 낭비될 수 있습니다.

sortGNU 및 Linux를 커널로 사용하는 많은 비임베디드 운영 체제 사용 (GNU는 지원하지 않지만 GNU 지원 NUL 문자를 입력에 libc 추가함 ):sortstrcoll()

$ printf 'a\0£1\na\0€2\n' | LC_ALL=C ltrace -e strcoll -e memcmp sort
sort->memcmp("a\0\302\2431", "a\0\342\202\254", 5)                      = -1
a£1
a€2
$ printf 'a\0£1\na\0€2\n' | LC_ALL=C.UTF-8 ltrace -e strcoll -e memcmp sort
sort->strcoll("a", "a")                                                 = 0
sort->strcoll("\302\2431", "\342\202\2542")                             = -31
a£1
a€2

(실제로 비교하려는 두 문자열의 바이트 수가 같은 경우 GNU는 두 문자열 이 동일한 경우 호출하기 전에 호출 sort합니다 . 이는 에 비해 비용이 저렴하기 때문입니다 .)memcmp()strcoll()memcmp()strcoll()

이 출력의 일부 타이밍은 1,000,000번 반복됩니다.

$ printf 'a\0£1\na\0€2\n%.0s' {1..1000000} > file.test
$ wc -mc file.test
10000000 13000000 file.test
$ time LC_ALL=C sort file.test > /dev/null
LC_ALL=C sort file.test > /dev/null  0.74s user 0.06s system 390% cpu 0.205 total
$ time LC_ALL=C.UTF-8 sort file.test > /dev/null
LC_ALL=C.UTF-8 sort file.test > /dev/null  6.04s user 0.12s system 522% cpu 1.179 total

따라서 UTF-8로 인코딩된 텍스트를 코드 포인트별로 정렬하려면 을 사용하거나 C기능상 C.UTF-8아무런 차이가 없지만 구현 C에 따라 사용하는 것이 더 효율적일 수 있습니다.sort

이제 모든 바이트 시퀀스가 ​​유효한 UTF-8을 형성하는 것은 아니므로 UTF-8이 아닌 입력(즉, UTF-8로 디코딩할 수 없는 바이트 시퀀스가 ​​포함된 입력)이 포함될 때 동작에 차이가 있을 수 있습니다. C여전히C.UTF-8 GNU 시스템에서는:

$ print -l 'a\200b' 'a\201b' | LC_ALL=C sort -u
a�b
a�b
$ print -l 'a\200b' 'a\201b' | LC_ALL=C.UTF-8 sort -u
a�b

(여기서 �는 내 터미널 에뮬레이터가 알 수 없는 것을 표현한 것입니다)

C.UTF-8에서 strcoll()유효한 UTF-8 텍스트를 형성하지 않는 두 문자열에 대해 0을 반환하면 실제로는 동일한 정렬 순서를 갖는 것으로 보고됩니다.

C 언어 환경에서는 LINE_MAX0이 아닌 바이트 시퀀스로 구성되고 길이가 바이트를 초과하지 않는 모든 줄은 유효한 텍스트입니다. C.UTF-8에는 추가 제한 사항이 있습니다. 이는 a\200bUTF-8에서는 유효하지 않으므로 텍스트가 아니므로 sortPOSIX에 따라 해당 동작이 지정되지 않습니다.

참고로 GNU 시스템에서는 메시징 언어보다 LC_ALL=C우선 하지만 우선 순위는 없습니다.$LANGUAGELC_ALL=C.UTF-8

$ LC_ALL=C LANGUAGE=fr:es:en sort /
sort: read failed: /: Is a directory
$ LC_ALL=C.UTF-8 LANGUAGE=fr:es:en sort /
sort: échec de lecture: /: est un dossier

또한 C로케일 문자 집합은 ASCII를 기반으로 할 필요는 없으며 ASCII는 0부터 127까지의 값만 포함한다는 점에 유의하세요. CASCII를 사용하는 로케일은 정의되지 않은 문자이기는 하지만 여전히 바이트 128~255를 문자로 처리합니다. 그러나 로캘 C문자 집합은 문자당 1바이트를 보장해야 하므로 C로캘 문자 집합은 UTF-8이 될 수 없습니다.

관련 정보