raku 语法的标记不会命中文档的第一次出现,但会命中以下类似的出现

jea*_*ban 7 xml grammar hebrew raku

我想用希伯来语处理整个 Tanach 文件。为此,我选择了 Raku 语言,因为它的一些特性(语法和 unicode 支持)。

因此,我定义了一些标记来选择相关数据。

grammar HEB {
        token TOP {'<hebrewname>'<t_word>'</hebrewname>'}
        token t_word {<graph>+}
};

grammar CHA {
        token TOP {'<c n="'<t_number>'">'}
        token t_number {\d+}
};

grammar VER {
        token TOP {'<v n="'<t_number>'">'}
        token t_number {\d+}
};

grammar WOR {
        token TOP {'<w>'<t_word>'</w>'}
        token t_word {<graph>+}
};
Run Code Online (Sandbox Code Playgroud)

在这里,文档的一小部分(XML 格式的 Tanach)足以说明问题:

<names> <name>Genesis</name> <abbrev>Gen</abbrev> <number>1</number> <filename>Genesis</filename> <hebrewname>??????</hebrewname> </names> <c n="1"> <v n="1"> <w>???/?????????</w> <w>???????</w> <w>?????????</w> <w>????</w> <w>??/??????????</w> <w>??/????</w> <w>??/???????</w> </v> <v n="2"> <w>??/??/??????</w> <w>????????</w> <w>???????</w> <w>??/??????</w> <w>??/????????</w> <w>????</w> <w>???????</w> <w>???????</w> <w>??/??????</w> <w>?????????</w> <w>??????????</w> <w>????</w> <w>???????</w> <w>??/????????</w> </v>

问题是代码无法识别前两个单词 ( <w>???/?????????</w> <w>???????</w>) 但似乎可以很好地处理以下单词......有人可以向我解释什么是错的吗?

主循环是:

for $file_in.lines -> $line {
    $memline = $line.trim;

    if HEB.parse($memline) {
          say "hebrew name of book is "~ $/<t_word>;
          next;
    }
    if CHA.parse($memline) {
        say "chapitre number is "~ $/<t_number>;
        next;
    }
    if VER.parse($memline) {
        say "verse number is "~ $/<t_number>;
        next;
    }
    if WOR.parse($memline) {
        $computed_word_value = 0;
        say "word is "~ $/<t_word>;
        $file_out.print("$/<t_word>");
        say "numbers of graphemes of word is "~ $/<t_word>.chars;
        @exploded_word = $/<t_word>.comb;
        for @exploded_word {
                say $_.uniname;
        };
        next;
    }
    say "not processed";
}
Run Code Online (Sandbox Code Playgroud)

输出文件 :

请注意,在verse number 为 1 之后,不会处理前 2 个单词。不要专注于扭曲的希伯来语(Windows 控制台)!

not processed
not processed
not processed
not processed
not processed
hebrew name of book is ?‘?¨??©?™?×
not processed
chapitre number is 1
verse number is 1
not processed
not processed
word is ??±?œ?¹?”?´?‘?™?
numbers of graphemes of word is 5
HEBREW LETTER ALEF
HEBREW LETTER LAMED
HEBREW LETTER HE
HEBREW LETTER YOD
HEBREW LETTER FINAL MEM
word is ??µ?¥?×
numbers of graphemes of word is 2
HEBREW LETTER ALEF
HEBREW LETTER TAV
not processed
word is ?•?°/??µ?¥?×
numbers of graphemes of word is 4
HEBREW LETTER VAV
SOLIDUS
Run Code Online (Sandbox Code Playgroud)

我希望我的问题清楚地暴露出来。

Bra*_*ert 7

我无法重现你的问题。
我唯一能猜到的是你没有用正确的编码打开文件。

或者更糟的是,您从 STDIN 获取文件并且没有选择正确的代码页。(这是有道理的,因为你的输出也是 mojibake。)
Rakudo 并没有真正做代码页,所以如果你没有将你的环境设置为 utf8,你必须更改$*STDIN(and $*STDOUT)的编码以匹配它是什么。


我现在要假装你在 CodeReview.StackExchange.com 上发帖了。

首先我不知道你为什么要为这么小的东西创建一个完整的语法,这可以用简单的正则表达式轻松完成。

