Bash: 100,000자 이상의 문자를 10진수 형식으로 변환하시겠습니까?

Bash: 100,000자 이상의 문자를 10진수 형식으로 변환하시겠습니까?

저는 100,000줄 이상의 텍스트를 10진수 형식으로 변환할 수 있는 빠르고 CPU 집약적인 솔루션을 찾고 있습니다.

# random ascii
string='QPWOEIRUTYALSKDJFHGZMXNCBV,./;[]75498053$#!@*&^%(*'

convert () {
    for ((b=0; b<${#string}; b++ ))
    do
        # convert to dec, append colon character, add to array
        arr+=$(printf '%d,' "'${string:$b:1}");
    done;
    # show array contents
    printf '%s' "${arr[@]::-1}"
}

time convert

위의 방법은 단기 작업에 적합하며 작업은 1초 이내에 완료됩니다.

$ ./stackexchange.sh

81,80,87,79,69,73,82,85,84,89,65,76,83,75,68,74,70,72,71,90,77,88,78,67,66,86,44,46,47,59,91,93,55,53,52,57,56,48,53,51,36,35,33,64,42,38,94,37,40,42
real    0m0.059s
user    0m0.032s
sys     0m0.016s

그러나 이는 많은 문자가 포함된 파일에는 적합한 솔루션이 아닙니다. 아래 기능으로 인해 CPU가 급증하고 기본적으로 작업을 완료하지 못합니다. 글쎄, 몇 분 후에 Ctrl+c를 눌러 중지했습니다. 다음은 변수가 수정된 동일한 스크립트입니다 string.

# random ascii
string="$(cat /tmp/100000-characters.txt)"

convert () {
    for ((b=0; b<${#string}; b++ ))
    do
        arr+=$(printf '%d,' "'${string:$b:1}");
    done;
    printf '%s' "${arr[@]::-1}"
}

time convert

나는 또한 while 루프를 시도했다. 100,000자 파일 변환에 성공했지만, 여전히 완료하는 데 오랜 시간이 걸렸습니다.

string="$(cat /tmp/100000-characters.txt)"

convert () {
    # iteracte through each like 
    while read -r -n1 char; do
        arr+=$(printf '%d,' "'$char");
    done <<< "$string"
    
    printf '%s' "${arr[@]::-3}"
}
time convert 

큰 텍스트 파일을 콜론으로 구분된 십진수 값으로 변환하는 우아하고 간단한 솔루션이 있습니까?

답변1

Perl이 구출하러 옵니다!

perl -nE 'say join ",", map ord, split //' < file
  • -n입력 내용을 한 줄씩 읽고 각 줄에 대한 코드를 실행합니다.
  • 나뉘다빈 정규식에서 //입력을 단일 문자로 분할
  • 지도각 문자를 해당 문자에 매핑주문하다
  • 가입하다문자에서 문자열을 만들고 그 사이에 쉼표를 삽입합니다.
  • 설명하다출력 결과

입력을 한 줄씩 처리하지 않으려면 추가 조정이 필요할 수 있습니다.

답변2

대부분의 시간은 배열을 만드는 데 소요되며 마지막에 콜론을 제거하기 위해 이 작업을 수행하는 것 같습니다. 대신, 플래그만 사용하고 배열 전체를 구축하는 것을 피하세요. 그러면 훨씬 더 빨라질 것입니다.

#!/bin/bash

string='QPWOEIRUTYALSKDJFHGZMXNCBV,./;[]75498053$#!@*&^%(*'

convert() {
    local first=1
    for ((b=0; b<${#string}; b++ )); do
        (( first )) && first=0 || printf ,
        printf '%d' "'${string:$b:1}"
    done
}

time convert

비교시간입니다. 첫째, 초기 솔루션에는 1000자가 포함됩니다.

real  0m0.454s
user  0m0.439s
sys   0m0.057s

이 솔루션의 문자 수는 1,000자입니다.

real  0m0.148s
user  0m0.147s
sys   0m0.001s

이는 내장 명령을 사용하여 bash를 입력하는 것만큼 빠릅니다. 가능하다면 위의 Perl과 같이 이를 처리하기 위한 더 나은 도구를 구입하는 것이 좋습니다.

답변3

hexdump이것이 /의 목적입니다 od:

<input hexdump -ve '/1 ",%u"' | tail -c+2

예를 들어.

메소드처럼 각 문자의 코드 포인트 1 값이 아니라 각 바이트의 값을 인쇄합니다. ASCII 문자만 포함된 예에서는 아무런 효과가 없습니다.

유니코드 코드 포인트를 얻으려면 먼저 입력을 UCS4로 변환하면 됩니다. 비교하다:

$ printf %s 'Stéphane' | hexdump -ve '/1 ",%u"' | tail -c+2
83,116,195,169,112,104,97,110,101
$ printf %s 'Stéphane' | iconv -t ucs-4le | hexdump -ve '/4 ",%u"' | tail -c+2
83,116,233,112,104,97,110,101

리틀 엔디안 프로세서(x86)가 있는 UTF-8 로케일에서 첫 번째 방법이 é(U+00E9) 문자의 UTF-8 인코딩의 2바이트(195 및 169)를 덤프하는 방법을 확인하고 두 번째 방법은 233(0xe9)을 인쇄합니다.


1 bash에 대한 내장 함수 printf는 일반적으로 단일 바이트 문자 매핑이 있는 로케일의 바이트 값을 인쇄하므로 해당 문자 세트에 코드 포인트를 제공하고 대부분의 시스템에서 유니코드 코드가 되는 멀티바이트 문자 매핑의 경우 포인트의 넓은 문자 값

답변4

문자열이 이름이 지정된 파일에 저장되어 있다고 가정하면 file다음을 사용할 수 있습니다.파이썬각 문자의 서수를 평가합니다.

$ python3 -c 'import sys
with open(sys.argv[1]) as fh: s=fh.read()
print(*[ord(c) for c in s.rstrip("\n")], sep=",")
' ./file
  • 파일은 문자열 변수 s에 저장됩니다.

  • 목록 이해 구성을 사용하여 문자열을 문자별로 반복하고 내장 함수를 사용하여 각 문자열의 서수를 평가한 ord()다음 목록 구분 기호로 쉼표를 사용하여 인쇄되는 익명 목록에 이를 누적합니다.

또는 Linux 유틸리티의 gnu 버전을 사용할 수 있습니다 xargs+grep. 이렇게 하면 printf 호출 횟수가 최소화됩니다.

$ printf '%s\0' "$string"   \
| grep -oz '.'              \
| xargs -r0 printf "'%s\\0" \
| xargs -r0 printf '%d\n'   \
| paste -sd,

관련 정보