如何将(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
:我们将记录分隔符设置为对整数的引用,这是指定固定长度记录(以数量为单位)的方法人物)。然后在读取第一条记录后,我们就地截断标准输入(因此在第一条记录的末尾)并退出。
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尝试首先从当前工作目录gawk
加载inplace
扩展(asinplace
或),有人可能已经在其中植入了恶意软件。随系统提供的扩展inplace.awk
的路径可能会有所不同,请参阅输出inplace
gawk
gawk 'BEGIN{print ENVIRON["AWKPATH"]}'
Shell 内置函数
使用ksh93
, bash
or zsh
(使用除 之外的 shell 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]}
与ksh93
或bash
(注意在几个版本的多字节字符中它是假的bash
):
IFS= read -rN1234 s < "$file" &&
printf %s "$s" > "$file"
ksh93
还可以就地截断文件,而不是使用<>;
重定向运算符重写它:
IFS= read -rN1234 0<>; "$file"
图标 + 头
到打印对于前 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))"
但效率较低,因为它一次读取一个字节并写入一个字节。iconv
是一个标准命令,但编码名称没有标准化,因此您可能会发现系统没有UCS-4
笔记
在任何情况下,虽然输出最多有 1234 个字符,但它最终可能不是有效文本,因为它可能以非分隔行结束。
另请注意,虽然这些解决方案不会在字符中间剪切文本,但它们可能会在字符中间中断文本。字素,如é
表示为 U+0065 U+0301(ae
后跟组合锐音重音),或分解形式的韩文音节字素。
bs
1 并且在管道输入上,除非使用GNU 扩展,否则不能可靠地使用1 以外的值iflag=fullblock
,因为dd
如果读取管道的速度比iconv
填充管道的速度快,则可以进行短读取
答案2
如果您知道文本文件包含编码为 UTF-8 的 Unicode,则必须首先解码 UTF-8 以获取 Unicode 字符实体序列并拆分它们。
我会选择 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 提供对 Unicode 的高级内置支持。除了文件句柄名称之外,字素都转换为 Unicode 联盟的“标准化形式 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>
。
这会一一删除最后一个字符,直到达到目标,这似乎在性能方面非常糟糕,尤其是对于较大的文件。我只是想把这个作为一个想法来展示,以展示更多的可能性。