my token HEB {
  '<hebrewname>'
  $<t_word> = [<.graph>+]
  '</hebrewname>'
}
my token CHA {
 '<c n="' $<t_number> = [\d+] '">'
}
my token VER {
  '<v n="' $<t_number> = [\d+] '">'
}
my token WOR {
  '<w>' $<t_word> = [<.graph>+] '</w>'
}
Run Code Online (Sandbox Code Playgroud)

老实说,这仍然比您似乎需要的要多,因为您每个正则表达式只处理一个元素。

这也忽略了我真的不喜欢你给元素命名t_wordt_number。这是毫无意义的,因为它们在 内部$/,并且 Grammar 也没有任何类似命名的方法,因此它们不可能干扰任何其他命名空间。如果你必须给他们起名字,就给他们起描述性的名字。

您可以限制$/为仅对您关心的部分进行字符串化<(…)>。(它在这里有效,因为您只捕获一件事。)

<(意味着忽略之前的一切,)>意味着忽略之后的一切。

my token HEB {
  '<hebrewname>'
  <( <.graph>+ )> # $/ will contain only what <.graph>+ matches
  '</hebrewname>'
}
my token CHA {
 '<c n="' <( \d+ )> '">'
}
my token VER {
  '<v n="' <( \d+ )> '">'
}
my token WOR {
  '<w>' <( <.graph>+ )> '</w>'
}
Run Code Online (Sandbox Code Playgroud)

您正在解析它,好像它只是一个面向行的文件。
这确实有一定的意义,因为它被格式化为一个,这会导致更少的内存使用。

为此使用命名的正则表达式,更不用说整个语法有点矫枉过正了。当这种简单的匹配不是真的需要时,它还可以分离逻辑。

以下是我将如何以面向行的方式解析该文件:

my $in-names = False;
my %names;
my @chapters;
my @verses;
my @current-verse;

for $file_in.lines {
  when /'<names>' / { $in-names = True  }
  when /'</names>'/ { $in-names = False }

  # chapter
  when /'<c n="' <( \d+ )> '">'/ {
    @verses := @chapters[ +$/ - 1 ] //= [];
  }
  when /'</c>'/ {
    # finalize this chapter
    # for example print out statistics
    # (only needed if you don't want `default` to catch it)
  }

  # verse
  when /'<v n="' <( \d+ )> '">'/ {
    @current-verse := @verses[ +$/ - 1 ] //= [];
  }
  when /'</v>'/ {
    # finalize this verse
  }

  # word
  when /'<w>' <( <.graph>+ )> '</w>'/ {
    push @current-verse, ~$/;
  }

  # name tags
  # must be after more specific regexes
  when /'<' <tag=.ident> '>' $<value> = [<.ident>|\d+] {} "</$<tag>>"/ {
    if $in-names {
      %names{~$<tag>} = ~$<value>
    } else {
      note "not handling $<tag> => $<value> outside of <names>"
    }
  }

  default { note "unexpected text '$_'" }
}
Run Code Online (Sandbox Code Playgroud)

请注意,when这使得您不必这样做next
由于我们只是使用$_代替$line,所以我们可以直接使用正则表达式作为这些when语句的条件。

我不想使用^or$所以没有必要使用trimor 或使用^\s*and \s*$
它确实使它变得更加脆弱,因此如果出现问题,您可能需要更改它。

如果你真的只想像你正在做的那样做简单的线处理,我相信你可以改变上面的内容以满足你的需要。

我想让这对将来遇到这个问题的人更有用。所以我从文件中创建了一个数据结构,而不是按照你在做什么。


真的,如果我.parse()一次性访问整个文件,我可能只会达到语法。

这就是这样一个语法的样子。

grammar Book {
  rule TOP {
    <names>
    <chapter> +
    # note that there needs to be a space between <chapter> and +
    # so that whitespace can be between <c…>…</c> elements
  }

  rule names {
    '<names>'  ~  '</names>'
    <name> +
  }

  token name {
    '<' <tag=.ident> '>'
    $<name> = [<.ident>|\d+]
    {}
    "</$<tag>>"
  }

  rule chapter {
    # note space before ]
    ['<c n="' <number> '">' ]  ~  '</c>'
    <verse> +
  }
  rule verse {
    ['<v n="' <number> '">' ]  ~  '</v>'
    <word> +
  }

  token number { \d+ }
  token word { '<w>' <( <.graph>+ )> '</w>' }
}
Run Code Online (Sandbox Code Playgroud)

