是否可以从第一列包含与其他文件名称匹配的标识符的文件中提取某些行?
Posted
技术标签:
【中文标题】是否可以从第一列包含与其他文件名称匹配的标识符的文件中提取某些行?【英文标题】:Is it possible to extract certain rows from a file where the first column contains an identifier that matches the names of other files? 【发布时间】:2021-12-23 08:31:39 【问题描述】:我昨天在一个更大的问题中提出了这个问题。一位评论者建议将其拆分为自己的问题。
我有 x 个文件。前五个文件的内容如下所示。在第二行,i
是一个计数器,因此i = 0
用于第一个文件。 i
旁边是 time
,当 i
增加 1 时,它总是增加 0.5。
但是,我只保留了每个第 x 个文件。因此,i = 0
用于我拥有的第一个文件,i = 100
用于第二个文件,依此类推。
6 # This file is called "0.xyz" (<--the "6" is the same in all files)
i = 0, time = 0.000, k = 9000000000000
X -7.6415350292 6.0494971539 8.1919697993
Y -6.6418362233 5.9231018862 8.4056822626
Y -8.0518670684 6.3158684817 9.0061271154
X 26.8252967820 20.4661074967 17.8025744066
Y 26.4477411207 20.4071029058 16.9121571912
Y 26.4399648474 21.2950722068 18.1009273227
6 # This file is called "100.xyz"
i = 100, time = 50.000, k = 2500000000000
X -6.2423192714 -1.5704681396 -9.5648670474
Y -5.4925100813 -1.6522059045 -8.9030589772
Y -6.7765278574 -2.3616512405 -9.4776648590
X 4.1248924594 27.8487302083 -17.5400886312
Y 4.1238657681 26.9869907778 -17.9727402579
Y 5.0750649402 28.1292768156 -17.6848507559
6 # This file is called "200.xyz"
i = 200, time = 100.000, k = 3945000000000
X 19.0090162215 -5.9338939011 6.1931167954
Y 18.4748060757 -6.4905073540 5.6656446036
Y 19.2825591449 -6.4479943255 7.0179774953
X 11.0203415273 34.6029396705 2.7220660957
Y 11.1184002007 34.8398120338 1.8089008500
Y 10.3349649622 33.9509485292 2.5605794622
6 # This file is called "300.xyz"
i = 300, time = 150.000, k = 2341000000000
X -7.6415350292 6.0494971539 8.1919697993
Y -6.6418362233 5.9231018862 8.4056822626
Y -8.0518670684 6.3158684817 9.0061271154
X 26.8252967820 20.4661074967 17.8025744066
Y 26.4477411207 20.4071029058 16.9121571912
Y 26.4399648474 21.2950722068 18.1009273227
6 # This file is called "400.xyz"
i = 400, time = 200.000, k = 2500000000000
X -6.2423192714 -1.5704681396 -9.5648670474
Y -5.4925100813 -1.6522059045 -8.9030589772
Y -6.7765278574 -2.3616512405 -9.4776648590
X 4.1248924594 27.8487302083 -17.5400886312
Y 4.1238657681 26.9869907778 -17.9727402579
Y 5.0750649402 28.1292768156 -17.6848507559
我想做的是将这些文件(上)与另一个文件(下)中的数据进行匹配。在下面的文件中,每一行根据i
(步骤)匹配上面的一个文件。然后我想将下面文件中匹配行的前三列打印到一个新文件中。
虽然我已包含文件名,但我更愿意使用i
进行匹配,而不是文件名。
我知道如何通过简单的迭代来做到这一点。意思是,我可以计算下面文件的每 xth 行并将其打印到一个新文件中。但是,我想使用一种更复杂的方法来专门匹配i
,因为这些文件很长,可能会丢失行,所以我最终会在上面的文件和我想要的文件之间出现不匹配。 .
# Step Time Ax Ay Az Bx By Bz Cx Cy Cz Final
0 0.000 14.8032123290 0.0000000000 0.0000000000 0.0000000000 14.8032123290 0.0000000000 0.0000000000 0.0000000000 14.8032123290 3243.9033438318
1 0.500 14.8029498502 0.0000000000 0.0000000000 0.0000000000 14.8029498502 0.0000000000 0.0000000000 0.0000000000 14.8029498502 3243.7307919244
2 1.000 14.8026923814 0.0000000000 0.0000000000 0.0000000000 14.8026923814 0.0000000000 0.0000000000 0.0000000000 14.8026923814 3243.5615395313
3 1.500 14.8024398604 0.0000000000 0.0000000000 0.0000000000 14.8024398604 0.0000000000 0.0000000000 0.0000000000 14.8024398604 3243.3955453870
4 2.000 14.8021922354 0.0000000000 0.0000000000 0.0000000000 14.8021922354 0.0000000000 0.0000000000 0.0000000000 14.8021922354 3243.2327751298
...
100 50.000 14.8032123290 0.0000000000 0.0000000000 0.0000000000 14.8032123290 0.0000000000 0.0000000000 0.0000000000 14.8032123290 3243.9033438318
...
200 100.000 14.8029498502 0.0000000000 0.0000000000 0.0000000000 14.8029498502 0.0000000000 0.0000000000 0.0000000000 14.8029498502 3243.7307919244
...
300 150.000 14.8026923814 0.0000000000 0.0000000000 0.0000000000 14.8026923814 0.0000000000 0.0000000000 0.0000000000 14.8026923814 3243.5615395313
301 150.500 14.8024398604 0.0000000000 0.0000000000 0.0000000000 14.8024398604 0.0000000000 0.0000000000 0.0000000000 14.8024398604 3243.3955453870
...
400 200.000 14.8021922354 0.0000000000 0.0000000000 0.0000000000 14.8021922354 0.0000000000 0.0000000000 0.0000000000 14.8021922354 3243.2327751298
示例我希望通过操作上面的文件来匹配问题顶部的示例文件集的结果:
0 0.000 14.8032123290
100 50.000 14.8032123290
200 100.000 14.8029498502
300 150.000 14.8026923814
400 200.000 14.8021922354
如果有人对如何处理此问题有任何提示,我将不胜感激。
【问题讨论】:
请添加到您的问题(不发表评论):您搜索了什么,找到了什么?您尝试过什么,它是如何失败的? 每个问题都应该独立存在;这意味着样本输入应该与(预期的)输出匹配......这里不是这种情况;我建议更新问题以显示匹配数据(输入和输出之间);或者,由于我们知道前 6 个文件是从一个较大的文件中提取的(请参阅之前问答的链接),因此(相反)提供较大文件的样本和匹配的(预期的)输出可能更有意义;一旦您找到可接受的答案,您就可以将这两个问答整合到一个脚本中(以满足您最初的两部分要求) @markp-fuso,谢谢。我明白你的意思,希望问题现在更清楚了。 在某一时刻您声明 根据 i(步骤)和时间匹配 ...,然后您声明 使用 i 或时间进行匹配我>;您能否更新问题以阐明匹配要求...i *AND* time
或i *OR* time
?
@markp-fuso,是的,如果我能找到引起混乱的地方,我会很高兴。我很偏执,所以更愿意匹配 i AND time,但只匹配 i 应该没问题。
【参考方案1】:
您可以使用awk
脚本执行此操作,如下所示:
awk 'FNR == 1
if ($0 ~ /^i =/)
dataFile = 0;
step[$3+0] = FILENAME;
else dataFile = 1;
dataFile == 1 && step[$1]
print $1, $2, $3;
' *.xyz data.txt
(假设最终文件名为data.txt
;根据需要进行更改)
FNR == 1
匹配每个文件的第一行,并且将从xyz
文件中捕获步骤,或者设置一个标志,指示我们已经到达数据文件。由于请求执行数学运算,$3 + 0
位只会强制 awk
将第三个字段转换为数字(即,删除尾随逗号)。
dataFile == 1 && step[$i]
匹配数据文件中在xyz
文件中看到步长值的行。
注意:您必须在最终数据文件之前指定所有xyz
文件,以便在处理数据文件之前收集所有步骤。
抱歉,当我将上述解决方案放在一起时,我认为 # This file is called
行不是文件的一部分。修改后的脚本在这里:
awk '
FNR == 2 && FILENAME != ARGV[ARGC-1]
step[$3+0] = FILENAME;
FILENAME == ARGV[ARGC-1] && step[$1]
print $1, $2, $3;
' *.xyz data.txt
此版本使用 ARGV & ARGC 来判断是否正在处理“数据”文件。
如果不是数据文件和行号== 2,缓存“step”值 如果数据文件和步骤在列表中,则打印前 3 个字段结果:
0 0.000 14.8032123290
100 50.000 14.8032123290
200 100.000 14.8029498502
300 150.000 14.8026923814
400 200.000 14.8021922354
【讨论】:
非常感谢。在处理大型文件的操作方面,Awk 比 Python(我习惯使用它)快得多,但我没有足够的经验来做我有时需要做的事情。谢谢你教育我! @markp-fuso,您是否删除了 .xyz 文件的第一行(其中包含“6”的文件)? @markp-fuso,抱歉,我应该像您一样说,可以修改脚本以访问正确的行。对于我的特殊情况,删除该行还有另一个好处。谢谢你们俩的帮助。我的文件有数十万行长,使用 Awk 方法来处理它们比我所做的要快得多,也更可靠。 @markp-fuso:是的,我忽略了在文件中包含“6”行。我已经更新了答案,并留下了原始脚本以供参考。【参考方案2】:假设:
匹配基于i
(又名step
)和time
step / time / Ax
数据文件:
$ cat match.dat
# Step Time Ax Ay Az Bx By Bz Cx Cy Cz Final
0 0.000 14.8032123290 0.0000000000 0.0000000000 0.0000000000 14.8032123290 0.0000000000 0.0000000000 0.0000000000 14.8032123290 3243.9033438318
1 0.500 14.8029498502 0.0000000000 0.0000000000 0.0000000000 14.8029498502 0.0000000000 0.0000000000 0.0000000000 14.8029498502 3243.7307919244
2 1.000 14.8026923814 0.0000000000 0.0000000000 0.0000000000 14.8026923814 0.0000000000 0.0000000000 0.0000000000 14.8026923814 3243.5615395313
3 1.500 14.8024398604 0.0000000000 0.0000000000 0.0000000000 14.8024398604 0.0000000000 0.0000000000 0.0000000000 14.8024398604 3243.3955453870
4 2.000 14.8021922354 0.0000000000 0.0000000000 0.0000000000 14.8021922354 0.0000000000 0.0000000000 0.0000000000 14.8021922354 3243.2327751298
100 50.000 14.8032123290 0.0000000000 0.0000000000 0.0000000000 14.8032123290 0.0000000000 0.0000000000 0.0000000000 14.8032123290 3243.9033438318
200 100.000 14.8029498502 0.0000000000 0.0000000000 0.0000000000 14.8029498502 0.0000000000 0.0000000000 0.0000000000 14.8029498502 3243.7307919244
300 150.000 14.8026923814 0.0000000000 0.0000000000 0.0000000000 14.8026923814 0.0000000000 0.0000000000 0.0000000000 14.8026923814 3243.5615395313
301 150.500 14.8024398604 0.0000000000 0.0000000000 0.0000000000 14.8024398604 0.0000000000 0.0000000000 0.0000000000 14.8024398604 3243.3955453870
400 200.000 14.8021922354 0.0000000000 0.0000000000 0.0000000000 14.8021922354 0.0000000000 0.0000000000 0.0000000000 14.8021922354 3243.2327751298
一个awk
想法:
awk '
FNR==NR if (FNR>1) # skip header line in 1st file
Ax[$1 OFS $2]=$3 # use step + OFS + time as index for Ax[] array
next
$1 == "i" gsub(/,/,"") # remove commas from line so we can use normal FS delimiter to pull ...
i=$3 # field #3 (i) and ...
time=$6 # field #6 (time)
if ( (i OFS time) in Ax) # if i + OFS + time is an index in Ax[] array ...
print i,time,Ax[i OFS time] # print our 3 values to stdout
' match.dat [0-9]*.xyz
这会生成:
0 0.000 14.8032123290
100 50.000 14.8032123290
200 100.000 14.8029498502
300 150.000 14.8026923814
400 200.000 14.8021922354
如果 OP 需要用漂亮的列显示输出,一个想法是将结果通过管道传输到 column
,例如:
$ awk '...' match.dat [0-9]*.xyz | column -t
0 0.000 14.8032123290
100 50.000 14.8032123290
200 100.000 14.8029498502
300 150.000 14.8026923814
400 200.000 14.8021922354
注意:此代码匹配字符串的完全匹配; 不是基于数值的匹配;所以150 != 150.00
【讨论】:
关于字符串格式和使用的重要提示。感谢您竭尽全力帮助解决此问题。【参考方案3】:这可能对你有用(GNU sed):
sed -En '2~100s/^((\s*\S+)3).*/\1/p' file
打开扩展正则表达式并关闭隐式打印-En
。
从第 2 行(标题后面的行)开始,使用模数 100 选择所需的行,然后使用替换命令仅保留前三列。
如果必须使用初始文件进行匹配,那么:
sed -En 's/^i =\s*(\S+),.*/s#^\(\\s\*\1\(\\s\*\\S+\)\2\\)\.\*#\\1#p/p' file1of5 |
sed -Enf - fileWithStep
这会生成一组 sed 命令,这些命令提取步长值并将它们与具有步长值的文件进行匹配,并仅检索前 3 列。
注意这五个文件可以连接起来,也可以根据第一组 sed 命令单独命名,即将file1of5
替换为file1 file2 file3 file4 file5
如果它们包含唯一的步骤值。
【讨论】:
以上是关于是否可以从第一列包含与其他文件名称匹配的标识符的文件中提取某些行?的主要内容,如果未能解决你的问题,请参考以下文章