将行换行到指定数量的字段

将行换行到指定数量的字段

我有一个文本文件,其中包含多个向量,如下所示。这些向量的组成部分用空格分隔,并且分布在几行中。该文件是我在 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.12V2y=-0.919V2z=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

printfbash 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 再次利用格式重用功能依次打印每个以换行符结尾的数组元素。

相关内容