2

ローカル Web サイトをスクレイピングして今後のイベントのデータベースを作成するアプリに取り組んでおり、正規表現を使用してできるだけ多くの形式の日付を取得しようとしています。

次の文の断片を考えてみてください。

  • 「2013 年 2 月 2 日土曜日のセミナーの焦点は [...]」
  • "バレンタイン スペシャル @ ラディソン、2 月 14 日"
  • 「2 月 15 日の金曜日に、ハリウッドをテーマにした特別な [...]」
  • 「2月8日(金)こども遊びシンポジウム」
  • 「旧暦3月9日~11日にクラフトワークショップを開催 […]」

これらをスキャンして、できるだけ多くの日付をキャッチできるようにしたいと考えています。現時点では、おそらく欠陥のある方法でこれを行っています (私は正規表現が得意ではありません)。次のように、いくつかの正規表現ステートメントを次々と実行します。

/([0-9]+?)(st|nd|rd|th) (of)? (Jan|Feb|Mar|etc)/i
/([0-9]+?)(st|nd|rd|th) (of)? (January|February|March|Etcetera)/i
/(Jan|Feb|Mar|etc) ([0-9]+?)(st|nd|rd|th)/i
/(January|February|March|Etcetera) ([0-9]+?)(st|nd|rd|th)/i

これらすべてを 1 つの巨大な正規表現ステートメントにマージすることもできますが、おそらくサードパーティのライブラリなど、php でこれを行うためのよりクリーンな方法が必要なようです。

編集: 上記の正規表現にはエラーがある可能性があります。これは単なる例です。

4

2 に答える 2

4

以下を使用して、テキストから日付を抽出する関数を作成しましたstrtotime()

function parse_date_tokens($tokens) {
  # only try to extract a date if we have 2 or more tokens
  if(!is_array($tokens) || count($tokens) < 2) return false;
  return strtotime(implode(" ", $tokens));
}

function extract_dates($text) {
  static $patterns = Array(
    '/^[0-9]+(st|nd|rd|th|)?$/i', # day
    '/^(Jan(uary)?|Feb(ruary)?|Mar(ch)?|etc)$/i', # month
    '/^20[0-9]{2}$/', # year
    '/^of$/' #words
  );
  # defines which of the above patterns aren't actually part of a date
  static $drop_patterns = Array(
    false,
    false,
    false,
    true
  );
  $tokens = Array();
  $result = Array();
  $text = str_word_count($text, 1, '0123456789'); # get all words in text

  # iterate words and search for matching patterns
  foreach($text as $word) {
    $found = false;
    foreach($patterns as $key => $pattern) {
      if(preg_match($pattern, $word)) {
        if(!$drop_patterns[$key]) {
          $tokens[] = $word;
        }
        $found = true;
        break;
      }
    }

    if(!$found) {
      $result[] = parse_date_tokens($tokens);
      $tokens = Array();
    }
  }
  $result[] = parse_date_tokens($tokens);

  return array_filter($result);
}

# test
$texts = Array(
  "The focus of the seminar, on Saturday 2nd February 2013 will be [...]",
  "Valentines Special @ The Radisson, Feb 14th",
  "On Friday the 15th of February, a special Hollywood themed [...]",
  "Symposium on Childhood Play on Friday, February 8th",
  "Hosting a craft workshop March 9th - 11th in the old [...]"
);

$dates = extract_dates(implode(" ", $texts));
echo "Dates: \n";
foreach($dates as $date) {
  echo "  " . date('d.m.Y H:i:s', $date) . "\n";
}

これは以下を出力します:

Dates: 
  02.02.2013 00:00:00
  14.02.2013 00:00:00
  15.02.2013 00:00:00
  08.02.2013 00:00:00
  09.03.2013 00:00:00

このソリューションは完璧ではない可能性があり、確かに欠陥がありますが、問題に対する非常に単純なソリューションです。

于 2013-01-29T14:59:02.470 に答える
1

この種の潜在的に複雑な正規表現については、個別に単体テスト、保守、および進化できる単純な部分に分解する傾向があります。

私はRELを使用します。これは、正規表現の一部を再構築して再利用できるようにする DSL (Scala 内) です。このようにして、これらの日付マッチャーのように正規表現を定義し、各部分で単体テストを行うことができます。

また、ユニット/仕様テストは、このビットの正規表現のドキュメントとしても機能し、一致するものと一致しないものを示します (これは正規表現で重要になる傾向があります)。

REL の次のバージョン (0.3) では、たとえば PCRE (つまり PHP) フレーバーで正規表現を直接エクスポートして、独立して使用できるようになります。今のところ、github リポジトリに実装されているのは JavaScript と .NET の翻訳のみです。最新の (まだ公にコミットされていない) スナップショットを使用すると、英語の英数字の日付正規表現の PCRE フレーバーは次のようになります。

/(?:(?:(?<!\d)(?<a_d1>(?>(?:(?:[23]?1)st|(?:2?2)nd|(?:2?3)rd|(?:[12]?[4-9]|[123]0)th)\b|0[1-9]|[12][0-9]|3[01]|[1-9]|[12][0-9]|3[01]))(?: ?+(?:of )?+))(?>(?<a_m1>jan(?>uary|\.)?|feb(?>ruary|r?\.?)?|mar(?>ch|\.)?|apr(?>il|\.)?|may|jun(?>e|\.)?|jul(?>y|\.)?|aug(?>ust|\.)?|sep(?>tember|t?\.?)?|oct(?>ober|\.)?|nov(?>ember|\.)?|dec(?>ember|\.)?))|(?:\b(?>(?<a_m2>jan(?>uary|\.)?|feb(?>ruary|r?\.?)?|mar(?>ch|\.)?|apr(?>il|\.)?|may|jun(?>e|\.)?|jul(?>y|\.)?|aug(?>ust|\.)?|sep(?>tember|t?\.?)?|oct(?>ober|\.)?|nov(?>ember|\.)?|dec(?>ember|\.)?)))(?:(?:(?: ?+)(?<a_d2>(?>(?:(?:[23]?1)st|(?:2?2)nd|(?:2?3)rd|(?:[12]?[4-9]|[123]0)th)\b|0[1-9]|[12][0-9]|3[01]|[1-9]|[12][0-9]|3[01]))(?!\d))?))(?:(?:,?+)(?:(?:(?: ?)(?<a_y>(?:1[7-9]|20)\d\d|'?+\d\d))(?!\d))|(?<=\b|\.))/i

fr.splayce.rel.matchers.en.Date.ALPHAusing を使用して取得しますPCREFlavor(まだ GitHub リポジトリにはありません)。febアルファベット形式 ( 、feb.またはfebruary) で表される月がある場合にのみ一致します。正規表現は、より複雑….Date.ALLな数値形式にも一致します。2/21/2013

また、この特定の正規表現はあなたの例と一致しますが、ニーズに合わせてまだ少し制限されている場合があります:

  • 平日は含みません
  • 日付範囲と一致しません (一致のみMarch 9th)
  • 年が最初とは一致しません。2013, jan. 14th
于 2013-01-29T14:39:52.410 に答える