쉘 내장 함수

쉘 내장 함수

(UTF-8로 인코딩된) 텍스트 파일을 주어진 문자 수로 자르는 방법은 무엇입니까? 줄 길이는 상관없구요, 단어 중간에 컷이 있어도 됩니다.

  • cut한 줄씩 작업하는 것 같지만 완전한 파일을 원합니다.
  • head -c문자가 아닌 바이트를 사용하십시오.

답변1

일부 시스템에는 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 /usr/share/awk/inplace.awk -v RS='^$' -e '
  {printf "%s", substr($0, 1, 1234)}' -E /dev/null "$file"
  • -e code -E /dev/null "$file"임의의 파일 이름을 전달하는 방법입니다.gawk
  • RS='^$':흡연 모드.

사용하지 마세요-i inplace현재 작업 디렉터리(as or)에서 확장 기능을 먼저 gawk로드 하려고 하면 누군가가 해당 디렉터리에 악성 코드를 심었을 수 있습니다. 시스템과 함께 제공되는 확장 프로그램 의 경로 는 다를 수 있습니다. 출력을 참조하세요.inplaceinplaceinplace.awkinplacegawkgawk 'BEGIN{print ENVIRON["AWKPATH"]}'

쉘 내장 함수

ksh93, bash또는 사용 ( 내용에 NUL 바이트가 포함되어 있지 않다고 가정하고 zshExcept 이외의 셸 사용 ):zsh

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"

아이콘 + 헤더

도착하다인쇄UTF32BE처음 1234자의 경우 / 와 같이 문자당 고정된 바이트 수를 사용하는 인코딩으로 변환하는 것이 또 다른 옵션일 수 있습니다 UCS-4.

iconv -t UCS-4 < "$file" | head -c "$((1234 * 4))" | iconv -f UCS-4

head -c표준은 아니지만 꽤 일반적입니다. 표준에 해당하는 것은 dd bs=1 count="$((1234 * 4))"한 번에 1바이트를 읽고 한 번에 1바이트를 쓰기 때문에 효율성이 떨어집니다. iconv표준 명령이지만 인코딩 이름은 표준화되어 있지 않으므로 시스템이 그렇지 않을 수도 있습니다.UCS-4

노트

어떤 경우든 출력은 최대 1234자까지 가능하지만 구분되지 않은 줄로 끝날 수 있으므로 유효한 텍스트가 아닐 수도 있습니다.

또한 이러한 솔루션은 문자 중간에 있는 텍스트를 자르지 않지만 문자 중간에 있는 텍스트를 깨뜨릴 수 있습니다.문자소, 예를 들어 éU+0065 U+0301(a e다음에 날카로운 악센트 결합) 또는 분해된 형태의 한국어 음절 문자소로 표현됩니다.


bs1 및 파이프 입력에서는 GNU 확장을 사용하지 않는 한 1 이외의 값을 안정적으로 사용할 수 없습니다. 파이프를 채울 수 있는 것보다 파이프를 더 빨리 읽을 수 있으면 짧은 읽기가 가능하기 iflag=fullblock때문입니다.ddiconv

답변2

텍스트 파일에 UTF-8로 인코딩된 유니코드가 포함되어 있다는 것을 알고 있는 경우 먼저 UTF-8을 디코딩하여 유니코드 문자 엔터티 시퀀스를 얻고 이를 분할해야 합니다.

나는 이 작업을 위해 Python 3.x를 선택하겠습니다.

Python 3.x 함수 사용열려 있는()encoding=읽기를 위한 추가 키워드 인수가 있습니다.텍스트 파일. 방법 설명io.TextIOBase.read()유망해 보입니다.

따라서 Python 3을 사용하면 다음과 같습니다.

truncated = open('/path/to/file.txt', 'rt', encoding='utf-8').read(1000)

분명히 실제 도구에는 명령줄 매개변수, 오류 처리 등이 추가됩니다.

Python 2.x를 사용하면 자신만의 파일류 객체를 구현하고 입력 파일을 한 줄씩 디코딩할 수 있습니다.

답변3

사용행복하다(이전의 Perl6)

Raku는 유니코드에 대한 고급 내장 지원을 제공합니다. 파일 핸들 이름을 제외하고 문자소는 유니코드 컨소시엄의 이름으로 변환됩니다.표준화된 형태 C"(NFC)가 기본으로 설정되어 있습니다. 아래는 이모티콘 예시입니다.

입력 예:

~$ raku -e 'for (0..8) -> $i { $_.[0..$i].join.put given "\x1F600".."\x1F64F"};' > emoticons_0-to-8.txt
~$ cat emoticons_0-to-8.txt

답변4

다른 방법을 추가하고 싶습니다. 아마도 최고의 성능을 발휘하지는 못하고 길지만 이해하기 쉽습니다.

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

이는 목표에 도달할 때까지 마지막 문자를 하나씩 제거하는데, 이는 특히 대용량 파일의 경우 성능 측면에서 매우 나쁜 것 같습니다. 저는 단지 이것을 더 많은 가능성을 보여주기 위한 아이디어로 제시하고 싶었을 뿐입니다.

관련 정보