难倒正则表达式

时间:2014-06-27 13:18:23

标签: python regex

我在文件中有这样的行:

l_12_interval         j_10_int
Length:100         Min.   :-2120803808
Class :character   1st Qu.: -992076064
Mode  :character   Median :  263935522
                   Mean   :  -33801580
                   3rd Qu.:  896644601
                   Max.   : 1890084945
                   NA's   :53

我想解析我称之为“主要专栏”的内容:

   j_10_int
Min.   :-2120803808
1st Qu.: -992076064
Median :  263935522
Mean   :  -33801580
3rd Qu.:  896644601
Max.   : 1890084945
NA's   :53

列将对齐,但我不能依赖于最后一个主列的起始位置。标题不是问题,我正在尝试为Python的re.sub()函数组成一个正则表达式,以去除标签之前的所有内容。我认为我可以通过将正则表达式中的标签和冒号包含为子表达式并将匹配表达式替换为子表达式来实现。说起来容易做起来难!这是我得到的最接近的:

>>> line
'       Length:100         Min.   :-2120803808'
>>> re.sub(r"^.*([a-z1-9][a-z1-9.' ]*:)", r"\1", line, re.IGNORECASE)
'n.   :-2120803808'
>>>

我认为我可以在子表达式开始之前立即抛出一个空格,但这不起作用:

>>> re.sub(r"^.*\s([a-z1-9][a-z1-9.' ]*:)", r"\1", line, re.IGNORECASE)
'       Length:100         Min.   :-2120803808'
>>> re.sub(r"^.* ([a-z1-9][a-z1-9.' ]*:)", r"\1", line, re.IGNORECASE)
'       Length:100         Min.   :-2120803808'
>>> re.sub(r"^.*( [a-z1-9][a-z1-9.' ]*:)", r"\1", line, re.IGNORECASE)
'       Length:100         Min.   :-2120803808'
>>> re.sub(r"^.*(\w[a-z1-9][a-z1-9.' ]*:)", r"\1", line, re.IGNORECASE)
'in.   :-2120803808'

正如你所看到的,我甚至试图在子表达式中拉出空格......这是可以接受的。但我仍然没有更接近完整的解决方案。

有人有任何建议吗?

4 个答案:

答案 0 :(得分:2)

这个假设基于名称的格式和第一列的值的假设,但它适用于您的示例:

^(?:[A-Z][a-z]+\s*:[a-z0-9]*|)\s*([A-Z0-9].*)$

根据您对不同名称和值的格式的确切了解,可能需要更多的工作。

演示: http://regex101.com/r/oP3pT2

答案 1 :(得分:0)

如果您不必使用正则表达式并且列具有固定宽度,则以下示例也适用于您。

with open("data", "rb") as f:
    for line in f:
        print(line.strip("\n")[19:])

这将输出:

   j_10_int
Min.   :-2120803808
1st Qu.: -992076064
Median :  263935522
Mean   :  -33801580
3rd Qu.:  896644601
Max.   : 1890084945
NA's   :53

答案 2 :(得分:0)

请注意,不要编写python正则表达式。

因此,如果这是一次性工作,那么我就有了perl oneliner。

perl -pe 's/^\w++\s++(\w++)/\t$1/||s/.*?\s*+:\s*+\w++\s++(.*)/$1/||s/\s++(.*)/$1/' FILE.txt > NEWFILE.txt

从文件FILE.txt到文件NEWFILE.txt

生成所需的输出

我希望这很有用。

诀窍是在:之前寻找垃圾,然后是可能的空格和一组单词字符,然后是一些空格。在那之后的所有事情都是你想要的。

我还处理了其他情况,比如第一行和某些行上的前导空格。

答案 3 :(得分:0)

我希望倒数第二列中的值不能包含空格,因为在这种情况下,通常,我们无法将列中的标题与上一列中的值区分开来。这在(虚构的)示例中显示:

Length:100    Ticks   Min.   :-2120803808
Class :char   Acter   1st Qu.: -992076064
Mode  :char   Acter   Median :  263935522

假设冒号后的值不能包含空格(或者我们至少有一个没有空格)我建议找到最后一列的边距,就像我们用眼睛做的那样:统计上,找到看起来像的缩进在文件中的所有列的开头。这可能是实施:

import re
import itertools

with open('stat.txt', 'r') as sf:
    next(sf)            # Skip headers.
    lines = list(sf)    # Read file to memory.

# Find the last colon and the next-to-last one.
lc = lines[0].rfind(':')
nlc = lines[0].rfind(':', 0, lc)

# Collect indents statistics.
indents = {}
for line in lines:
    words = re.finditer('([\w\.]+)', line[nlc+1:lc])
    for match in words:
        indent = match.start()
        if indent in indents:
            indents[indent] += 1
        else:
            indents[indent] = 1

# Find most "popular" indent. Assumed to be only one.
pi_freq = max(indents.values())
pi_idxs = itertools.filterfalse(
    lambda x: indents[x] != pi_freq or x == 0, indents.keys())
pi_val = next(pi_idxs)

# WE HAVE FOUND IT!
last_col_idx = nlc + pi_val + 1

# And now we are ready.
for line in lines:
    print(line[last_col_idx:], end='')

当然,如果您不喜欢它,则无需在内存中读取文件。我们可以简单地访问它两次。