簡體   English   中英

以各種格式呈現時推薦的解析日期的方法

[英]Recommended way of parsing dates when presented in a variety of formats

我有一組日期作為用戶在一段時間內輸入的字符串。 由於這些來自人類,幾乎沒有驗證,因此為日期輸入的格式差異很大。 以下是一些示例(前導數字僅供參考):

  1. 1897年8月20日
  2. 1991年5月31日,即1909年6月1日
  3. 2007年1月29日
  4. 1954年5月10日,11日,12日
  5. 2006年3月26日,27日,28日,29日和30日
  6. 11月27日,28日,29日,30日,2006年12月1日

我想在c#中解析這些日期以最終得到DateTime對象集,每天有一個DateTime對象。 因此上面的(1)將導致2個DateTime對象,並且(6)將導致5個DateTime對象。

我建議處理它們進行泛化(基本上刪除數字和名稱並使它們成為占位符)然后按類似格式分組,這樣你就可以使用一個樣本組。

例如, 20th, 21st August 1987然后成為[number][postfix], [number][postfix] [month] [year] (假設<number><st|th|rd|nd>被識別為數字和后綴和月份是顯而易見的,多年是4位數字)。

從那里,您可以找到有多少人遵循該模式,然后找出需要匹配的獨特模式的數量。 那么你至少可以有一個樣本來測試你想要使用的任何算法(正則表達式可能是你最好的選擇,因為它可以檢測重復的模式( #th[, $th[, ...]] )和日名。)


看起來你可能想用模式分解它(假設你提供了什么)。 因此,例如首先打破年度信息:

(.*?)([0-9]{4})(?:, |$)

然后你需要把它分解成幾個月

(.*?)(January|February|...)(?:, |$)

然后你想要那個月內包含的日子:

(?:([0-9]{1,2})(?:st|nd|rd|th)(?:, )?)*(?:, |$)

然后是關於編譯信息。 但同樣,那只是使用你面前的東西。 最終,您需要知道您正在使用哪種數據以及如何解決這些數據。


更新

所以,我忍不住試着自己解決這個問題。 我想說我正在使用的方法有些准確,而且我沒有把煙熏在你的裙子上。 話雖如此,這就是我想出的。 請注意,這是在PHP中出於以下幾個原因:

  1. PHP更容易掌握
  2. 我覺得如果這一個可行的解決方案,你應該努力將其移植。 :眉開眼笑:

無論如何,這里是源和演示輸出。 請享用。

<?php
  $samples = array(
    '20th, 21st August 1897',
    '31st May, 1st June 1909',
    '29th January 2007',
    '10th, 11th, 12th May 1954',
    '26th, 27th, 28th, 29th, 30th March 2006',
    '27th, 28th, 29th, 30th November, 1st December 2006',
    '30th, 31st, December 2010, 1st, 2nd January 2011'
  );

  //header('Content-Type: text/plain');

  $months = array('january','february','march','april','may','june','july','august','september','october','november','december');

  foreach ($samples as $sample)
  {
    $dates = array();

    // find yearly information first
    $yearly = null;
    if (preg_match_all('/(?:^|\s)(?<month>.*?)\s?(?<year>[0-9]{4})(?:$|,)/',$sample,$yearly))
    {//var_dump($yearly);
      for ($y = 0; $y < count($yearly[0]); $y++)
      {
        $year = $yearly['year'][$y];
        //echo "year: {$year}\r\n";

        $monthly = null;
        if (preg_match_all('/(?<days>(?:(?:^|\s)[0-9]{1,2}(?:st|nd|rd|th),?)*)\s?(?<month>'.implode('|',$months).')$/i',$yearly['month'][$y],$monthly))
        {//var_dump($monthly);
          for ($m = 0; $m < count($monthly[0]); $m++)
          {
            $month = $monthly['month'][$m];
            //echo "month: {$month}\r\n";

            $daily = null;
            if (preg_match_all('/(?:^|\s)(?<day>[0-9]{1,2})(?:st|nd|rd|th)(?:,|$)/i',$monthly['days'][$m],$daily))
            {//var_dump($daily);
              for ($d = 0; $d < count($daily[0]); $d++)
              {
                $day = $daily['day'][$d];
                //echo "day: {$day}\r\n";

                $dates[] = sprintf("%d-%d-%d", array_search(strtolower($month),$months)+1, $day, $year);
              }
            }
          }
        }
        $data = $yearly[1];
      }
    }

    echo "<p><b>{$sample}</b> was parsed to include:</p><ul>\r\n";
    foreach ($dates as $date)
      echo "<li>{$date}</li>\r\n";
    echo "</ul>\r\n";
  }
?>

2097年8月21日解析為包括:

  • 1897年8月20日
  • 1897年8月21日

解剖1909年5月31日,包括:

  • 1909年6月1日

20071月29日被解析為包括:

  • 2007年1月29日

19545月10日,11日,12日被解析為包括:

  • 1954年5月10日
  • 1954年5月11日
  • 1954年5月12日

20063月26日,27日,28日,29日,30日被解析為包括:

  • 2006年3月26日
  • 2006年3月27日
  • 2006年3月28日
  • 2006年3月29日
  • 2006年3月30日

11月27日,28日,29日,30日,200612月1日被解析為包括:

  • 2006年12月1日

2010年12月30日,31日,20111月1日,2日解析為包括:

  • 二〇一〇年十二月三十〇日
  • 2010年12月31日
  • 2011年1月1日
  • 2011年1月2日

為了證明我的袖子沒有, http://www.ideone.com/GGMaH

我想到了更多關於這一點,解決方案變得明顯。 對字符串進行標記並以相反的順序解析標記。 這將檢索年份,然后是月份和天。 這是我的解決方案:

// **** Start definition of the class bcdb_Globals ****
public static class MyGlobals
{
    static Dictionary<string, int> _month2Int = new Dictionary<string, int>
    {
        {"January", 1},
        {"February", 2},
        {"March", 3},
        {"April", 4},
        {"May", 5},
        {"June", 6},
        {"July", 7},
        {"August", 8},
        {"September", 9},
        {"October", 10},
        {"November", 11},
        {"December", 12}
    };
    static public int GetMonthAsInt(string month)
    {
        return( _month2Int[month] );
    }
}


public class MyClass
{
    static char[] gDateSeparators = new char[2] { ',', ' ' };

    static Regex gDayRegex = new Regex("[0-9][0-9]?(st|nd|rd|th)");
    static Regex gMonthRegex = new Regex("January|February|March|April|May|June|July|August|September|October|November|December");
    static Regex gYearRegex = new Regex("[0-9]{4}");

    public void ParseMatchDate(string matchDate)
    {
        Stack matchDateTimes = new Stack();
        string[] tokens = matchDate.Split(gDateSeparators,StringSplitOptions.RemoveEmptyEntries);
        int curYear = int.MinValue;
        int curMonth = int.MinValue;
        int curDay = int.MinValue;

        for (int pos = tokens.Length-1; pos >= 0; --pos)
        {
            if (gYearRegex.IsMatch(tokens[pos]))
            {
                curYear = int.Parse(tokens[pos]);
            }
            else if (gMonthRegex.IsMatch(tokens[pos]))
            {
                curMonth = MyGlobals.GetMonthAsInt(tokens[pos]);
            }
            else if (gDayRegex.IsMatch(tokens[pos]))
            {
                string tok = tokens[pos];
                curDay = int.Parse(tok.Substring(0,(tok.Length-2)));
                // Dates are in reverse order, so using a stack means we'll pull em off in the correct order
                matchDateTimes.Push(new DateTime(curYear, curMonth, curDay));
            }
        }

        // Now get the datetimes
        while (matchDateTimes.Count > 0)
        {
            // Do something with dates here
        }
    }

}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM