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)
我希望我的问题清楚地暴露出来。
我无法重现你的问题。
我唯一能猜到的是你没有用正确的编码打开文件。
或者更糟的是,您从 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_word和t_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)
您的解析问题似乎在某种程度上仅限于您发布的示例文本,因为您提供的希伯来语文本片段中似乎嵌入了正斜杠(“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)
哈。