首页 > 解决方案 > 在某些条目充当后续条目的标题的列表中循环的最 Pythonic 方式是什么?

问题描述

作为记录 CLI 工具命令的系统输出的结果...

import subprocess
list_operations_cmd = 'cloud_cli_tool --list'
all_operations_sysout = subprocess.check_output(list_operations_cmd, shell=True)

我得到一个看起来像这样的字符串:

Project: foo foo foo (Environment: PRODUCTION)
  ipsum dolor sit
  Excepteur sint occaecat
  POST aliquip ex ea
Project: foo foo foo (Environment: DEVELOPMENT)
  ipsum dolor sit
  Excepteur sint occaecat
  POST aliquip ex ea
Project: bar (Environment: PRODUCTION)
  velit esse cillum
  occaecat cupidatat

我想把它变成一个看起来像这样的数据结构,以促进我的工作对我正在编写这个 Python 脚本以向系统用户公开的命令接口的参数进行输入清理。

[
 {
  project: "foo foo foo",
  environment: "PRODUCTION",
  operation: "ipsum dolor sit"
 },
 {
  project: "foo foo foo",
  environment: "PRODUCTION",
  operation: "Excepteur sint occaecat"
 },
 {
  project: "foo foo foo",
  environment: "PRODUCTION",
  operation: "POST aliquip ex ea"
 },
 {
  project: "foo foo foo",
  environment: "DEVELOPMENT",
  operation: "ipsum dolor sit"
 },
 {
  project: "foo foo foo",
  environment: "DEVELOPMENT",
  operation: "Excepteur sint occaecat"
 },
 {
  project: "foo foo foo",
  environment: "DEVELOPMENT",
  operation: "POST aliquip ex ea"
 },
 {
  project: "bar",
  environment: "PRODUCTION)",
  operation: "velit esse cillum"
 },
 {
  project: "bar",
  environment: "PRODUCTION)",
  operation: "occaecat cupidatat"
 }
]

如您所见,项目/环境的变化可以通过没有缩进的换行来检测;可以通过换行符后跟两个空格来检测记录的更改。项目/环境行充当以下行的“标题”,直到检测到新的“项目/环境行”。

我知道我可以只做.splitlines()整个事情并开始一次循环一行,缓存东西并 if-then-elsing ......但感觉可能有一种比 Pythonic 更复杂的方式来做到这一点:

proj_env_pattern = r'^Project: (.*) \(Environment: (.*)\)$'
all_operations_list = all_operations_sysout.splitlines()
for line in all_operations_list[:20]:
    is_header = False
    if not line.startswith('  '):
        is_header = True
        project = re.search(proj_env_pattern, line, re.IGNORECASE).group(1)
        print project
        # TO DO:  code...
    # TO:  code...

Linux 上的 Python 2.7.5;无法安装任何新模块。


更新:

感谢@fsimonjetz 的提示。如果你想让它成为答案,我可以接受并关闭。

代码现在比我做的要整洁得多:

import re
import subprocess

def get_all_operations():
    list_operations_cmd = 'cloud_cli_tool --list'
    proj_env_pattern = r'^(.*) \(Environment: (.*)\)$'
    all_operations_list = []
    all_projects = subprocess.check_output(list_operations_cmd, shell=True).split('Project: ')
    for project in all_projects[1:]:
        project_lines = project.splitlines()
        project_search = re.search(proj_env_pattern, project_lines[0])
        for line in project_lines[1:]:
            all_operations_list.append({'project': project_search.group(1), 'environment': project_search.group(2), 'operation': line.lstrip()})
    return all_operations_list

print get_all_operations()

标签: pythonpython-2.7text-parsing

解决方案


感谢@fsimonjetz 的提示。如果你想让它成为一个答案,我可以接受,但在评论中添加我在你的建议中实施的内容,这样对于那些匆忙寻找答案的人来说,这不再是没有答案的。

解决方案是利用所有“标题行”开头的幸运事实,Project: 并将整个 sysout 字符串分解为每个标题子字符串.split('Project: ')。对于每个子字符串,进一步的解析是通常的正则表达式.splitlines()、、&[1:]列表切片。

import re
import subprocess

def get_all_operations():
    list_operations_cmd = 'cloud_cli_tool --list'
    proj_env_pattern = r'^(.*) \(Environment: (.*)\)$'
    all_operations_list = []
    all_projects = subprocess.check_output(list_operations_cmd, shell=True).split('Project: ')
    for project in all_projects[1:]:
        project_lines = project.splitlines()
        project_search = re.search(proj_env_pattern, project_lines[0])
        for line in project_lines[1:]:
            all_operations_list.append({'project': project_search.group(1), 'environment': project_search.group(2), 'operation': line.lstrip()})
    return all_operations_list

print get_all_operations()

推荐阅读