如何匹配以单个空格分隔的单词和以多个空格分隔的单词?

3

我需要从以下类似的文本中分离出键和值:

学生编号: 0
部门编号 = 18432
姓名                        XYZ

Subjects:
Computer Architecture
Advanced Network Security 2

在上面的例子中,学生ID、部门ID和姓名是关键字,0、18432、XYZ是值。关键字与值之间用:、=或多个空格分隔。我尝试使用正则表达式,例如


    $line =~ /(([\w\(\)]*\s)*)([=:\s?]?)\s*(\S.*)?$/;
    $key   = $2;
    $colon=$3;
    $value = $4;

我面临的问题是如何识别单个空格和多个空格分隔的单词。
我得到的输出是:行是学生ID:0,键是Student,值是ID:0,而我想要的是键是学生ID,值为0。对于像Subjects: 和Computer Architecture这样的行,键应该有Subjects和Computer Architecture。当没有值或冒号时我将字符串附加到前一个键上,使其看起来像Subjects=Computer Architecture;Advanced Network Security 2。
更新:感谢Ikegami指出我应该使用回顾操作符,但是我仍然似乎无法解决它。
$line=~/^(?: ( [^:=]+ ) (?<!\s\s)\s* [:=]\s*|\s*)(.*)$/x;

当我说(?<!\s\s)\s* [:=]\s*|\s*时,我指的是当有两个以上的空格时,消耗所有空格;当没有两个连续的空格时,查找:或=并消耗空格。如果您将以下行传递给表达式,那么$1应该是Name,$2应该是ABC XYZ,对吗?
Name         ABC XYZ

我看到的是键为空,值为名称 ABC XYZ。
2个回答

4
如果
Name Eric Brine
Computer Architecture x86

means

key: Name Eric               value: Brine
key: Computer Architecture   value: x86

那么你想要什么?
# Requires 5.10
if (/
   ^
   (?: (?<key> [^:=]+ (?<!\s) ) \s* [:=] \s* (?<val> .*  )
   |   (?<key> .+     (?<!\s) ) \s+          (?<val> \S+ )
   )
   \s* $
/x) {
   my $key = $+{key};
   my $val = $+{val};
   ...
}

或者
if (/
   ^
   (?: ( [^:=]+ (?<!\s) ) \s* [:=] \s* ( .*  )
   |   ( .+     (?<!\s) ) \s+          ( \S+ )
   )
   \s*
   ( .* )
/x) {
   my ($key,$val) = defined($1) ? ($1,$2) : ($3,$4);
   ...
}

如果
Name Eric Brine
Computer Architecture x86

意思是

key: Name       value: Eric Brine
key: Computer   value: Architecture x86

那么你想要什么?
# Requires 5.10
if (/
   ^
   (?: (?<key> [^:=]+ (?<!\s) ) \s* [:=]
   |   (?<key> \S+ ) \s
   )
   \s*
   (?<val> .* )
/x) {
   my $key = $+{key};
   my $val = $+{val};
   ...
}

或者
if (/
   ^
   (?: ( [^:=]+ (?<!\s) ) \s* [:=]
   |   ( \S+ ) \s
   )
   \s*
   ( .* )
/x) {
   my $key = defined($1) ? $1 : $2;
   my $val = $3;
   ...
}

请注意,您可以删除所有空格和换行符。例如,最后一个代码片段可以写成:
if (/^(?:([^:=]+(?<!\s))\s*[:=]|(\S+)\s)\s*(.*)/) {
   my $key = defined($1) ? $1 : $2;
   my $val = $3;
   ...
}

如果您可以接受修剪键,则可以简化它,但是我的解决方案已经比Junuxx的更短了。 - ikegami
完成了!但是我不知道发生了什么。我错过了问题中的一个要求,在“计算机体系结构”这一行,它将“计算机”作为键,将“体系结构”作为值。我希望以“计算机体系结构”作为键。 - learningtocode
@learningtocode,你是否也希望将“Name Eric Brine”作为键,将“Eric Brine”作为值? - ikegami
这就是你提出的解决方案正在做的事情。如果Eric和Bane与Name不在同一行,那么是的,但如果他们在同一行,所有内容都应该放入key中。谢谢ikegami,它解决了大部分问题,也许如果我更好地理解你的表达方式,我就能解决剩下的问题。我会发布我找到的东西。 - learningtocode
不确定是肯定还是否定。已更新。 - ikegami
我是指不。但我希望将“计算机体系结构”这一行解释为key=计算机体系结构.value,冒号为空。 - learningtocode

1

尝试将关键部分指定为两个文本位,中间可以选择性地加入空格;

$line =~ /([\w\(\)]*\s?[\w\(\)]*)\s*([=:]?)\s*(\S.*)?$/;

这应该可以捕获一个词和两个词的关键字。


关键字可能超过两个单词。 - learningtocode
他和我的代码都可以接受使用“:”或“=”时键名为多个单词的情况,但是对于第三种语法,无法接受带有空格的键名(因为无法确定“abc def ghi”是指“abc: def ghi”还是“abc def: ghi”)。 - ikegami

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接