提取 .txt 文件中两个关键字之间的所有单词
Posted
技术标签:
【中文标题】提取 .txt 文件中两个关键字之间的所有单词【英文标题】:Extract all words between two keywords in .txt file 【发布时间】:2020-04-05 00:30:26 【问题描述】:我想提取 .txt 文件中特定关键字中的所有单词。对于关键字,有一个起始关键字PROC SQL;
(我需要它不区分大小写),结束关键字可以是RUN;
、quit;
或QUIT;
。这是我的示例.txt
file。
到目前为止,这是我的代码:
with open('lan sample text file1.txt') as file:
text = file.read()
regex = re.compile(r'(PROC SQL;|proc sql;(.*?)RUN;|quit;|QUIT;)')
k = regex.findall(text)
print(k)
输出:
[('quit;', ''), ('quit;', ''), ('PROC SQL;', '')]
但是,我的预期输出是获取介于关键字之间的单词:
proc sql; ("TRUuuuth");
hhhjhfjs as fdsjfsj:
select * from djfkjd to jfkjs
(
SELECT abc AS abc1, abc_2_ AS efg, abc_fg, fkdkfj_vv, jjsflkl_ff, fjkdsf_jfkj
FROM &xxx..xxx_xxx_xxE
where ((xxx(xx_ix as format 'xxxx-xx') gff &jfjfsj_jfjfj.) and
(xxx(xx_ix as format 'xxxx-xx') lec &jgjsd_vnv.))
);
1)
jjjjjj;
select xx("xE'", PUT(xx.xxxx.),"'") jdfjhf:jhfjj from xxxx_x_xx_L ;
quit;
PROC SQL; ("CUuuiiiiuth");
hhhjhfjs as fdsjfsj:
select * from djfkjd to jfkjs
(SELECT abc AS abc1, abc_2_ AS efg, abc_fg, fkdkfj_vv, jjsflkl_ff, fjkdsf_jfkj
FROM &xxx..xxx_xxx_xxE
where ((xxx(xx_ix as format 'xxxx-xx') gff &jfjfsj_jfjfj.) and
(xxx(xx_ix as format 'xxxx-xx') lec &jgjsd_vnv.))(( ))
);
2)(
RUN;
任何建议或解决此问题的不同方法将不胜感激!
执行用户@Finefoot 的代码后的输出:
但是,有没有办法将线条分开,使其看起来像这样?:
【问题讨论】:
考虑使用正则表达式 你忘记了re.DOTALL
标志
【参考方案1】:
我认为,在您的模式中 (PROC SQL;|proc sql;(.*?)RUN;|quit;|QUIT;)
是一个错字,因为您在 proc sql;
之后和 (.*?)
之前缺少右括号 )
以及之后的左括号 (
。然而,这还不是全部,修正错字后,您仍然无法获得想要的结果。
查看re
的 Python 文档:
.
(点)在默认模式下,它匹配除换行符以外的任何字符。如果指定了DOTALL
标志,则匹配任何字符,包括换行符。
由于您的输入确实包含您希望.
匹配的换行符,因此您需要使用re.DOTALL
标志。当我们讨论标志时:如果您真的不关心关键字是否区分大小写,您可能还想使用re.IGNORECASE
标志。
另外,我猜你不希望你的结果中出现像PROC SQL;
这样的关键字,所以你可以使用(?:...)
,这是常规括号的非捕获版本。
最终的正则表达式模式:
re.findall(r"(?:PROC SQL;)(.*?)(?:RUN;|QUIT;)", text, flags=re.IGNORECASE|re.DOTALL)
更新:
在上面 Jupyter 单元格中的更新代码中,re.findall
的结果保存为变量 regex
。这是一个与模式匹配的字符串列表。如果您调用print(regex)
,您将打印列表(它将显示其元素,字符串,\n
)。如果您不想要\n
,您可以打印元素(字符串本身):print(*regex)
不过,两个元素之间的默认分隔符将是一个简单的空格字符,因此您可能希望将sep
设置为其他类似多个换行符print(*regex, sep="\n"*5)
或-----
的分隔线print(*regex, sep="\n"+"-"*44+"\n")
。但这是您必须决定哪种方式最适合您展示您的结果。
此外,如果该模式看起来还不是太令人困惑,您可能希望使用“内联修饰符”而不是 flags
参数。 (?i:...)
用于不区分大小写的匹配,(?s:...)
而不是 DOTALL
标志:
re.findall(r"(?i:PROC SQL;)((?s:.*?))(?i:RUN;|QUIT;)", text)
【讨论】:
【参考方案2】:这对我有用:
import re
with open('lan sample text file1.txt') as file:
condition = False
text_to_return = ""
for line in file:
if condition == True:
if line[0:5].lower() == "quit;" or line[0:4].upper() == "RUN;":
condition = False
text_to_return += line
if line[0:9].upper() == "PROC SQL;":
condition = True
text_to_return += line
output_file = open("output.txt", "w")
output_file.write(text_to_return)
output_file.close()
这是您可以接受的解决方案吗?
【讨论】:
你好 asymmetryFan,谢谢!我试过你的代码,它有效!虽然它可能不适用于我拥有的其他类似的.txt
文件,这些文件不包含与您在代码中编写的行范围相同的开始和结束词。
@jackie 不客气,总是乐于助人! :) 我希望我的代码至少可以作为泛化多个文件的起点。【参考方案3】:
不想使用正则表达式的解决方案:
starts=["PROC SQL;"]
ends = ["RUN;", "RUN;", "QUIT;"]
with open('/tmp/some_file.txt') as f:
content = f.read()
for s, e in zip(starts, ends):
if s.lower() in content.lower() and e.lower() in content.lower():
start = content.lower().find(s.lower())
end = content.lower().find(e.lower()) + len(e)
print(content[start:end])
对你有帮助吗?
【讨论】:
嘿菲利克斯,非常感谢您的代码,它可以工作:) 但是当我在我的工作桌面上尝试它时,我有多个文件要解析,不仅仅是一个,它似乎无法捕获包含关键字的所有内容,跨多个.txt
文件。【参考方案4】:
通过匹配关键字可以获得更高效的匹配,匹配所有不以quit
或RUN
开头的行,防止.*?
引起不必要的回溯
如果您希望关键字包含在匹配中,您可以省略捕获组。
您可以使用re.IGNORECASE
标志来获得不区分大小写的匹配,并使用re.MULTILINE
,因为该模式包含一个断言字符串开头的锚。
^PROC SQL;.*\n(?:(?!RUN;|QUIT;).*\n)*(?:RUN|QUIT);
^
行首
PROC SQL;
字面上匹配
.*\n
匹配除换行符以外的任何字符 0+ 次,然后匹配换行符(或使用 \r?\n
(?:
非捕获组
(?!RUN;|QUIT;)
断言右边不是RUN;
或QUIT;
.*\n
匹配除换行符以外的任何字符 0+ 次,然后匹配换行符
)*
关闭组并重复0+次
(?:RUN|QUIT);
匹配 RUN;
或 QUIT;
Regex demo | Python demo
例如
with open('lan sample text file1.txt') as file:
text = file.read()
regex = re.compile(r'^PROC SQL;.*\n(?:(?!RUN;|QUIT;).*\n)*(?:RUN|QUIT);', re.MULTILINE | re.IGNORECASE)
k = regex.findall(text)
print(k)
【讨论】:
谢谢,第四只鸟!您的代码有效:) 但我想它是特定于大小写的,只能应用于这个特定的.txt
文件,而不适用于我的其他 .txt
文件,它们相似但在 .txt
中的关键字放置有所不同文件。以上是关于提取 .txt 文件中两个关键字之间的所有单词的主要内容,如果未能解决你的问题,请参考以下文章