ファイルを最大文字数(バイトではない)に切り捨てる方法


13

(UTF-8でエンコードされた)テキストファイルを特定の文字数に切り捨てるにはどうすればよいですか?行の長さは気にせず、単語の途中でカットすることもできます。

  • cut 行で動作するようですが、ファイル全体が必要です。
  • head -c 文字ではなくバイトを使用します。

GNUの実装はcutまだマルチバイト文字をサポートしていないことに注意してください。もしそうなら、あなたはすることができますcut -zc-1234 | tr -d '\0'
ステファンシャゼラス

絵文字をどのように処理しますか?一部は1文字以上です... stackoverflow.com/questions/51502486/…–
phuzi

2
キャラクターとは?いくつかのシンボルはいくつかのコードポイントを使用します
Jasen

回答:


14

一部のシステムには、truncateファイルを文字数ではなくバイト数に切り捨てるコマンドがあります

perlほとんどのシステムにデフォルトでインストールされているものに頼ることができますが、私はいくつかの文字に切り捨てられるものを知りません:

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" 任意のファイル名を渡す1つの方法 gawk
  • RS='^$'丸lurみモード

シェルビルトイン

有するksh93bash又は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]}

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の後に続く鋭いアクセント)で表されるように、書記素の途中でテキストを分割できます。または、分解された形式のハングル音節のグラフェン。ée


¹とパイプに使用することはできません入力bsは、使用しない限り、確実に1以外の値をiflag=fullblockGNU拡張をとして、ddそれはより速くパイプを読み取ると、短い行うことができます読み取りiconv塗りつぶし、それを


できることdd bs=1234 count=4
Jasen

2
@Jasen、それは信頼できません。編集を参照してください。
ステファンシャゼラス

うわー!近くにいると便利です!便利なUnixコマンドをたくさん知っていると思いましたが、これは素晴らしいオプションの信じられないほどのリストです。
マークスチュワート

5

テキストファイルにUTF-8としてエンコードされたUnicodeが含まれていることがわかっている場合、最初にUTF-8をデコードしてUnicode文字エンティティのシーケンスを取得し、それらを分割する必要があります。

仕事には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コマンドをさらに使用したいと思います。
ピテル

5
Linuxのフレーバーで「標準Linux」が意味するものは何でも…
MichaelStröder18年

1
確かに、Python、とにかくそのバージョンは、最近ではかなり標準的です。
-muru

テキストファイルを明示的に処理できるPython 3のスニペットで回答を既に編集しました。
マイケルストレーダー

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>ます。

これにより、目標が達成されるまで最後の文字が1つずつ削除されます。これは、特に大きなファイルの場合、実際にはパフォーマンスが悪いようです。これを、より多くの可能性を示すアイデアとして提示したかっただけです。


ええ、これはパフォーマンスにとって間違いなく恐ろしいことです。長さnのファイルの場合、ファイルwcへの途中のターゲットポイントの合計バイト数O(n ^ 2)のオーダーでカウントします。増加または減少する変数などを使用して、線形検索の代わりにバイナリ検索を行うことができるはず echo -n "${result::-$chop}" | wc -mです。(そして、ファイルの内容がで始まっていて-eも、おそらくを使用している場合でも、安全を確保してくださいprintf)。ただし、各入力文字を一度だけ見るメソッドに勝るものはないので、おそらくそれだけの価値はありません。
ピーター

あなたは間違いなく正しい、実際的な答えよりもむしろ技術的な答えです。また、逆にして$result、目的の長さに一致するまでcharごとにcharを追加することもできますが、必要な長さが大きい数値の場合は、同様に非効率的です。
紙吹雪

1
$desired_charsローエンドまたはおそらくハイエンドのバイトから開始することにより、適切な場所の近くから開始できます4*$desired_chars。しかし、それでもまったく別のものを使用するのが最善だと思います。
ピーター
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.