从文本文件中获取一个模式,将文本文件分割成多个部分。

3
我有许多固定宽度数据的文本文件,例如:
$ head model-q-060.txt 
% x                      y                        
15.0                     0.0                      
15.026087                -1.0                     
15.052174                -2.0                     
15.07826                 -3.0                     
15.104348                -4.0                     
15.130435                -5.0                     
15.156522                -6.0                     
15.182609                -6.9999995               
15.208695                -8.0  

数据包含3或4次模拟的运行,全部存储在同一个文本文件中,没有分隔符。换句话说,每个运行之间没有空行或其他任何内容,例如如果每个运行仅有3个记录,则对于3个运行,它将如下所示:
$ head model-q-060.txt 
% x                      y                        
15.0                     0.0                      
15.026087                -1.0                     
15.052174                -2.0                     
15.0                     0.0                      
15.038486                -1.0                     
15.066712                -2.0                     
15.0                     0.0                      
15.041089                -1.0                     
15.087612                -2.0                     

这是一个COMSOL Multiphysics输出文件,对于那些感兴趣的人来说。从视觉上可以看出新运行数据从哪里开始,因为第一个x值被重复了(实际上,所有第二行可能都是相同的)。所以我需要首先打开文件并获取这个x值,保存它,然后将其作为模式与awk或csplit匹配。我正在努力解决这个问题!
csplit将完成这项工作:
$ csplit -z -f 'temp' -b '%02d.txt' model-q-060.txt /^15\.0\\s/ {*}

但我必须知道要分割的模式。这个问题很相似,但我的每个文本文件可能有不同的匹配模式:根据文件内容和模式匹配拆分文件
Ben。

你介意使用Python或Perl解决方案吗?我只需要写几行代码。 - DSM
接下来我将在python-matplotlib中处理数据,所以Python也很棒! - a different ben
新的运行开始是唯一的独立变量可以倒退的时候吗?这似乎比寻找重复行更为强大的检测方法。 - Ben Voigt
Ben Voigt - 是的,观察得很好。您打算如何使用它呢? - a different ben
3个回答

3
这里有一个简单的awk脚本可以实现你想要的功能:
BEGIN { fn=0 }
NR==1 { next }
NR==2 { delim=$1 }
$1 == delim {
    f=sprintf("test%02d.txt",fn++);
    print "Creating " f
}

{ print $0 > f }
  1. 初始化输出文件编号
  2. 忽略第一行
  3. 从第二行提取分隔符
  4. 对于每个第一个标记与分隔符匹配的输入行,设置输出文件名
  5. 对于所有行,写入当前输出文件

这是最优雅、非常干净易读的代码,谢谢!很抱歉我不能与你和@icyrock.com分享积分。 - a different ben
+1 对于解释,即使我可能需要一段时间才能弄明白。 - a different ben
在上面的第三行中,$1是否将整行内容捕获到delim中? - a different ben
谢谢Jim,我做了一些小改动,添加了文件头,反转了字段输出顺序,并转换为逗号分隔。非常酷。 - a different ben

1

这应该可以完成任务 - 在你没有很多temp*.txt文件的地方进行测试::)

rm -f temp*.txt

cat > f1.txt <<EOF
% x                      y                        
15.0                     0.0                      
15.026087                -1.0                     
15.052174                -2.0                     
15.0                     0.0                      
15.038486                -1.0                     
15.066712                -2.0                     
15.0                     0.0                      
15.041089                -1.0                     
15.087612                -2.0    
EOF

first=`awk 'NR==2{print $1}' f1.txt|sed 's/\\./\\\\./'`
echo --- Splitting by: $first

csplit -z -f temp -b %02d.txt f1.txt /^"$first"\\s/ {*}

for i in temp*.txt; do
  echo ---- $i
  cat $i
done

以上的输出结果为:
--- Splitting by: 15\.0
51
153
153
136
---- temp00.txt
% x                      y                        
---- temp01.txt
15.0                     0.0                      
15.026087                -1.0                     
15.052174                -2.0                     
---- temp02.txt
15.0                     0.0                      
15.038486                -1.0                     
15.066712                -2.0                     
---- temp03.txt
15.0                     0.0                      
15.041089                -1.0                     
15.087612                -2.0    

当然,如果您遇到重复的第二列数值(如上例中的 15.0 ),则可能会遇到问题 - 解决这个问题可能会有点困难 - 需要读者自行练习...

谢谢,干得好。15.0 的值直到新的执行开始才会重复出现,但 15.000000 可能会出现。 - a different ben
谢谢。15.000000 将被 \\s 过滤,所以您在这方面应该没问题了。唯一剩下的就是尝试一下... :) - icyrock.com
第一次使用awk,还有些学习要做,但这似乎是一个不错的方式,即使其中一些部分让人眼花缭乱。我已经尝试过它,它可以正常工作。 - a different ben

0
如果每次运行的行数是恒定的,您可以使用以下代码:
cat your_file.txt | grep -P "^\d" | \
   split --lines=$(expr \( $(wc -l "your_file.txt" | \
   awk '{print $1'}) - 1 \) / number_of_runs)

1
出色的横向思维,这应该是可行的,因为在每个模拟中,每次运行都将具有相同数量的“记录”。但是,我必须检查,因为有时我会使用3次运行,有时使用4次或更少。 - a different ben
当然,你需要在代码中更改“number_of_runs”变量以匹配。 - Blackle Mori

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接