我有一个文本文件,其中包含多个向量,如下所示。这些向量的组成部分用空格分隔,并且分布在几行中。该文件是我在 Ubuntu 终端中运行命令后生成的。
0 -1 -0.494 0.12 -0.919 0.112 0.914 -0.681 -0.067 -0.918 -0.443 -0.216 -0.48 0.55 0.701 0.429 0.699 -0.726 -0.39 0.172 0.61 -0.599 0.728 -0.883 -0.32 0.044 -0.189 -0.732 -0.309 -0.286 -0.859 0.107 0.298
0 0 0.869 0.641 -0.331 -0.631 -0.236 0.303 0.998 0.153 -0.89 -0.927 -0.671 -0.478 0.693 -0.007 -0.64 0.091 -0.249 -0.881 0.641 0.689 0.222 -0.398 0.548 -0.268 -0.877 -0.333 -0.55 0.858 0.504 0.215 -0.178
0 0 0 0.758 -0.214 0.768 0.329 0.667 -0.013 0.367 0.103 -0.307 -0.565 0.685 0.171 -0.903 0.32 -0.682 -0.887 -0.44 -0.467 0.409 -0.649 0.249 0.772 -0.962 0.443 -0.594 0.776 -0.427 0.088 -0.971 0.938
如何使用具有以下格式的 shell 命令将此文件转换为另一个文件,其中每个向量位于单独的行中,并且文件头是三分量向量的数量?
n
V1x V1y V1z
V2x V2y V2z
V3x V3y V3z
...
Vnx Vny Vnz
其中 n 是文件中三分量向量的数量。在我的文件中:V1x=0
, V1y=-1
, V1z=-0.494
. V2x=0.12
、V2y=-0.919
、V2z=0.112
等等。
答案1
Perl 一行代码:
perl -p00E 'y/\n/ /;say s/(\S+\s+){3}\K/\n/g' file
请注意,该解决方案和格努克的假设文件足够小,可以作为一个整体存储在内存中。
解释
-p
表示将文件的每条记录别名为 ,并在处理每条记录后$_
打印内容。$_
-00
表示将记录分隔符设置为空,以便将整个文件作为一条记录读取。-E
表示将以下字符串视为 Perl 代码。使用-E
而不是通常的方式-e
意味着我可以使用该say
功能。y/\n/ /
使整个文件成为一行(请注意,这y///
是 Perl 中的同义词,tr///
以方便sed
用户)。s/(\S+\s+){3}\K/\n/g
表示在每个模式后添加一个新行(非空格后跟空格重复 3 次 == 一个向量)。- 由于
s///
将返回已成功进行的替换数,因此将其用作参数say
将打印出替换数(=向量)。 - 打印计数后,
$_
会打印 的内容,因为我们使用了-p
。
更新
如果你想要最大值:
perl -p00E 'y/\n/ /;s/(\S+\s+){1}\K/\n/g' file | sort -nr | head -1
该解决方案的优点
它只有一个“神奇数字”。换句话说,如果您突然开始使用二维向量,您所需要做的就是{3}
将代码中的 更改为{2}
。
该解决方案的缺点
如果您不熟悉 Perl,它读起来就像一个黑魔法咒语。
答案2
就像是
ruby -e 'ns = STDIN.read.split(/\s+/); puts(ns.size/3); 0.step(ns.size,3) do |i| puts(ns[i,3].join(" ")) end' < yourfile
如果您允许从 shell 调用外部程序,则应该可以工作。
编辑:也许我们应该在高尔夫球场上做这个:-)
答案3
所以你想做两件事:
- 重新包装数据,使其每行恰好有 3 个坐标;
- 在一行中添加向量数量的前缀。
将其作为两个连续的独立问题来处理会更简单。首先,重新包装数据。为此,您可以使用 awk,告诉它任何空格序列都是输入记录分隔符。
awk -v RS='[[:space:]]+' '{if (NR % 3) printf "%s ", $0; else print}' <input.txt >wrapped.txt
您可以通过将输出分隔符设置为行号为 3 的倍数的换行符(否则为空格)来缩短此长度。
awk -v RS='[[:space:]]+' '{ORS = NR % 3 ? " " : "\n"; print}' <input.txt >wrapped.txt
由于打印是默认操作,因此可以缩短为
awk -v RS='[[:space:]]+' 'ORS = NR % 3 ? " " : "\n"' <input.txt >wrapped.txt
向量的数量是中间文件中的行数。
wc -l wrapped.txt >output.txt
cat wrapped.txt >>output.txt
答案4
printf
bash shell 的内置有一个有趣的功能
The format is reused as necessary to consume all of the argu‐
ments.
这似乎允许我们读取一个由空格分隔的值的文件,并使用简单的 printf 将它们吐出为一行三
printf '%8.3f %8.3f %8.3f\n' $(<file)
(我使用8.3
浮点格式只是为了美化输出,但您可以使用%s
将每个字段作为原始字符串进行回显)。
要计算结果向量,你可以使用wc
- 如果你不介意计数来后数据然后你就可以输出
printf '%8.3f %8.3f %8.3f\n' $(<file) | tee >(wc -l)
如果您确实坚持将计数放在顶部,那么一种可能性可能是打印到变量,然后计数并打印变量(这将受到与其他就地方法相同的内存考虑因素的影响)
printf -v vecs '%8.3f %8.3f %8.3f\n' $(<file)
wc -l < <(printf "$vecs") ; printf "$vecs"
如果您是真正的 shell 纯粹主义者,那么您可以使用mapfile
(或其同义词readarray
)将重新格式化的数据放入数组而不是字符串变量中 - 然后使用 shell 的${#array[@]}
计数运算符来避免外部调用wc
mapfile vecs < <(printf '%8.3f %8.3f %8.3f\n' $(<file))
printf '%d\n' ${#vecs[@]} ; printf '%s' "${vecs[@]}"
最后的 printf 再次利用格式重用功能依次打印每个以换行符结尾的数组元素。