파일을 최대 문자 수 (바이트 아님)로 자르는 방법 주어진 문자

(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

GNU sed를 사용하면 할 수 있습니다 (파일에 유효한 문자를 형성하지 않는 NUL 문자 또는 바이트 시퀀스가 ​​포함되어 있지 않다고 가정하면 둘 다 텍스트 파일에 해당해야 함).

sed -Ez -i -- 's/^(.{1234}).*/\1/' "$file"

그러나 파일을 전체적으로 읽고 메모리에 전체를 저장하고 새 사본을 작성하므로 효율성이 훨씬 떨어집니다.

GNU awk

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(이외의 껍질 zshNUL을 포함하지 않는 내용을 가정하는 바이트) :

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]}

ksh93bash(주의 여러 버전의 멀티 바이트 문자에 대한 그것의 가짜를bash )

IFS= read -rN1234 s < "$file" &&
  printf %s "$s" > "$file"

ksh93<>;리디렉션 연산자를 사용하여 파일을 다시 쓰지 않고 대신 파일을 잘라낼 수도 있습니다 .

IFS= read -rN1234 0<>; "$file"

iconv + 머리

처음 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=fullblockGNU 확장 을 사용하지 않으면 1 이외의 값을 안정적으로 사용할 수 없습니다ddiconv


답변

텍스트 파일에 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>.

이렇게하면 목표가 달성 될 때까지 마지막 문자가 하나씩 제거됩니다. 특히 큰 파일의 경우 성능이 현명하지 않습니다. 더 많은 가능성을 보여주는 아이디어로 이것을 제시하고 싶었습니다.


답변