Как сопоставлять слова, разделенные одним пробелом, и слова, разделенные несколькими пробелами

Мне нужно отделить ключ и значения от текста, который выглядит ниже

Идентификатор студента: 0
Идентификатор факультета = 18432
Имя XYZ

Subjects:
Computer Architecture
Advanced Network Security 2

В приведенном выше примере идентификатор студента, идентификатор отдела и имя являются ключами, а 0,18432, XYZ являются значениями. Ключи отделяются от значений либо знаком :,=, либо несколькими пробелами. Я пробовал reg ex, например

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

Проблема, с которой я сталкиваюсь, заключается в том, чтобы определить, когда слово разделено одним пробелом, а когда оно разделено более чем одним.

Вывод, который я получаю, представляет собой строку «Идентификатор студента: 0», ключ «Студент», значение — «Идентификатор: 0», в то время как я хочу, чтобы ключ — «Идентификатор студента», а значение — 0. Для таких строк, как «Предметы:» и «Архитектура компьютера», ключ должен иметь «Предметы» и «Архитектура компьютера». У меня есть логика позже, когда нет значения или двоеточия, я добавляю строки к предыдущему ключу, чтобы он выглядел как Subjects=Computer Architecture;Advanced Network Security 2

Обновление: спасибо, Икегами, за то, что указал, что я использую оператор просмотра. Но у меня все еще есть проблема с ее решением.

$line=~/^(?: ( [^:=]+ ) (?<!\s\s)\s* [:=]\s*|\s*)(.*)$/x;

Поэтому, когда я говорю (?<!\s\s)\s* [:=]\s*|\s*, я имею в виду, что когда пробелов больше двух, используйте все пробелы, а когда нет двух последовательных пробелов, ищите: или = и используйте пробелы. Итак, если вы передадите строку ниже в выражение, разве я не должен получить $1 = Name и $ 2 = ABC XYZ?

Name         ABC XYZ

То, что я, кажется, получаю, это то, что ключ пуст, а значение - Name ABC XYZ.


person learningtocode    schedule 03.10.2012    source источник


Ответы (2)


If

Name Eric Brine
Computer Architecture x86

означает

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};
   ...
}

or

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

If

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};
   ...
}

or

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;
   ...
}
person ikegami    schedule 03.10.2012
comment
Это можно было бы упростить, если бы вы были в порядке с обрезкой ключа, но мое решение уже короче, чем у Junuxx. - person ikegami; 03.10.2012
comment
Это делает это! Но я понятия не имею, что происходит. Я пропустил одно из требований в вопросе, для строки «Архитектура компьютера» он принимает «Компьютер» в качестве ключа и «Архитектура» в качестве значения. Я бы хотел, чтобы компьютерная архитектура была ключевой. - person learningtocode; 03.10.2012
comment
@learningtocode, вы также хотите, чтобы Name Eric Brine принимало Name в качестве ключа и Eric Brine в качестве значения? - person ikegami; 03.10.2012
comment
Это то, что решение, которое вы предложили, делает сейчас. Если Эрик и Бэйн находятся в разных строках от имени, то да, но если они в одной строке, все это должно войти в ключ. Спасибо, ikegami, это решает большую часть проблемы, может быть, я смогу исправить остальное, если лучше пойму ваше выражение. Выложу, что найду. - person learningtocode; 03.10.2012
comment
Не уверен, что это да или нет. Обновлено. - person ikegami; 03.10.2012
comment
Я имел в виду нет. Но я бы хотел, чтобы строка Computer Architecture интерпретировалась как key=Computer Architecture.value, а двоеточие было пустым. - person learningtocode; 03.10.2012

Попробуйте указать ключевую часть в виде двух битов текста с необязательным пробелом между ними;

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

Это должно захватывать как ключи из одного слова, так и ключи из двух слов.

person Junuxx    schedule 03.10.2012
comment
Также ключ может состоять более чем из двух слов. - person learningtocode; 03.10.2012
comment
И его, и мой код принимают более одного слова для ключа, когда используется : или =, но невозможно принять ключи с пробелами для третьего синтаксиса (потому что нет способа узнать, что если abc def ghi означает abc: def ghi или abc def: ghi). - person ikegami; 03.10.2012