像你一样做类似的处理

class Line-Actions {
  has IO::Handle:D $.file-out is required;
  has $!number-type is default<chapter>;

  method name ($/) {
    if $<tag> eq 'hebrewname' {
      say "hebrew name of book is $<name>";
    }
  }

  # note that .chapter and .verse will run at the end
  # of parsing them, which is too late for when .word is processed
  # so we do it in .number instead
  method number ($/) {
    say "$!number-type number is $/";
    $!number-type = 'verse';
  }
  method chapter ($/) {
    # reset to default of "chapter"
    # as the next .number will be for the next chapter
    $!number-type = Nil;
  }

  method word ($/) {
    say "word is $/";
    $!file-out.print(~$/);
    say "number of graphemes in word is $/.chars()";
    .say for "$/".comb.map: *.uninames.join(', ');
  }
}


Book.parsefile(
  $filename,
  actions => Line-Actions.new( 'outfile.txt'.IO.open(:w) )
);
Run Code Online (Sandbox Code Playgroud)

  • @jeanpauldavidtzaban 语法只是一堆放在一起的正则表达式。它所做的唯一一件事就是将复杂性分散到一堆正则表达式中,而不是单个正则表达式中。这类似于创建一个类来处理复杂性。如果您的正则表达式代码并不复杂,那么转换为语法实际上会使其变得更加复杂。它实际上并没有多大作用使其变得更紧凑。请注意,当 `VER` 是单个正则表达式 `/'&lt;vn="'&lt;(\d+)&gt;'"&gt;'/` 时,它会变得多么短。 (2认同)

jub*_*us1 5

您的解析问题似乎在某种程度上仅限于您发布的示例文本,因为您提供的希伯来语文本片段中似乎嵌入了正斜杠(“solidus”字符)。

您提供的脚本很容易修复,我WOR在您的 Raku 脚本中重新编写了令牌以仅选择<:Script<Hebrew>>unicode。虽然这可能有助于处理杂散/嵌入的“solidus”字符(和其他非希伯来语字符),但大概您可以重新编写脚本以更快地解析。这是脚本:

grammar HEB {
        token TOP {'<hebrewname>'<t_word>'</hebrewname>'}
        token t_word {<graph>+}
};

grammar CHA {
        token TOP {'<c n="'<t_number>'">'}
        token t_number {\d+}
};

grammar VER {
        token TOP {'<v n="'<t_number>'">'}
        token t_number {\d+}
};

grammar WOR {
        token TOP {'<w>'<t_word>'</w>'}
        token t_word {<:Script<Hebrew>>+}
};

for $*ARGFILES.lines -> $line {
    my $memline = $line.trim;

    if HEB.parse($memline) {
          say "hebrew name of book is "~ $/<t_word>;
          next;
    }
    if CHA.parse($memline) {
        say "chapitre number is "~ $/<t_number>;
        next;
    }
    if VER.parse($memline) {
        say "verse number is "~ $/<t_number>;
        next;
    }
    if WOR.parse($memline) {
        say "word is "~ $/<t_word>;
        say "numbers of graphemes of word is "~ $/<t_word>.chars;
        my @exploded_word = $/<t_word>.comb;
        for @exploded_word {
                say $_.uniname, ": ", $_;
        };
        next;
    }
    say "not processed";
}
Run Code Online (Sandbox Code Playgroud)

从一个新的测试文件开始,我能够XML解析以下文本的124655/126663 行:

http://www.tanach.us/Books/Genesis.xml

以下是第 103-119 行的解析文本(以前给您带来问题的单词):

hebrew name of book is ??????
not processed
chapitre number is 1
verse number is 1
word is ????????????
numbers of graphemes of word is 6
HEBREW LETTER BET: ???
HEBREW LETTER RESH: ??
HEBREW LETTER ALEF: ?
HEBREW LETTER SHIN: ????
HEBREW LETTER YOD: ?
HEBREW LETTER TAV: ?
word is ???????
numbers of graphemes of word is 3
HEBREW LETTER BET: ???
HEBREW LETTER RESH: ???
HEBREW LETTER ALEF: ?
Run Code Online (Sandbox Code Playgroud)

哈。