저는 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
답변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,