3

项目:

以维基百科的罗马执政官列表为例,将数据放在 CSV 中,这样我就可以绘制出各个家族在执政官方面的兴衰图表

示例数据源:

509,L. Iunius Brutus,L. Tarquinius Collatinus
suff.,Sp. Lucretius Tricipitinus,P. Valerius Poplicola
suff.,M. Horatius Pulvillus,
508,P. Valerius Poplicola II,T. Lucretius Tricipitinus
507,P. Valerius Poplicola III,M. Horatius Pulvillus II

Vim 搜索:

/\v(\d+|suff\.),((\w+\.=) (\w+)(\s\w+)=(\s\w+)=(\s[iv]+)=(\s\(.{-}\))=,=){,2}

所以本质上:

  1. 查找开头的年份(或 suffect consul 的指示):(\d+|suff\.)
  2. 下一个分组(我们称之为外组)最多需要找到两次:(outer group){,2}
  3. 对于这两个外部组中的每一个,找到:
    1. Praenomen,带有可选句号(有时不存在):(\w+.=)
    2. 无人:(\w+)
    3. 可选的认知(包括空格,如下所示):(\s\w+)=
    4. 可选的 agnome:(\s\w+)=
    5. 可选迭代​​(表示他第 n 次担任领事)。数据源的迭代次数不超过 8 次(所以 I 和 V 就足够了):(\s[iv]+)=
    6. 可选的解释性说明,例如“Sicinius (Sabinus?)”:(\s\(.{-}\))=

(最后一个逗号是可选的,因为它是行尾。)

所以后面的引用结果是:

\1: year or suffect
\2: the entire second outer group
\3: Praenomen of second outer group (same with all below)
\4: Nomen
\5: Cognomen
\6: Agnomen
\7: Iteration
\8: Explanatory note

问题是我不知道如何捕获第一个外部组。就像 \2 和 \3-\8 引用在看到第二个外部组时被覆盖。

使用此替换:

:%s//1:{\1}^I2:{\2}^I3:{\3}^I4:{\4}^I5:{\5}^I6:{\6}^I7:{\7}^I8:{\8}^I9:{\9} 

我得到这个输出:

1:{509} 2:{L. Tarquinius Collatinus}    3:{L.}  4:{Tarquinius}  5:{ Collatinus} 6:{}    7:{}    8:{}    9:{}
1:{suff.}   2:{P. Valerius Poplicola}   3:{P.}  4:{Valerius}    5:{ Poplicola}  6:{}    7:{}    8:{}    9:{}
1:{suff.}   2:{M. Horatius Pulvillus,}  3:{M.}  4:{Horatius}    5:{ Pulvillus}  6:{}    7:{}    8:{}    9:{}
1:{508} 2:{T. Lucretius Tricipitinus}   3:{T.}  4:{Lucretius}   5:{ Tricipitinus}   6:{ II} 7:{}    8:{}    9:{}
1:{507} 2:{M. Horatius Pulvillus II}    3:{M.}  4:{Horatius}    5:{ Pulvillus}  6:{ II} 7:{}    8:{}    9:{}

我无法访问第一个外部组中的那些组。我认为它们被覆盖了:它们被覆盖了吗?如果是这样,有没有办法解决这个问题?

编辑:原标题 Vim 正则表达式(或任何兼容的正则表达式):如果迭代外部组,如何引用组(组内)?

4

2 回答 2

4

我会在子步骤中分解它,使用 vim 函数而不是全部normal(双关语)方式:

/\v(.{-}),(.{-}),(.*)

看看我做了什么?让一切变得更简单、更清晰

编辑变得稍微不那么懒惰了,让我们定义一个辅助函数来拆分为至少 3 个子字符串并用制表符分隔它们:

function! Consul(s)        
    return join((split(a:s) + ["","",""])[0:2], "\t")
endf

现在将替换减少到(仅限 SO 的换行符

%s/\v(.{-}),(.{-}),(.*)/\=join(
  [submatch(1), Consul(submatch(2)), Consul(submatch(3))], "\t")/g

在您的输入上运行这种美丽会产生

509 L.  Iunius  Brutus  L.  Tarquinius  Collatinus
suff.   Sp. Lucretius   Tricipitinus    P.  Valerius    Poplicola
suff.   M.  Horatius    Pulvillus           
508 P.  Valerius    Poplicola   T.  Lucretius   Tricipitinus
507 P.  Valerius    Poplicola   M.  Horatius    Pulvillus

我很确定这将是一个非常简单的步骤,可以根据自己的喜好进一步装饰现在整齐地以制表符分隔的列。我可能会添加它,但现在,这是我能想到的最简单的事情:

:%s/\v(.{-})\t(.{-})\t(.{-})\t(.{-})\t(.{-})\t(.{-})\t(.{-})$/1:{\1}\t2:{\2}\t3:{\3}\t4:{\4}\t5:{\5}\t6:{\6}\t7:{\7}/g

结果:

1:{509} 2:{L.}  3:{Iunius}  4:{Brutus}  5:{L.}  6:{Tarquinius}  7:{Collatinus}
1:{suff.}   2:{Sp.} 3:{Lucretius}   4:{Tricipitinus}    5:{P.}  6:{Valerius}    7:{Poplicola}
1:{suff.}   2:{M.}  3:{Horatius}    4:{Pulvillus}   5:{}    6:{}    7:{}
1:{508} 2:{P.}  3:{Valerius}    4:{Poplicola}   5:{T.}  6:{Lucretius}   7:{Tricipitinus}
1:{507} 2:{P.}  3:{Valerius}    4:{Poplicola}   5:{M.}  6:{Horatius}    7:{Pulvillus}
于 2012-02-08T22:34:30.703 回答
3

是的,在重复中捕获组会被覆盖为最近的匹配值。根据链接页面底部附近的重复和反向引用部分:

正则表达式引擎不会永久替换正则表达式中的反向引用。每次需要使用时,它将使用保存到反向引用中的最后一个匹配项。如果通过捕获括号找到了新匹配,则覆盖先前保存的匹配。

您必须明确写出一定数量的捕获组。

我不是特别熟悉 vim 的正则表达式引擎,所以这里有一个简单的例子。
假设您的文本是abc 12 345 6789 xyz.

# with repetition
/^\w+( \d+){1,3} \w+$/

# yields:
# 0: abc 12 345 6789 xyz
# 1:  6789

# -----
# writing out each subpattern
/^\w+( \d+)( \d+)?( \d+)? \w+$/

# yields:
# 0: abc 12 345 6789 xyz
# 1:  12
# 2:  345
# 3:  6789

请注意,在重复范围为 的情况下{1,3},我将第二个和第三个( \d+)设置为可选?

于 2012-02-08T21:57:19.843 回答