문자열에서 문자를 수집하고 해당 유니코드를 인쇄합니다.

문자열에서 문자를 수집하고 해당 유니코드를 인쇄합니다.

문맥(관심하지 않으시면 건너뛰세요. 제가 완전히 틀릴 것 같다면 계속 읽어보세요.)

메모리가 작은 임베디드 시스템의 경우 실제로 필요한 글리프만 포함된 글꼴을 생성하고 싶습니다. 따라서 빌드 시 언어 파일을 스캔하고 문자열에서 문자를 추출한 다음 해당 코드를 글꼴 생성 도구에 대한 인수로 사용해야 합니다.

관련 문자열이 포함된 번역 파일(물론 이것은 단지 예일 뿐이지만 적어도 일부 유니코드 내용을 다룹니다.)

TEXT_1=Foo
TEXT_2=Bar
TEXT_3=Baz
TEXT_4=Ünicødé
TEXT_5=ελληνικά

예상 출력

0x42,0x61,0x72,0x42,0x61,0x7A,0x46,0x6F,0x6F,0xDC,0x6E,0x69,0x63,0xF8,0x64,0xE9,0x3B5,0x3BB,0x3BB,0x3B7,0x3BD,0x3B9,0x3BA,0x3AC

지금까지의 나의 접근 방식

스크립트는 내가 설명한 작업을 수행합니다. 즉, sed파일을 읽고, 문자열을 추출하고, 형식을 지정할 준비를 하고 printf, sort -u중복 항목을 제거합니다.

for char in $(sed "s/[^=]*=//;s/./'& /g" myLang.translation|sort -u); do
  printf "0x%02X\n" $char
done

이 예에서는 작동하지만 실제 파일에서는 추악하고 신뢰할 수 없으며 버그가 있고 속도가 느릴 수 있습니다. 따라서 더 나은 도구, 더 나은 접근 방식, 더 나은 것을 지정할 수 있습니까?

답변1

그리고 perl:

