我在编程方面只有很少的经验,目前我正在努力提高我的技能。基本上,我需要编写一个程序,可以对 .txt 文件中的某些数据执行一些特定的处理。
从头开始,我有一个 .txt 文件,其中的数据如下所示:
>tex_1 abcdefghijklmnopqrstu
>tex_2 abcdefghijklmnopqrstuv
>tex_3 abcdefghijklmnopqrstuv
>tex_4 abcdefghijklmnopqrst
// x
>tex_1 abcdefghijklmnopqrstu
>tex_2 abcdefghijklmnopqrstuv
>tex_3 abcdefghijklmnopqrst
>tex_4 abcdefghijklmnopqrstuv
// x x
>tex_1 abcdefghijklmnopqrstuv
>tex_2 abcdefghijklmnopqrstuv
// x x
我需要对这些数据做一些奇怪的事情,以便最终得到一个数据集,可以在我使用的软件中进行分析。每个“//...”行引用上面的数据组,直到下一个“//...”行
这是我想做的一系列事情:
移动“//...”行,因此它引用的数据组位于该行下方,而不是上方:
// x
>tex_1 abcdefghijklmnopqrstu
>tex_2 abcdefghijklmnopqrstuv
>tex_3 abcdefghijklmnopqrstuv
>tex_4 abcdefghijklmnopqrst
// x x
>tex_1 abcdefghijklmnopqrstu
>tex_2 abcdefghijklmnopqrstuv
>tex_3 abcdefghijklmnopqrst
>tex_4 abcdefghijklmnopqrstuv
// x x
>tex_1 abcdefghijklmnopqrstuv
>tex_2 abcdefghijklmnopqrstuv
在 // 之后为每个组添加唯一的名称,而不移动该行中的剩余文本:
//Name 1 x
>tex_1 abcdefghijklmnopqrstu
>tex_2 abcdefghijklmnopqrstuv
>tex_3 abcdefghijklmnopqrstuv
>tex_4 abcdefghijklmnopqrst
//Name 2 x x
>tex_1 abcdefghijklmnopqrstu
>tex_2 abcdefghijklmnopqrstuv
>tex_3 abcdefghijklmnopqrst
>tex_4 abcdefghijklmnopqrstuv
//Name 3 x x
>tex_1 abcdefghijklmnopqrstuv
>tex_2 abcdefghijklmnopqrstuv
将其输出到新文件,而不更改原始文件。然后抓取下面的每个名称行 + 行,并将其输出到 File2:
//Name 1 x
>tex_1 abcdefghijklmnopqrstu
//Name 2 x x
>tex_1 abcdefghijklmnopqrstu
//Name 3 x x
>tex_1 abcdefghijklmnopqrstuv
更改结构,命名如下,并将其输出到File3:
>Name 1 abcdefghijklmnopqrstu
>Name 2 abcdefghijklmnopqrstu
>Name 3 abcdefghijklmnopqrstuv
上面的数据是一个结构,我其实可以分析一下。
现在我知道这是一项艰巨的任务,我不是在问你“我该如何编程”。我只是想知道,你们会从哪里开始这样一个项目,以及你们认为哪种语言最适合这个项目?
通过在这个网站上获得帮助,我成功地在 UNIX 上做了一些事情。例如,通过以下 unix 代码为每个“//...”行赋予唯一的名称:
awk -F '' '/\/\//{n++ ; t=" Name "n ; sub("// {0,"length(t)-1"}","//"t)}{print}' File1.txt
您能给我一些从哪里开始的提示吗?
这个问题适合作为Python项目吗?
原始.txt数据文件包含大量数据,因此无法进行手工处理。此外,该项目也是进一步深入编程的一种方式。
答案1
对我来说,这看起来更像是一个 python 工作。一般的经验法则是:如果您的任务仅需要“平面”和内容盲处理,那么核心实用程序(最好是 gnu)是最佳选择。这适用于字符串替换、删除、基于行的处理、简单排序、计数、过滤等等...这些工具可以让你非常快速地编写一行代码来完成你想要的事情,经过练习,它甚至不需要需要很多思考。
另一方面,如果您需要一项复杂的工作,需要查看文件,暗示分层(树状)结构,自定义分隔符和上下文感知字符串,那么在您的文件中使用相同的结构会更容易编程语言。一个极端的例子是 html/xml/json 文件、任何带有嵌套大括号的文件等等...如果您有非常强大的可用数据结构(Python 就有),只需将其写入,以类似对象的方式处理并读取它退出。您仍然可以在 awk 中完成此操作(或者在 sed 中付出更多努力),但这不值得。
在您的情况下,您将需要记住前一个标记的位置(或保留一个长缓冲区),这对于行处理软件来说有点笨拙。然而,在 python 中,这很容易。
示例代码:
#!/usr/bin/python
import sys
with open(sys.argv[1],'r') as file, open(sys.argv[2],'w') as file1, open(sys.argv[3],'w') as file2, open(sys.argv[4],'w') as file3:
counter = 1
current_buffer = []
for line in file:
if line.startswith('//'):
#we found a delimiter, flush the buffer
#could use regular expressions, but for the sake of this example
#this is enough
prefix = '//Name {}'.format(counter)
new_header = prefix+line[len(prefix):]
file1.write(new_header)
file2.write(new_header)
for oldline in current_buffer:
file1.write(oldline)
if current_buffer: #only first line to file 2
first_line = current_buffer[0]
file2.write(first_line)
#same here, could use regular expressions from "import re" but we don't have to
rest_of_line = first_line.split(' ',1)[1]
file3.write('//Name {} {}\n'.format(counter,rest_of_line.strip()))
current_buffer=[]
counter+=1
else:
current_buffer.append(line)
#if the file is well-formatted, current_buffer should be empty now - otherwise, do special handling
称之为./test.py inputfile file1 file2 file3
,看看这是否是您想要的。
答案2
你应该选择让你最舒服的方式。也就是说,您也不应该避免学习新工具的使用。我对 非常满意sed
,而且我相信它nl
特别适合这项任务,因此我结合使用了这两种工具:
<<\INFILE \
nl -bp'^//' -w1 -s' ' |\
sed -ne ' s|^ *>|>|;//H;x;//x;t' -e 'G
x;s|.*|### The following is written to: File1|p
x;s|\([0-9]*\)\( *\)\( \)*//\2|//Name \1\3|pw File1
x;s|1|2|p;x;s|\n\(\n.*\)*|&&|2;s|||2p;w File2' -e 'x;s|2|3|p
x;s|//\([^ ]* [^ ]*\).*\n[^ ]*|>\1|pw File3'
>tex_1 abcdefghijklmnopqrstu
>tex_2 abcdefghijklmnopqrstuv
>tex_3 abcdefghijklmnopqrstuv
>tex_4 abcdefghijklmnopqrst
// x
>tex_1 abcdefghijklmnopqrstu
>tex_2 abcdefghijklmnopqrstuv
>tex_3 abcdefghijklmnopqrst
>tex_4 abcdefghijklmnopqrstuv
// x x
>tex_1 abcdefghijklmnopqrstuv
>tex_2 abcdefghijklmnopqrstuv
// x x
...这似乎可以解决问题。
当我说nl
非常适合这项任务这是因为,除了根据模式轻松地按顺序对块进行编号并--separator
在编号行上的文本和行号之间插入任意字符串之外,nl
还总是缩进行没有编号以匹配它添加的缩进。
这意味着任何未编号为与^//
模式匹配的行现在都以 a 开头<space>
。因此,即使您的实际文本比示例中的数据复杂得多,这sed
实际上也很容易做到。事实上,上述大部分工作只是为了生成有意义的调试输出sed
。一个功能齐全的语句可以简单如下:
nl -bp'^//' -w1 -s' ' <infile |
sed -ne 's|^ *>|>|;//H;x;//x;t' -e 'G;h
s|\([0-9]*\)\( *\)\( \)*//\2|//Name \1\3|w File1
s|\n|&&|2;s|\n\n.*||;w File2
s|//\([^ ]* [^ ]*\).*\n[^ ]*|>\1|w File3'
我对你的说法有点困惑不移动该行的剩余文本因为它似乎与你的例子相矛盾(以及可能性的领域)。我这样说是因为:
// x
……明显转移了一点当它转换为右侧时:
//Name 1 x
......事实上1和X在两个示例中占据同一列。我试图在中间解决这个矛盾 - 它不会移动文本,除非它必须避免覆盖非字符,<space>
同时仍然插入该Name [num]
对加一个空格。
因此并没有考虑到...之间的差异。
//Name 3 x x
...和...
// x x
...我已经忽略了(希望?)示例中的一个错误。
我的管道将所有内容打印到标准输出,但以 开头的行后面的块###
也依次附加到指定的文件中。这是我的管道打印的内容:
### The following is written to: File1
//Name 1 x
>tex_1 abcdefghijklmnopqrstu
>tex_2 abcdefghijklmnopqrstuv
>tex_3 abcdefghijklmnopqrstuv
>tex_4 abcdefghijklmnopqrst
### The following is written to: File2
//Name 1 x
>tex_1 abcdefghijklmnopqrstu
### The following is written to: File3
>Name 1 abcdefghijklmnopqrstu
### The following is written to: File1
//Name 2 x x
>tex_1 abcdefghijklmnopqrstu
>tex_2 abcdefghijklmnopqrstuv
>tex_3 abcdefghijklmnopqrst
>tex_4 abcdefghijklmnopqrstuv
### The following is written to: File2
//Name 2 x x
>tex_1 abcdefghijklmnopqrstu
### The following is written to: File3
>Name 2 abcdefghijklmnopqrstu
### The following is written to: File1
//Name 3 x x
>tex_1 abcdefghijklmnopqrstuv
>tex_2 abcdefghijklmnopqrstuv
### The following is written to: File2
//Name 3 x x
>tex_1 abcdefghijklmnopqrstuv
### The following is written to: File3
>Name 3 abcdefghijklmnopqrstuv
答案3
我不同意 python 在这种情况下更容易被接受。没有困难的行操作 - 只需设置变量并打印各种变量集。所以这是通常的 bash 操作。所以我们需要一些我更喜欢的计算,awk
而不是sed
(通常的文本操作程序)。
awk -F '' '
/\/\//{
n++
t="Name "n" "
sub("// {0,"length(t)"}","//"t)
print $0 "\n" l1 l2 > "File2"
print $0 "\n" l1 > "File3"
sub("[^ ]* ","<"t,l1)
print l1 > "data.file"
l1=""
l2=""
next
}
{
if (l1=="")
l1=$0
else l2=l2 "\n" $0
}' Input.txt
但每个人都更喜欢自己更了解的工具。
PS 关于输入字符串的长度。如果OP检查你的例子你可以看到«x» is above «e»
// x
>tex_1 abcdefghijklmnopqrstu
但输入后Name 1
«x» is above «d»
//Name 1 x
>tex_1 abcdefghijklmnopqrstu
所以在脚本中length(t)
不应该是lenght(t)-1
。此外,如果没有足够的空间来输入名称脚本,则需要将字符串的剩余部分向右移动。