【问题标题】:How to write regular expressions to match white space delimited multi-line column data如何编写正则表达式来匹配空格分隔的多行列数据
【发布时间】:2014-09-07 15:48:39
【问题描述】:

所以我有以

形式出现的日志文件
n400_108tb_48gb           2   G    1,3-7                1       20G /  286T (< 1% ) 
n400_108tb_48gb:1         1   D    1-3:bay1-6           -      2.1G /   48T (< 1% ) 
n400_108tb_48gb:3         3   D    1-3:bay7-12          -      1.9G /   48T (< 1% ) 
n400_108tb_48gb:4         4   D    1-3:bay13-18         -       10G /   48T (< 1% ) 
n400_108tb_48gb:5         5   D    1-3:bay19-24         -      2.0G /   48T (< 1% ) 
n400_108tb_48gb:6         6   D    1-3:bay25-30         -      2.2G /   48T (< 1% ) 
n400_108tb_48gb:7         7   D    1-3:bay31-36         -      1.7G /   48T (< 1% ) 

这看起来很好而且处理起来很简单,所以我可以编写正则表达式来一次处理一行。

([0-9a-z_:]*)\s*([1-9])\s*([DGPTE])\s*([0-9a-z_:,-]*)\s*([1-9])\s*([0-9.]+[KMGTPE]).*?([0-9]*[KMGTPE])

我的意思是,这很难看,但我可以将其简化为

_name =  r"([0-9a-z_:]*)\s*"
_id = r"([1-9])
_type = r"([DGPTE])"
_members = r"([0-9a-z_:,-]*)"
_vhs = r"([1-9-])"
_used = r"([0-9.]*[KMGTPE])"
_size = r"([0-9.]*[KMGTPE])"
_disk_protections_regex_string = r"{0}\s*{1}\s*{2}\s*{3}\s*{4}\s*{5}.*?{6}".format(
    _name,
    _id,
    _type,
    _members,
    _vhs,
    _used,
    _size,)

然后我发现我必须解析这种格式的文件。

s200_13tb_400gb  1     +3 system, vhs_de 1:0-23,      1      53T /  218T (25% )
-ssd_48gb-ram             ny_writes, vhs 2:0-23, 3:0-                          
                          _hide_spare,   1,3-19,21-25                          
                          ssd_metadata   , 4:0-23,                             
                                         5:0-23,                               
                                         6:0-23,                               
                                         7:0-23,                               
                                         8:0-23,                               
                                         9:0-23,                               
                                         10:0-23,                              
                                         11:0-23,                              
                                         12:0-23,                              
                                         13:0-23,                              
                                         14:0-23,                              
                                         15:0-23,                              
                                         16:0-23,                              
                                         17:0-23,                              
                                         18:2-25                               

突然期望值是

s200_13tb_400gb-ssd_48gb-ram 
system vhs_deny_writes, vhs_hide_spare, ssd_metadata
1:0-23, 2:0-23, 3:0-1,3-19,21-25, 4:0-23, 5:0-23, 6:0-23, 7:0-23, 8:0-23, 9:0-23, 10:0-23, 11:0-23, 12:0-23, 13:0-23, 14:0-23, 15:0-23, 16:0-23, 17:0-23, 18:0-23,

以及我提供的原始格式。我什至不知道从哪里开始以空格分隔的列分隔值。

【问题讨论】:

  • “空格分隔” = 制表符、空格或任何组合?
  • 看起来它只是空格,但鉴于我在这些文件中发现的格式存在巨大差异,目前我没有做任何假设。
  • 您的输入是否像第三列的第二、第三和第四行一样未对齐,或者是拼写错误?
  • 遗憾的是,目前的数据是文件的精确复制和粘贴。
  • 每个文件是否有多个记录(对于 collimated 格式)?如果是这样,有什么迹象表明它们是如何划分的?

标签: python regex parsing text


【解决方案1】:

为列定义切片,然后聚合每一行中的数据

