5

Python 爱好者在这里。我有一个列出数千行信息的文本文件,我试图根据它们是否匹配模式来选择一行和以下 2-3 行。我已将文件从原始文件中过滤出来,只包含我感兴趣的文件部分,因此我当前的文件如下所示:

trig1.RESP: 
stim4: silence.wav 
trig1.RESP: 
trig6.RESP: 1 
trig1.RESP:
trig1.RESP: 
trig5.RESP: 1
stim5: silence.wav
trig1.RESP:
trig6.RESP: 1
stim3: silence.wav
trig1.RESP:
stim5: silence.wav
trig1.RESP:
trig6.RESP: 1

依此类推……基本上我要做的是编写包含该行的silence.wav部分的每一行,然后是接下来的两行。我使用了以下代码:

parsed_output = open("name-of-file-to-be-written", "w")
filtered_input = open("name-of-file-that-has-above-data", "r")
for line in filtered_input:
    if "silence.wav" in line and "trig1" in filtered_input.next():
        parsed_output.write(line)
        parsed_output.write(filtered_input.next())
parsed_output.close()

这在大多数情况下都可以正常工作,因为它会打印silence.wav 行和具有响应的行(我最感兴趣的部分,此时响应之前的trig1 不太重要)。但是我遇到的问题是线路何时出现:

stim3: silence.wav
trig1.RESP: 
stim5: silence.wav

由于我的输出将写入 stim3(当前行)和 stim5(跳过 trig1 后的下一行),我认为它会转到下一个“stim:silence.wav”并跳过 stim5,因为它包含在上一个命令中写的时候。我想要 stim5 之后的 trig6.RESP: 1 但由于我所描述的原因,我的输出没有显示它。有没有办法让它不跳过那个 stim5?

对不起,如果这真的很长。先感谢您!

4

4 回答 4

4

这样的事情怎么样?(完全未经测试

count = 3
for line in filtered_input:
    if "silence.wav" in line:
        count = 0
    else:
        count += 1

    if count <= 2:
        filtered_output.write(line)

它并不花哨,但我认为它应该非常强大。

于 2012-11-30T21:02:18.337 回答
2

我尝试将其翻译为伪代码说:

For each (Line) {
      If Next Line is "Trig1" AND Current Line is "silence.wav"
          Log it
}
## And We're Done

(请随时在这里纠正我)

您错过了 Trig6,因为您要求下一行不存在。你能在你向后而不是向前引用的地方重写它并解决你的问题吗?

于 2012-11-30T21:08:30.177 回答
1

一种可能的方法是使用双端队列,这样您就可以一次跟踪三行:

import collections

parsed_output = open("name-of-file-to-be-written", "w")
filtered_input = open("name-of-file-that-has-above-data", "r")

window = collections.deque([None]*3, maxlen=3)
for line in filtered_input:
    window.append(line)
    if 'silence.wav' in window[0]:
        parsed_output.write(window[0])
        # only output next two lines if they don't also contain 'silence.wav'
        if 'silence.wav' not in window[1]:
            parsed_output.write(window[1])
            if 'silence.wav' not in window[2]:
                parsed_output.write(window[2])
# following if/elif in case last or second to last line contain 'silence.wav'
if 'silence.wav' in window[1]:
    parsed_output.write(window[1])
    parsed_output.write(window[2])
elif 'silence.wav' in window[2]:
    parsed_output.write(window[2])
parsed_output.close()

如果您将maxlen参数提供给 deque,那么当您向一端添加其他元素时,会从另一端弹出一个元素,例如:

>>> x = collections.deque([1, 2, 3], maxlen=3)
>>> x
deque([1, 2, 3], maxlen=3)
>>> x.append(4)
>>> x
deque([2, 3, 4], maxlen=3)
>>> x.append(5)
>>> x
deque([3, 4, 5], maxlen=3)

这允许您遍历文件,但以方便的方式保存您读取的最后 3 行,只要第一个元素window与您的条件匹配,只要输出匹配的行和后面的两行,只要它们不也符合你的条件。

于 2012-11-30T21:07:03.677 回答
1

你真的应该学习正则表达式的使用(模块 re)
当一个人想要分析文本时,它是强制性的。

看看它允许做什么:

import re

ss = """trig1.RESP: 
stim4: silence.wav 
trig1.RESP: 
trig6.RESP: 1 
trig1.RESP:
trig1.RESP: 
trig5.RESP: 1
stim5: silence.wav
trig1.RESP:
trig6.RESP: 1
stim3: silence.wav
trig1.RESP:
stim5: silence.wav
trig1.RESP:
trig6.RESP: 1

stim777: silence.wav
stim111: silence.wav 
trig1.RESP: 
trig6.RESP: 1
trig1.RESP: 
trig6.RESP: 1"""

pat = ('^(.+silence.wav.*)(?<! ) *\n'
       '(?:(?!.*silence.wav)(.*)(?<! ) *\n)?'
       '(?:(?!.*silence.wav)(.*)(?<! ) *)?')

RE = re.compile(pat,re.MULTILINE)

for tugr in RE.findall(ss):
    print tugr

结果

('stim4: silence.wav', 'trig1.RESP:', 'trig6.RESP: 1')
('stim5: silence.wav', 'trig1.RESP:', 'trig6.RESP: 1')
('stim3: silence.wav', 'trig1.RESP:', '')
('stim5: silence.wav', 'trig1.RESP:', 'trig6.RESP: 1')
('stim777: silence.wav', '', '')
('stim111: silence.wav', 'trig1.RESP:', 'trig6.RESP: 1')

然后你用这些行的元组做你想做的事

pat是一个字符串,用作定义正则表达式的模式。
RE是一个编译的正则表达式,它是一个对象,它有方法searchmatchfindall

括号( )定义一个组。
一个组捕获文本的某些部分。但是(?: )定义了一个不捕获它匹配的文本部分的组。对部分文本进行操作很有用,例如将限定符*?+放在组的末尾。

当第三行有'silence.wav'时,它必须保持不匹配,而当第二行有'silence.wav'时,只有第一行必须匹配。这就是为什么(?.*silence.wav)模式中有两个地方的部分。

^是一个符号,意思是“字符串的开头”
^,标志的re.MULTILINE 意思是“一行的开头”

图案的部分(?<! ) *在那里不会捕捉到行尾的空格。

模式中的点.表示“任何字符”,LF 字符除外

\n

和其他点,如果你需要,我可以回答你。

于 2012-11-30T21:27:21.553 回答