Заставить длину квантификатора регулярного выражения зависеть от предыдущей группы захвата

Я надеюсь использовать регулярное выражение для анализа строк, начинающихся с целого числа n. После пробела идет n символов, после которых может быть еще текст. Я надеюсь захватить n и последующие символы n. На эти n символов ограничений нет. Другими словами, 5 hello world должен совпадать с группами захвата 5 и hello.

Я попробовал это регулярное выражение, но оно не скомпилировалось, потому что его структура зависит от ввода: (\d+) .{\1}.

Есть ли способ заставить компилятор регулярных выражений делать то, что я хочу, или мне нужно самому разобрать это?

Я использую ящик Rust regex, если это имеет значение. И если это невозможно с regex, возможно ли это с другим, более сложным механизмом регулярных выражений?

Спасибо!


person Jack    schedule 03.07.2020    source источник
comment
Вам нужно преобразовать строку, такую ​​как "5", в целое число, которое она представляет, 5. Вы не можете сделать это с помощью регулярного выражения. Каково максимальное значение n?   -  person Cary Swoveland    schedule 03.07.2020
comment
n — это 32-битное целое число, но его можно изменить ради удобного решения.   -  person Jack    schedule 03.07.2020
comment
Вы можете простое регулярное выражение получить n, затем в коде преобразовать его в целое число и затем извлечь интересующие строки.   -  person Cary Swoveland    schedule 03.07.2020


Ответы (2)


Как сказал @Cary Swoveland в комментариях, это невозможно в регулярном выражении за один шаг без жесткого кодирования различных возможных длин.

Однако не так уж сложно взять подстроку совпавшей строки с длиной из совпадающей цифры:

use regex::Regex;
    
fn main() {
    let re = Regex::new(r"(\d+) (.+)").unwrap();
    let test_str = "5 hello world";

    for cap in re.captures_iter(test_str) {
        let length: usize = cap[1].parse().unwrap_or(0);
        let short_match: String = cap[2].chars().take(length).collect();

        println!("{}", short_match); // hello
    }
}

Если вы знаете, что будете иметь дело только с символами ASCII (без Unicode, знаков ударения и т. д.), вы можете использовать более простой синтаксис фрагмента let short_match = &cap[2][..length];.

person jdaz    schedule 03.07.2020
comment
Вы можете несколько упростить свой код, используя индексацию захвата. например, cap[1].parse().unwrap_or(0). - person BurntSushi5; 03.07.2020

Если Perl подходит вам, попробуйте:

perl -e '
$str = "5 abcdefgh";
$str =~ /(\d+) ((??{".{".($^N)."}"}))/;
print "1st capture group = $1\n";
print "2nd capture group = $2\n";
print "whole capture group = $&\n";
'

Выход:

1st capture group = 5
2nd capture group = abcde
whole capture group = 5 abcde

[Объяснение]

  • Если блок (??{...}) встречается в регулярном выражении, его содержимое расширяется как код Perl на лету.
  • Специальная переменная $^N относится к last captured group и расширяется как 5 в случае.
  • Затем код (??{".{".($^N)."}"}) оценивается как .{5}, который представляет собой точку, за которой следует квантификатор.
person tshiono    schedule 03.07.2020