perl -C -lne '
  if (/=(.*)/) {$c{$_}++ for split //, $1}
  END{print join ",", map {sprintf "0x%X", ord$_} sort keys %c}
  ' your-file

다음을 제공합니다:

0x42,0x46,0x61,0x63,0x64,0x69,0x6E,0x6F,0x72,0x7A,0xDC,0xE9,0xF8,0x3AC,0x3B5,0x3B7,0x3B9,0x3BA,0x3BB,0x3BD
  • -C로케일이 UTF-8을 문자 맵으로 사용하는 경우 UTF-8 I/O를 수행하십시오.
  • -ln sed -n각 입력 줄에서 코드가 실행되는 모드입니다. -l입력에서 줄 구분 기호를 제거하고 출력에 다시 추가합니다( 수행 $\ = $/).
  • -e 'code'스크립트가 아닌 명령줄에서 실행할 코드를 지정합니다.
  • /=(.*)/일치시킬 행에는 하나 이상의 캡처(첫 번째 캡처 그룹) =가 처음 발생한 이후의 내용이 포함됩니다.$1
  • split //, $1빈 구분 기호를 사용하여 단일 문자로 분할합니다.
  • $c{$_}++ for that-above문자 목록을 반복하고 해당 연관 배열 요소를 증가시킵니다. %c문자를 해당 항목에 매핑합니다. 여기서는 그 수를 사용하지 않습니다.
  • END{code}, code끝까지 실행하는 것뿐입니다.
  • sort keys %c이 연관 배열의 키를 어휘적으로 정렬합니다.
  • map { code } @list각 요소에 코드를 적용하여 목록을 변환합니다.
  • ord$_문자의 숫자 값을 가져옵니다.
  • sprintf "0x%X"16진수(대문자 ABCDEF, 소문자 0x)로 형식을 지정합니다.
  • join ",", @list목록에 추가,
  • print인쇄하고 그 뒤에 $\(개행 문자)를 입력하세요.

zsh에서는 (아마 훨씬 덜 효율적일 것입니다):

$ set -o cbases -o extendedglob
$ LC_COLLATE=C
$ echo ${(j[,])${(ous[])"$(<your-file cut -sd= -f2- | tr -d '\n')"}/(#m)?/$(([#16]#MATCH))}
0x42,0x46,0x61,0x63,0x64,0x69,0x6E,0x6F,0x72,0x7A,0xDC,0xE9,0xF8,0x3AC,0x3B5,0x3B7,0x3B9,0x3BA,0x3BB,0x3BD

또는 외부 유틸리티를 사용하지 않고:

$ set -o cbases -o extendedglob
$ LC_COLLATE=C
$ echo ${(j[,])${(@ous[])${(f)"$(<your-file)"}#*=}/(#m)?/$(([#16]#MATCH))}
0x42,0x46,0x61,0x63,0x64,0x69,0x6E,0x6F,0x72,0x7A,0xDC,0xE9,0xF8,0x3AC,0x3B5,0x3B7,0x3B9,0x3BA,0x3BB,0x3BD
  • "$(<you-file)"IFS로 구분되지 않도록 후행 줄 바꿈을 제거하고 따옴표로 묶은 파일의 내용
  • ${(f)param}행으로 분할 f하여 행을 목록으로 가져옵니다.
  • ${array#*=}*=배열 요소에서 가장 짧은 선행 부분 일치를 제거합니다.
  • @보장하기 위한 플래그 목록처리
  • o어휘 순서(C 로케일의 코드 포인트 기준)
  • unique 중복 제거
  • s[]별도의 문자로 나눕니다.
  • ${array/(#m)?/$(([#16]#MATCH))}감사( )에 캡처된 문자를 ?16진수 형식의 값(산술 표현식에서)으로 바꿉니다. 이 옵션을 사용하면 다음을 수행할 수 있습니다.$MATCH(#m)#MATCH[#16]cbases0xBEEF16#BEEF
  • j[,]에 참여하세요 ,.

이를 별도의 단계로 나누면 더 명확해집니다.

set -o cbases -o extendedglob
LC_COLLATE=C
contents=$(<your-file)
lines=( ${(f)contents} )
values=( ${lines#*=} )
chars=( ${(@ous[])values} )
codepoints=( ${chars/(#m)?/$(( [#16] #MATCH ))} )
echo ${(j[,])codepoints}

답변2

작동해야 합니다 iconv | hexdump.

샘플 입력에 대한 깔끔한 개념 증명:

cut -d= -f2- | iconv -t UTF-32LE | hexdump -ve '"0x%02X,"'

노트: 위 명령은 다음 환경에서 실행될 경우 예상대로 작동합니다.리틀 엔디안 방식x86 시리즈와 같은 CPU 아키텍처. 이 경고는 아래에 자세히 설명되어 있습니다.

코드 포인트를 통합하고 가짜 쉼표와 0x0A를 제거하려면 다음을 수행하세요.

cut -d= -f2- | sed 's/./&\
/g' | sort -u | tr -d '\n' | iconv -t UTF-32LE | hexdump -ve '",0x%02X"' | cut -d, -f2-

노트: 후자의 예에서 명령은 보다 이식 가능한 예를 제공하고 싶었기 때문에 sed명령의 대체 부분에 개행 문자를 포함합니다 . s///사용 중인 셸이 $'...'리터럴 구문을 지원하는 경우 sed전체 명령을 sed $'s/./&\\\n/g'로 바꿔 동일한 줄에 개행 문자를 쉽게 포함할 수 있습니다. 리터럴 구문은 일반적으로 예제에서 사용하는 내장 함수에 대한 캐스트 매개변수를 지원하는 버전에서 사용할 수 있습니다 $'...'.bash'<char>%Xprintf

이 솔루션에 대한 몇 가지 참고 사항:

  • hexdumpCPU 엔디안: CPU 자체 엔디안에서는 고정 크기 정수만 처리할 수 있는 를 입력하므로 iconv이러한 요구 사항에 맞는 문자 스트림으로 변환해야 합니다. UTF-32LE전체 유니코드 공간의 각 문자에 대한 고정 4바이트 인코딩이며 리틀 엔디안 형식으로 표현됩니다. 빅엔디안 CPU에서 명령을 실행해야 한다면 그렇게 할 수 있습니다 iconv -t UTF-32BE.
  • 위의 예에서는 입력이 다음을 사용하고 있다고 가정합니다.동일한 문자 세트 인코딩사용되었습니다저것구현하다현재 로케일, 귀하의 예에서와 같이. 관련된 인코딩을 일치시키지 않으려면 -f옵션을 사용하여 입력 데이터의 인코딩을 명시적으로 지정하는 것이 좋지만 iconv가장 안전한 방법은 전환하는 것입니다.모두입력 데이터와 동일한 인코딩을 사용하는 로캘로 파이프합니다. cut위 명령은 =문자를 감지해야 하므로 sed각 문자를 올바르게 감지해야 합니다.실재
  • 변환 가용성: 호스트가 iconv실제로 입력 데이터의 인코딩을 UTF32로 변환할 수 있는지 여부는 시스템에 따라 다릅니다. 적어도 iconvGNU(glibc)는 모든 "gconv 모듈"이 호스트 시스템에 설치되어 있을 때 매우 강력합니다. 위 명령의 경우 최소한 입력 데이터의 인코딩 및 UTF32 인코딩이 필요합니다. UTF8은 일반적으로 glibc 자체 기본 libc파일에 내장되어 있는 반면 UTF32 버전은 특정 gconv 모듈의 파일에 있습니다 .so. GNU glibc를 기반으로 하는 전형적인 성숙한 운영 체제에는 일반적으로 세계의 거의 모든 인코딩을 포괄하는 로드 가능한 gconv 모듈의 전체 세트가 함께 제공됩니다.
  • 변환 속도: GNU의 iconv가장 일반적인 "삼각측량", 즉 입력 인코딩에서 iconv자체 내부 표현으로 변환한 다음 후자에서 원하는 출력 인코딩으로 변환합니다. 내가 아는 한 매우매우몇몇 iconv의 gconv 모듈은 중간 단계를 건너뛰고 한 인코딩에서 다른 인코딩으로 직접 변환을 제공합니다. 이 "삼각측량" 동작과 cut | sed | sort전체 파이프라인(등)에서 수행되는 보조 변환이 iconvGNU가 아닌(또는 전혀 변환하지 않는) 변환보다 빠른지 느린지는 알 수 없습니다 .iconv

마지막으로, 위에서 언급한 내용 중 어느 것도 대상 임베디드 시스템에 적용되지 않습니다. 솔루션은 일반적으로 강력하고 모든 기능을 갖춘 시스템에서 실행되어야 합니다.

답변3

사용행복하다(이전 Perl_6)

여기에서: https://docs.raku.org/언어/faq.html#String:_How_can_I_get_the_헥사데시말_representation_of_a_string%3F

~$ raku -ne 'BEGIN my %chars; 
             %chars{$_.encode.gist}++ for .split("=", limit => 2, :skip-empty)[1..*].comb; 
             END put .keys for %chars.sort;'  file

#또는

~$ raku -ne 'BEGIN my %chars; 
             %chars{$_.encode.gist}++ for .comb(/<?after TEXT_ \d+ \= > .+ $/).comb; 
             END put .keys for %chars.sort;'  file

ordRaku(일명 Perl_6)에는 , ords, unique, printf등과 같은 기능이 있지만 sprintf위의 코드는 문서에서 직접 적용되었으므로 권장됩니다.

입력 예(하단에 추가 빈 줄이 있음):

TEXT_1=Foo
TEXT_2=Bar
TEXT_3=Baz
TEXT_4=Ünicødé
TEXT_5=ελληνικά

예제 출력:

utf8:0x<42>
utf8:0x<46>
utf8:0x<61>
utf8:0x<63>
utf8:0x<64>
utf8:0x<69>
utf8:0x<6E>
utf8:0x<6F>
utf8:0x<72>
utf8:0x<7A>
utf8:0x<C3 9C>
utf8:0x<C3 A9>
utf8:0x<C3 B8>
utf8:0x<CE AC>
utf8:0x<CE B5>
utf8:0x<CE B7>
utf8:0x<CE B9>
utf8:0x<CE BA>
utf8:0x<CE BB>
utf8:0x<CE BD>

utf8위에서 본 것과 같이 Raku가 기본적으로 사용됩니다. 그러나 @StephaneChazelas의 Perl(5) 답변과 동일한 결과를 얻기 위해 .encode("utf-16")이러한 결과를 반환 할 수 있습니다..values

~$ raku -e 'my @a = lines>>.subst(:global, / ^^ <(TEXT_ \d+ \= )> /).join.comb.unique;  \
            print join ",", map {sprintf("0x%X", .encode("utf-16").values) }, @a[].sort;'
0x42,0x46,0x61,0x63,0x64,0x69,0x6E,0x6F,0x72,0x7A,0xDC,0xE9,0xF8,0x3AC,0x3B5,0x3B7,0x3B9,0x3BA,0x3BB,0x3BD

원한다면 아래의 두 번째 링크에서 유니코드 정규화(Raku)에 대해 매우 광범위한 논의를 할 수 있습니다. 해당 페이지에서 원시 바이트를 복구해야 하는 경우 인코딩 utf8-c8(예: "utf8-clean8")을 사용할 수 있습니다.

https://docs.raku.org/언어/faq.html#String:_How_can_I_get_the_헥사데시말_representation_of_a_string%3F
https://docs.raku.org/언어/unicode
https://raku.org

답변4

내 원래 질문에 대한 답변을 제공했지만(그 중 하나를 수락하겠습니다) 완전성을 위해 python일부 변경 사항(형식 정의 변경, 부사 문자 모양은 다음과 같이 그룹화되어야 함) 을 추가했습니다.0x30-0x39

#!/bin/python3
# glycol is the GLYph COLlector
# it collects all used glyphs from the translation json files given as command line arguments
# and prints them as a string formatted to be used as -r argument for lv_font_conv
# Usage: lv_font_conv -r $(glycol de.json en.json fr.json) ...

import sys
Glyphs=[]
# Loop over all files
sys.argv.pop(0)
for file in sys.argv:
    # Sorry, low level tool without error handling
    with open(file, 'r', encoding="utf-8") as f:
        for line in f:
            parts = line.split('"')
            if len(parts) == 5:
                # expect format _"key":"string" -- No json parsing
                Glyphs.extend(ord(c) for c in parts[3])
Glyphs.sort()
# Now loop over the sorted glyph list, skip duplicates, join regions
last=0
region=0
for glyph in Glyphs:
    if (last == 0):
        print(hex(glyph), end='')
    elif (glyph == last + 1):
        region = glyph
    elif (glyph > last):
        if (region == last):
            print('-'+ hex(region), end='')
        print(','+ hex(glyph), end='')
    last = glyph
if (region == last):
    print('-'+ hex(region), end='')
print()

관련 정보