col_1 = slice(17)
col_2 = slice(25,40)
col_3 = slice(41,54)
col_4 = slice(55,None)
one, two, three, four = list(), list(), list(), list()

with open('file.txt') as f:
    for line in f:
        one.append(line[col_1])
        two.append(line[col_2])
        three.append(line[col_3])
        four.append(line[col_4])

print ''.join(item.strip() for item in one)
print ''.join(item.strip() for item in two)
print ''.join(item.strip() for item in three)
print ''.join(item.strip() for item in four)

>>> 
s200_13tb_400gb-ssd_48gb-ram
system, vhs_deny_writes, vhs_hide_spare,ssd_metadata
1:0-23,2:0-23, 3:0-1,3-19,21-25, 4:0-23,5:0-23,6:0-23,7:0-23,8:0-23,9:0-23,10:0-23,11:0-23,12:0-23,13:0-23,14:0-23,15:0-23,16:0-23,17:0-23,18:2-25
53T /  218T (25% )
>>> 

这将从示例中显示的 collimated 格式中提取数据。如果一个文件有多条记录,则需要确定记录分隔符。

【讨论】:

  • 这使得每一列的宽度都相同的大量假设。阅读评论:“鉴于我在这些文件中发现的格式存在巨大差异,我没有做任何假设。”
  • 确实如此,但 OP 帖子中的信息有限,没有提出任何实际问题。这试图解决“我什至不知道从哪里开始以空格分隔的列分隔值”。帖子中的声明。
  • 这实际上让我更接近解决问题。似乎(可悲)有不同的列宽取决于我们的代码库生成文件的版本。但这将问题改变为确定版本并根据找到的版本使用不同的宽度。更容易的问题。
  • 第一行的那些和加三看起来确实像是分隔符 - 无论如何在视觉上。
【解决方案2】:

我创建了一个更动态的方法,它可以自行查找列定义。

说明

  1. 脚本首先在文件中查找列 行字符是空格。
  2. 然后它根据空白列之间的关系定义数据列定义。 + [len(content[0])] 在末尾添加一个额外的空白列,以便在需要时访问最后一个数据列。
  3. 使用定义的列提取数据。
  4. 如果数据与特定定义的模式匹配,则打印数据。 警告:如果每个文件有多个记录,则必须更改此步骤。

代码

import re
from collections import Counter

# Patterns to save in the end, [name, attr, values]
patterns = [r"^([0-9a-z_-]{4,}$)", r"^([a-z_,\s]*$)", r"([0-9:,\s-]{4,})$"]

# Get file content, remove any trailing empty line.
with open('/path/to/my/file') as f:
    content = f.read().split('\n')
    if not content[-1]:
        content = content[:-1]

# 1) Find all single character columns in content with only whitespaces.
no_lines = len(content)
whitespaces = [i for l in content for i, char in enumerate(l) if char == ' ']
whi_columns = [k for k, v in Counter(whitespaces).iteritems() if v == no_lines]
#                                                .items() in python3
# 2) Get all real columns that are between whitespace columns.
columns_defs = []
for i, whi_col in enumerate(whi_columns + [len(content[0])]):
    if whi_col and not i: #special first column
        columns_defs.append(slice(whi_col))
    if whi_col > whi_columns[i - 1] + 1:
        columns_defs.append(slice(whi_columns[i - 1] + 1, whi_col))

# 3) Extract columns from file content.
data_columns = [[line[col].strip() for line in content] for col in columns_defs]

# 4) Save columns fitting patterns.
for data_col in data_columns:
    data = ''.join(data_col)
    if re.match(r'|'.join(patterns), data):
        print data

输出

s200_13tb_400gb-ssd_48gb-ram
system, vhs_deny_writes, vhs_hide_spare,ssd_metadata
1:0-23,2:0-23, 3:0-1,3-19,21-25, 4:0-23,5:0-23,6:0-23,7:0-23,8:0-23,9:0-23,10:0-23,11:0-23,12:0-23,13:0-23,14:0-23,15:0-23,16:0-23,17:0-23,18:2-25

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多