1

50,000 ワードのマークダウン ドキュメントで 400 程度のキーワードをハイパーリンクしようとしています。

これは、Perl の「ビルド チェーン」のいくつかのステップの 1 つであるため、Perl でもハイパーリンクを実現することが理想的です。

すべてのキーワードを含む別のファイルがあり、それぞれを次のように置き換える必要があるマークダウンフラグメントにマッピングします。

keyword::(keyword)[#heading-to-jump-to]

上記の例は、ソース マークダウン ドキュメントで「キーワード」が発生する場合は常に、マークダウン フラグメント「(キーワード)[#heading-to-jump-to]」に置き換える必要があることを意味します。

他のキーワードの部分文字列として出現するキーワード、複数形/単数形、あいまいなキーワードを無視すると、かなり単純です。しかし、当然のことながら、2 つの追加の制約があります。

次のキーワードのインスタンスのみを一致させる必要があります。

  • 行頭ではない #
  • ジャンプする見出しのすぐ下ではない

これらの平易な英語の意味は、次のとおりです。どの見出しのキーワードとも一致せず、リンク先の見出しの下にあるキーワードを置き換えないでください。

私の Perl スクリプトは $keyword::$link のペアを読み取り、ペアごとにそれらを正規表現に置き換え、ドキュメントをその正規表現で検索/置換します。

Regex Buddy の JGSoft 正規表現実装を使用して、マッチングを行う正規表現を作成しました (これまでに手動でテストした場合)。次のようになります。

Frog::(Frog)[#the-frog)
-->    
([Ff]rog'?s?'?)(?=[\.!\?,;: ])(?<!#+ [\w ]*[Ff]rogs?)(?<!#+ the-frog)(?<!#+ the-frog[^#]*)

これに関する問題 (または、おそらく問題) は Perl でサポートされていない可変長のルックバックを使用していることです。そのため、ドキュメント全体でこの正規表現をテストして、実際に機能するかどうかを確認することさえできません。

可変長のルックバックを回避する方法に関する他の投稿をたくさん読んだことがありますが、特定のケースではうまくいかないようです。常駐の正規表現ウィザードのいずれかが、Perl で実行されるより適切な正規表現を支援できますか?

4

2 に答える 2

2

これは恐ろしい正規表現の 1 つです。私はそれを維持することにこだわる貧しい吸盤になりたくありません. また、置換テンプレートからどのように生成しましたか?

かなり単純なものを提案します。ハッシュを使用して置換を保存し、単語境界を使用して部分一致を防ぎ、/i修飾子を使用して大文字と小文字を区別せずに一致させ、通常のループ ロジックを使用してコメント行での置換を回避します。

use strict;
use warnings;

my @kw = "keyword::(keyword)[#heading-to-jump-to]";
my %rep = map { /([^:]+)::(.+)/ } @kw;
while (<DATA>) {
    next if /^#/;
    for my $kw (keys %rep) {
        s/\b\Q$kw\E\b/$rep{$kw}/ig;
    }
} continue {
    print;
}

__DATA__
This is a text with keywords. Only the keyword 'keyword' should be replaced.
# Dont replace keyword when in a comment

出力:

This is a text with keywords. Only the (keyword)[#heading-to-jump-to] '(keyword)
[#heading-to-jump-to]' should be replaced.
# Dont replace keyword when in a comment

説明:

  • map各 keyword::replacement 文字列に対して 2 つの要素のリストを返すステートメントを使用して、置換キーワードのハッシュを作成します。
  • で始まる行では#、直接スキップしてprint
  • ハッシュ内の各キーワードに対して、各行/gで大文字と小文字を区別しないグローバルな/i置換を実行します。\b部分一致を防ぐために単語境界を使用し、メタ文字を で引用し\Q ... \Eます。そのキーワードのハッシュ値に置き換えます。

すべての言語処理と同様に、これにはいくつかの警告と処理が必要な特殊なケースがあります。たとえば、単語境界は で置き換えfooられfoo-barます。どの見出しの下で何を置き換えないかを制御する方法については、まず見出しの識別方法を教えてください。

アップデート:

私の理解が正しければ、独自の見出しを持つ段落内のキーワードをスキップするという意味は、次のようなものです。

#heading-to-jump-to
Here is 'keyword' not replaced

文字列を調べて、置換リストから#heading-to-jump-to削除します。keyword

キーが見出し参照であるルックアップ ハッシュを使用し、それを最初のハッシュの生成と組み合わせることができます。ただし、この場合、リンクごとに複数のキーワードを使用できるのではないかとfoo心配になりbarました。#foobar#foobarfoobar

my %rep;
my %heading;

for my $str (@kw) {
    chomp $str;
    my ($kw, $rep) = split /::/, $str, 2;  # split into 2 fields
    $rep{$kw} = $rep;
    my ($heading) = $rep =~ /\[([^]]+)\]/;
    push @{ $heading{$heading} }, $kw;
}

そして、単に行をスキップする代わりに、次のnextようにします

my @kws = keys %rep;   # default list
while (<DATA>) {
    if (/^(#.+)/) {    # inside heading
        my %exclude = map { $_ => 1 } @{ $heading{$1} };
        @kws = grep { ! $exclude{$_} } @kws;
    } else {
        # not in a heading
        # ...
    }
}

これは原則のデモンストレーションに過ぎず、実際のコードとして意図されたものではないことに注意してください。ご覧のとおり、ここで難しいのは、制限されたリストをいつリセットし、@kwsいつ使用するかを知ることです。私はあなたのデータを知らないので、あなたはそれらの決定を下す必要があります。

于 2013-09-09T12:44:35.707 に答える
1

私が見ているように、あなたのプログラムには 3 つの状態があります。

  1. 見出しで。
  2. 見出しの直後の段落。
  3. 他の段落で。

これはおおよそ正規言語であるため、正規表現で解析できます。しかし、テキスト全体で 400 回のパスが必要になることを考えると、なぜそれを行う必要があるのでしょうか?

ファイルを段落の配列に分割する方が本当に簡単かもしれません。見出しがヒットすると、そこを指すすべてのリンクが作成されます。次に、次の段落で、禁止されているものを除くすべてのキーワードを置き換えます。例えば:

my %substitutions = ...;
my $kw_regex = ...;
my %forbidden; # holds state

local $/ = ""; # paragraph mode
while (<>) {
  if (/^#/) {
    # it's a headline
    @forbidden{ slugify($_) } = ();  # extract forbidden link(s)
  } else {
    # a paragraph
    s{($kw_regex)}{
      my $keyword = $1;
      my $link = $substitutions{lc $keyword};
      exists $forbidden{$link} ? $keyword : "($keyword)[$link]";
    }eg;
    %forbidden = (); # forbidden links only in 1st paragraph after headline
  }
  print;
}

見出しが空の行によって段落から分離されていることが保証されていない場合、段落モードは機能せず、独自のロールを作成する必要があります。

正規表現はすばらしいものですが、常に適切なツールとは限りません。

于 2013-09-09T12:41:15.203 に答える