(UTF-8로 인코딩 된) 텍스트 파일을 주어진 문자 수로 자르려면 어떻게해야합니까? 나는 줄 길이에 신경 쓰지 않으며 컷은 단어 중간에있을 수 있습니다.
cut
라인에서 작동하는 것처럼 보이지만 전체 파일을 원합니다.head -c
문자가 아닌 바이트를 사용합니다.
(UTF-8로 인코딩 된) 텍스트 파일을 주어진 문자 수로 자르려면 어떻게해야합니까? 나는 줄 길이에 신경 쓰지 않으며 컷은 단어 중간에있을 수 있습니다.
cut
라인에서 작동하는 것처럼 보이지만 전체 파일을 원합니다.head -c
문자가 아닌 바이트를 사용합니다.답변:
일부 시스템에는 truncate
파일을 여러 바이트 (문자가 아닌)로 자르는 명령이 있습니다 .
perl
대부분의 시스템에 기본적으로 설치되는 것을 사용할 수는 있지만 여러 문자로 잘리는 것을 알 수 없습니다 .
perl -Mopen=locale -ne '
BEGIN{$/ = \1234} truncate STDIN, tell STDIN; last' <> "$file"
로 -Mopen=locale
로케일의 문자 개념을 사용합니다 (UTF-8 문자 세트를 사용하는 로케일에서 UTF-8로 인코딩 된 문자). -CS
로케일의 문자 세트에 관계없이 I / O를 UTF-8로 디코딩 / 인코딩하려면 대체하십시오 .
$/ = \1234
우리는 (다수의 고정 길이의 기록을 지정하는 방법으로 정수의 참조에 기록 구분자 설정 문자 ).
그런 다음 첫 번째 레코드를 읽으면 stdin이 제자리에서 잘리고 (첫 번째 레코드의 끝에서) 종료됩니다.
GNU sed
를 사용하면 할 수 있습니다 (파일에 유효한 문자를 형성하지 않는 NUL 문자 또는 바이트 시퀀스가 포함되어 있지 않다고 가정하면 둘 다 텍스트 파일에 해당해야 함).
sed -Ez -i -- 's/^(.{1234}).*/\1/' "$file"
그러나 파일을 전체적으로 읽고 메모리에 전체를 저장하고 새 사본을 작성하므로 효율성이 훨씬 떨어집니다.
GNU와 동일 awk
:
awk -i inplace -v RS='^$' -e '{printf "%s", substr($0, 1, 1234)}' -E /dev/null "$file"
-e code -E /dev/null "$file"
임의의 파일 이름을 전달하는 한 가지 방법 gawk
RS='^$'
: slurp 모드 .로 ksh93
, bash
또는 zsh
(이외의 껍질 zsh
NUL을 포함하지 않는 내용을 가정하는 바이트) :
content=$(cat < "$file" && echo .) &&
content=${content%.} &&
printf %s "${content:0:1234}" > "$file"
로 zsh
:
read -k1234 -u0 s < $file &&
printf %s $s > $file
또는:
zmodload zsh/mapfile
mapfile[$file]=${mapfile[$file][1,1234]}
와 ksh93
나 bash
(주의 여러 버전의 멀티 바이트 문자에 대한 그것의 가짜를bash
)
IFS= read -rN1234 s < "$file" &&
printf %s "$s" > "$file"
ksh93
<>;
리디렉션 연산자를 사용하여 파일을 다시 쓰지 않고 대신 파일을 잘라낼 수도 있습니다 .
IFS= read -rN1234 0<>; "$file"
처음 1234자를 인쇄 하기 위해 다른 옵션은 UTF32BE
/ 와 같이 문자 당 고정 바이트 수의 인코딩으로 변환하는 것입니다 UCS-4
.
iconv -t UCS-4 < "$file" | head -c "$((1234 * 4))" | iconv -f UCS-4
head -c
표준은 아니지만 상당히 일반적입니다. 표준 동등 물은 dd bs=1 count="$((1234 * 4))"
입력을 읽고 한 번에 1 바이트 씩 출력을 작성하므로 효율이 떨어집니다. iconv
표준 명령이지만 인코딩 이름이 표준화되지 않았으므로UCS-4
어쨌든 출력에 최대 1234자를 사용할 수 있지만, 구분되지 않은 행으로 끝나기 때문에 유효한 텍스트가 아닐 수 있습니다.
또한 이러한 솔루션은 문자 중간에 텍스트를 자르지 않지만 U + 0065 U + 0301 (a 와 결합 된 급성 악센트) 과 같이 grapheme 중간에 텍스트를 깰 수 있습니다 . 또는 분해 된 형태의 한글 음절 그래프.é
e
¹ 및 파이프 입력에서 파이프를 채우는 것 보다 빨리 파이프를 읽는 경우 짧은 읽기를 수행 할 수 bs
있으므로 iflag=fullblock
GNU 확장 을 사용하지 않으면 1 이외의 값을 안정적으로 사용할 수 없습니다dd
iconv
dd bs=1234 count=4
텍스트 파일에 UTF-8로 인코딩 된 유니 코드가 포함되어 있음을 알고 있으면 먼저 UTF-8을 디코딩하여 일련의 유니 코드 문자 엔티티를 가져 와서 분할해야합니다.
작업에 Python 3.x를 선택했습니다.
Python 3.x에서 open () 함수 에는 text-filesencoding=
를 읽는 데 필요한 추가 키워드 키워드 가 있습니다 . io.TextIOBase.read () 메소드에 대한 설명 은 유망 해 보입니다.
따라서 Python 3을 사용하면 다음과 같습니다.
truncated = open('/path/to/file.txt', 'rt', encoding='utf-8').read(1000)
분명히 실제 도구는 명령 줄 인수, 오류 처리 등을 추가합니다.
Python 2.x를 사용하면 파일과 유사한 객체를 구현하고 입력 파일을 한 줄씩 디코딩 할 수 있습니다.
다른 접근법을 추가하고 싶습니다. 아마도 최고의 성능은 아니지만 훨씬 더 길지만 이해하기 쉽습니다.
#!/bin/bash
chars="$1"
ifile="$2"
result=$(cat "$ifile")
rcount=$(echo -n "$result" | wc -m)
while [ $rcount -ne $chars ]; do
result=${result::-1}
rcount=$(echo -n "$result" | wc -m)
done
echo "$result"
로 호출하십시오 $ ./scriptname <desired chars> <input file>
.
이렇게하면 목표가 달성 될 때까지 마지막 문자가 하나씩 제거됩니다. 특히 큰 파일의 경우 성능이 현명하지 않습니다. 더 많은 가능성을 보여주는 아이디어로 이것을 제시하고 싶었습니다.
wc
중간 지점에서 대상 지점의 O (n ^ 2) 총 바이트 순서로 계산됩니다. 증가 또는 감소하는 변수 등을 사용하여 선형 검색 대신 이진 검색을 수행 할 수 있어야합니다 echo -n "${result::-$chop}" | wc -m
. (그리고 당신이 그것을하는 동안 파일 내용으로 시작 -e
하거나 무언가를 사용 하더라도 아마 안전하게하십시오 printf
). 그러나 여전히 각 입력 문자를 한 번만 보는 방법을 이길 수 없으므로 가치가 없습니다.
$result
원하는 길이와 일치 할 때까지 char별로 char를 추가하기 위해 그것을 뒤집을 수도 있지만 원하는 길이가 높은 경우 비효율적입니다.
$desired_chars
로우 엔드 또는 4*$desired_chars
하이 엔드에서 바이트로 시작하여 올바른 위치에 가까이 시작할 수 있습니다. 그러나 여전히 다른 것을 완전히 사용하는 것이 가장 좋습니다.
cut
여전히 멀티 바이트 문자를 지원하지 않습니다. 그렇다면 할 수 있습니다cut -zc-1234 | tr -d '\0'
.