如何将(UTF-8 编码)文本文件截断为给定数量的字符?我不在乎行的长度,而且切口可以在字的中间。
cut 似乎在线操作,但我想要一个完整的文件。head -c 使用字节,而不是字符。Sté*_*las 14
某些系统具有truncate将文件截断为多个字节(而非字符)的命令。
我不知道任何截断为多个字符的字符,尽管您可以求助于perl大多数系统上默认安装的字符:
perl -Mopen=locale -ne '
BEGIN{$/ = \1234} truncate STDIN, tell STDIN; last' <> "$file"
Run Code Online (Sandbox Code Playgroud)
使用-Mopen=locale,我们使用语言环境的字符是什么概念(因此在使用 UTF-8 字符集的语言环境中,这是 UTF-8 编码的字符)。-CS如果您希望 I/O 以 UTF-8 进行解码/编码,而不管区域设置的字符集如何,请替换为。
$/ = \1234:我们将记录分隔符设置为对整数的引用,这是一种指定固定长度(字符数)记录的方法。
然后在读取第一条记录时,我们就地截断 stdin(所以在第一条记录的末尾)并退出。
使用 GNU sed,您可以这样做(假设文件不包含 NUL 字符或不构成有效字符的字节序列——这两者都应该适用于文本文件):
sed -Ez -i -- 's/^(.{1234}).*/\1/' "$file"
Run Code Online (Sandbox Code Playgroud)
但这效率要低得多,因为它会完整读取文件并将其整个存储在内存中,然后写入一个新副本。
与 GNU 相同awk:
awk -i inplace -v RS='^$' -e '{printf "%s", substr($0, 1, 1234)}' -E /dev/null "$file"
Run Code Online (Sandbox Code Playgroud)
-e code -E /dev/null "$file" 是将任意文件名传递给的一种方式 gawkRS='^$':啜饮模式。使用ksh93,bash或zsh(使用除 之外的外壳zsh,假设内容不包含 NUL 字节):
content=$(cat < "$file" && echo .) &&
content=${content%.} &&
printf %s "${content:0:1234}" > "$file"
Run Code Online (Sandbox Code Playgroud)
与zsh:
read -k1234 -u0 s < $file &&
printf %s $s > $file
Run Code Online (Sandbox Code Playgroud)
或者:
zmodload zsh/mapfile
mapfile[$file]=${mapfile[$file][1,1234]}
Run Code Online (Sandbox Code Playgroud)
使用ksh93or bash(当心它在多个版本的多字节字符中是伪造的bash):
IFS= read -rN1234 s < "$file" &&
printf %s "$s" > "$file"
Run Code Online (Sandbox Code Playgroud)
ksh93也可以原地截断文件,而不是用它的<>;重定向操作符重写它:
IFS= read -rN1234 0<>; "$file"
Run Code Online (Sandbox Code Playgroud)
要打印前 1234 个字符,另一种选择是转换为每个字符具有固定字节数的编码,例如UTF32BE/ UCS-4:
iconv -t UCS-4 < "$file" | head -c "$((1234 * 4))" | iconv -f UCS-4
Run Code Online (Sandbox Code Playgroud)
head -c不是标准的,但相当普遍。标准等价物将是dd bs=1 count="$((1234 * 4))"但效率较低,因为它会读取输入并一次写入一个字节¹。iconv是标准命令,但编码名称未标准化,因此您可能会发现系统没有UCS-4
在任何情况下,虽然输出最多有 1234 个字符,但它最终可能不是有效文本,因为它可能以非分隔行结尾。
同时还要注意,而这些解决方案将不能在字符的中间切开文字,他们就能把它在中间字形,像一个é表示为U + 0065 U + 0301(一个e接着一个组合重音符)或分解形式的韩文音节字素。
¹ 并且在管道输入上,bs除非您使用iflag=fullblockGNU 扩展,否则您不能可靠地使用 1 以外的值,因为dd如果读取管道比iconv填充管道快,则可以进行短读取
如果您知道文本文件包含编码为 UTF-8 的 Unicode,您必须首先解码 UTF-8 以获得一系列 Unicode 字符实体并拆分它们。
我会选择 Python 3.x 来完成这项工作。
在 Python 3.x 中,函数open()有一个额外的关键字参数encoding=用于读取文本文件。io.TextIOBase.read()方法的描述看起来很有希望。
所以使用 Python 3 它看起来像这样:
truncated = open('/path/to/file.txt', 'rt', encoding='utf-8').read(1000)
Run Code Online (Sandbox Code Playgroud)
显然,真正的工具会添加命令行参数、错误处理等。
使用 Python 2.x,您可以实现自己的类文件对象并逐行解码输入文件。