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


13

(UTF-8로 인코딩 된) 텍스트 파일을 주어진 문자 수로 자르려면 어떻게해야합니까? 나는 줄 길이에 신경 쓰지 않으며 컷은 단어 중간에있을 수 있습니다.

  • cut 라인에서 작동하는 것처럼 보이지만 전체 파일을 원합니다.
  • head -c 문자가 아닌 바이트를 사용합니다.

GNU 구현은 cut여전히 멀티 바이트 문자를 지원하지 않습니다. 그렇다면 할 수 있습니다 cut -zc-1234 | tr -d '\0'.
Stéphane Chazelas

이모지를 어떻게 처리하고 싶습니까? 일부는 하나 이상의 문자입니다 ... stackoverflow.com/questions/51502486/…
phuzi

2
캐릭터는 무엇입니까? 일부 기호는 여러 코드 포인트를 사용합니다.
Jasen

답변:


14

일부 시스템에는 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


가능dd bs=1234 count=4
Jasen

2
@Jasen, 그것은 신뢰할 수 없습니다. 편집을 참조하십시오.
Stéphane Chazelas

와! 근처에 있으면 편리합니다! 편리한 유닉스 명령어가 많이 있다는 것을 알았지 만 훌륭한 옵션의 놀라운 목록입니다.
마크 스튜어트

5

텍스트 파일에 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를 사용하면 파일과 유사한 객체를 구현하고 입력 파일을 한 줄씩 디코딩 할 수 있습니다.


그래, 할 수있어 그러나 그것은 CI 빌드 머신을위한 것이므로 표준 Linux 명령을 사용하고 싶습니다.
Pitel

5
리눅스 맛에 무엇이든 "표준 리눅스"수단 ...
마이클 Ströder

1
실제로, 파이썬의 일부 버전은 요즘 꽤 표준입니다.
muru

텍스트 파일을 명시 적으로 처리 할 수있는 Python 3 용 스 니펫으로 답변을 이미 편집했습니다.
마이클 Ströder

0

다른 접근법을 추가하고 싶습니다. 아마도 최고의 성능은 아니지만 훨씬 더 길지만 이해하기 쉽습니다.

#!/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>.

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


예, 이것은 성능이 끔찍합니다. 길이가 n 인 파일의 경우 파일의 wc중간 지점에서 대상 지점의 O (n ^ 2) 총 바이트 순서로 계산됩니다. 증가 또는 감소하는 변수 등을 사용하여 선형 검색 대신 이진 검색을 수행 할 수 있어야합니다 echo -n "${result::-$chop}" | wc -m. (그리고 당신이 그것을하는 동안 파일 내용으로 시작 -e하거나 무언가를 사용 하더라도 아마 안전하게하십시오 printf). 그러나 여전히 각 입력 문자를 한 번만 보는 방법을 이길 수 없으므로 가치가 없습니다.
Peter Cordes

실질적인 대답보다는 기술적 인 답변에 더 가깝습니다. $result원하는 길이와 일치 할 때까지 char별로 char를 추가하기 위해 그것을 뒤집을 수도 있지만 원하는 길이가 높은 경우 비효율적입니다.
색종이 조각

1
$desired_chars로우 엔드 또는 4*$desired_chars하이 엔드에서 바이트로 시작하여 올바른 위치에 가까이 시작할 수 있습니다. 그러나 여전히 다른 것을 완전히 사용하는 것이 가장 좋습니다.
Peter Cordes
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.