日付や時間を表す文字列を受理するオートマトン③

どのパターンか判断できるようにしてみた。Ideone.com - vcrHk5 - Online C# Compiler & Debugging Tool

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;

namespace Automaton
{
  class Program
  {
    const string INDENT = "\t";

    static void Main(string[] args)
    {
      DateTimeChecker checker = new DateTimeChecker();

      List<Tuple<string, DateTimeChecker.Formats>> testCase = new List<Tuple<string, DateTimeChecker.Formats>>();
      testCase.Add(Tuple.Create("2018/11/16 12:34:56.789", DateTimeChecker.Formats.yyyyMMddHHmmssfff));
      testCase.Add(Tuple.Create("2018/11/16 12:34:56.78", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("2018/11/16 12:34:56.7", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("2018/11/16 12:34:56.", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("2018/11/16 12:34:56", DateTimeChecker.Formats.yyyyMMddHHmmss));
      testCase.Add(Tuple.Create("2018/11/16 12:34:5", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("2018/11/16 12:34:", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("2018/11/16 12:34", DateTimeChecker.Formats.yyyyMMddHHmm));
      testCase.Add(Tuple.Create("2018/11/16 12:3", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("2018/11/16 12:", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("2018/11/16 12", DateTimeChecker.Formats.yyyyMMddHH));
      testCase.Add(Tuple.Create("2018/11/16 1", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("2018/11/16 ", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("2018/11/16", DateTimeChecker.Formats.yyyyMMdd));
      testCase.Add(Tuple.Create("2018/11/1", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("2018/11/", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("2018/11", DateTimeChecker.Formats.yyyyMM));
      testCase.Add(Tuple.Create("2018/1", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("2018/", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("2018", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("201", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("20", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("2", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("", DateTimeChecker.Formats.None));
      TestDateTime(checker, testCase);
      checker.Reset();
      testCase.Clear();

      testCase.Add(Tuple.Create("18/11/16 12:34:56.789", DateTimeChecker.Formats.yyMMddHHmmssfff));
      testCase.Add(Tuple.Create("18/11/16 12:34:56.78", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("18/11/16 12:34:56.7", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("18/11/16 12:34:56.", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("18/11/16 12:34:56", DateTimeChecker.Formats.yyMMddHHmmss));
      testCase.Add(Tuple.Create("18/11/16 12:34:5", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("18/11/16 12:34:", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("18/11/16 12:34", DateTimeChecker.Formats.yyMMddHHmm));
      testCase.Add(Tuple.Create("18/11/16 12:3", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("18/11/16 12:", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("18/11/16 12", DateTimeChecker.Formats.yyMMddHH));
      testCase.Add(Tuple.Create("18/11/16 1", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("18/11/16 ", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("18/11/16", DateTimeChecker.Formats.yyMMdd));
      testCase.Add(Tuple.Create("18/11/1", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("18/11/", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("18/11", DateTimeChecker.Formats.MMdd));
      testCase.Add(Tuple.Create("18/1", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("18/", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("18", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("1", DateTimeChecker.Formats.None));
      TestDateTime(checker, testCase);
      checker.Reset();
      testCase.Clear();

      testCase.Add(Tuple.Create("11/16 12:34:56.789", DateTimeChecker.Formats.MMddHHmmssfff));
      testCase.Add(Tuple.Create("11/16 12:34:56.78", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("11/16 12:34:56.7", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("11/16 12:34:56.", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("11/16 12:34:56", DateTimeChecker.Formats.MMddHHmmss));
      testCase.Add(Tuple.Create("11/16 12:34:5", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("11/16 12:34:", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("11/16 12:34", DateTimeChecker.Formats.MMddHHmm));
      testCase.Add(Tuple.Create("11/16 12:3", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("11/16 12:", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("11/16 12", DateTimeChecker.Formats.MMddHH));
      testCase.Add(Tuple.Create("11/16 1", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("11/16 ", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("11/16", DateTimeChecker.Formats.MMdd));
      testCase.Add(Tuple.Create("11/1", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("11/", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("11", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("1", DateTimeChecker.Formats.None));
      TestDateTime(checker, testCase);
      checker.Reset();
      testCase.Clear();

      testCase.Add(Tuple.Create("12:34:56.789", DateTimeChecker.Formats.HHmmssfff));
      testCase.Add(Tuple.Create("12:34:56.78", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("12:34:56.7", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("12:34:56.", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("12:34:56", DateTimeChecker.Formats.HHmmss));
      testCase.Add(Tuple.Create("12:34:5", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("12:34:", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("12:34", DateTimeChecker.Formats.HHmm));
      testCase.Add(Tuple.Create("12:3", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("12:", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("12", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("1", DateTimeChecker.Formats.None));
      TestDateTime(checker, testCase);
      checker.Reset();
      testCase.Clear();

      testCase.Add(Tuple.Create("34:56.789", DateTimeChecker.Formats.mmssfff));
      testCase.Add(Tuple.Create("34:56.78", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("34:56.7", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("34:56.", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("34:56", DateTimeChecker.Formats.HHmm));
      testCase.Add(Tuple.Create("34:5", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("34:", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("34", DateTimeChecker.Formats.None));
      testCase.Add(Tuple.Create("3", DateTimeChecker.Formats.None));
      TestDateTime(checker, testCase);
      checker.Reset();
      testCase.Clear();
    }

    static void TestDateTime(DateTimeChecker checker, IList<Tuple<string, DateTimeChecker.Formats>> testCase)
    {
      foreach (Tuple<string, DateTimeChecker.Formats> t in testCase)
      {
        Console.Write("[" + t.Item1 + "] ");
        checker.Reset();
        foreach (char ch in t.Item1)
        {
          checker.MoveNext(ch);
          if (checker.IsError())
          {
            Console.Write(INDENT);
            Console.WriteLine(":ERROR");
            Debug.Assert(checker.Format == t.Item2, "asersion failed!!");
            break;
          }
        }

        if (!checker.IsError())
        {
          if (checker.IsAcceptable())
          {
            Console.Write(INDENT);
            Console.Write(":OK");
            Console.Write(INDENT);
            Console.WriteLine(checker.Format);
            Debug.Assert(checker.Format == t.Item2, t.Item1 + " asersion failed!!");
          }
          else
          {
            Console.Write(INDENT);
            Console.WriteLine(":NG");
            Debug.Assert(checker.Format == t.Item2, "asersion failed!!");
          }
        }
      }
    }
  }

  interface Checker
  {
    void Reset();
    void MoveNext(char ch);
    bool IsAcceptable();
    bool IsError();
    bool IsNextError(char ch);
    string Current();
  }

  class DateTimeChecker : Checker
  {
    /// <summary>formats for date or time</summary>
    public enum Formats
    {
      /// <summary>nether date nor time</summary>
      None,
      /// <summary>yyyy/MM/dd HH:mm:ss.fff</summary>
      yyyyMMddHHmmssfff,
      /// <summary>yyyy/MM/dd HH:mm:ss</summary>
      yyyyMMddHHmmss,
      /// <summary>yyyy/MM/dd HH:mm</summary>
      yyyyMMddHHmm,
      /// <summary>yyyy/MM/dd HH</summary>
      yyyyMMddHH,
      /// <summary>yyyy/MM/dd</summary>
      yyyyMMdd,
      /// <summary>yyyy/MM</summary>
      yyyyMM,
      /// <summary>yy/MM/dd HH:mm:ss.fff</summary>
      yyMMddHHmmssfff,
      /// <summary>yy/MM/dd HH:mm:ss</summary>
      yyMMddHHmmss,
      /// <summary>yy/MM/dd HH:mm</summary>
      yyMMddHHmm,
      /// <summary>yy/MM/dd HH</summary>
      yyMMddHH,
      /// <summary>yy/MM/dd</summary>
      yyMMdd,
      /// <summary>MM/dd HH:mm:ss.fff</summary>
      MMddHHmmssfff,
      /// <summary>MM/dd HH:mm:ss</summary>
      MMddHHmmss,
      /// <summary>MM/dd HH:mm</summary>
      MMddHHmm,
      /// <summary>MM/dd HH</summary>
      MMddHH,
      /// <summary>MM/dd</summary>
      MMdd,
      /// <summary>HH:mm:ss.fff</summary>
      HHmmssfff,
      /// <summary>HH:mm:ss</summary>
      HHmmss,
      /// <summary>HH:mm</summary>
      HHmm,
      /// <summary>mm:ss.fff</summary>
      mmssfff,
    }

    enum States
    {
      Error,
      State00,
      State01,
      State02,
      State03,
      State04,
      State05,
      State06,
      State07,
      State08,
      State09,
      State10,
      State11,
      State12,
      State13,
      State14,
      State15,
      State16,
      State17,
      State18,
      State19,
      State20,
      State21,
      State22,
      State23,
      State24,
      State25,
      State26,
      State27,
      State28,
      State29,
    }

    StringBuilder _sb;
    States _state;
    List<States> _breadcrumb;
    Formats _format;

    public Formats Format
    {
      get { return _format; }
    }

    public DateTimeChecker()
    {
      _sb = new StringBuilder();
      _breadcrumb = new List<States>();
      Reset();
    }

    public void Reset()
    {
      _state = States.State00;
      _sb.Clear();
      _breadcrumb.Clear();
      _format = Formats.None;
    }

    public void MoveNext(char ch)
    {
      _sb.Append(ch);
      States oldState = _state;
      _state = GetNext(ch);
      JudgeFormat();
    }

    void JudgeFormat()
    {
      if (_state == States.State17)
      {
        _format = Formats.yyyyMM;
        _breadcrumb.Add(_state);
      }
      else if (_state == States.State10)
      {
        _format = Formats.MMdd;
        _breadcrumb.Add(_state);
      }
      else if (_state == States.State11)
      {
        _format = Formats.HHmm;
        _breadcrumb.Add(_state);
      }
      else if (_state == States.State22)
      {
        // there are two ways to come here.
        if (_breadcrumb[_breadcrumb.Count - 1] == States.State17)
        {
          _format = Formats.yyyyMMdd;
        }
        else
        {
          _format = Formats.yyMMdd;
        }
        _breadcrumb.Add(_state);
      }
      else if (_state == States.State23)
      {
        // there are three ways to come here.
        if (_breadcrumb[_breadcrumb.Count - 1] == States.State10)
        {
          _format = Formats.MMddHH;
        }
        else if (_breadcrumb[_breadcrumb.Count - 2] == States.State10)
        {
          _format = Formats.yyMMddHH;
        }
        else
        {
          _format = Formats.yyyyMMddHH;
        }
        _breadcrumb.Add(_state);
      }
      else if (_state == States.State29)
      {
        // there are three ways to come here.
        if (_breadcrumb[_breadcrumb.Count - 2] == States.State10)
        {
          _format = Formats.MMddHHmm;
        }
        else if (_breadcrumb[_breadcrumb.Count - 3] == States.State10)
        {
          _format = Formats.yyMMddHHmm;
        }
        else
        {
          _format = Formats.yyyyMMddHHmm;
        }
        _breadcrumb.Add(_state);
      }
      else if (_state == States.State24)
      {
        // there are four ways to come here.
        if (_breadcrumb[_breadcrumb.Count - 1] == States.State11)
        {
          _format = Formats.HHmmss;
        }
        else if (_breadcrumb[_breadcrumb.Count - 3] == States.State10)
        {
          _format = Formats.MMddHHmmss;
        }
        else if (_breadcrumb[_breadcrumb.Count - 4] == States.State10)
        {
          _format = Formats.yyMMddHHmmss;
        }
        else
        {
          _format = Formats.yyyyMMddHHmmss;
        }
        _breadcrumb.Add(_state);
      }
      else if (_state == States.State27)
      {
        // there are five ways to come here.
        if (_breadcrumb[_breadcrumb.Count - 1] == States.State11)
        {
          _format = Formats.mmssfff;
        }
        else if (_breadcrumb[_breadcrumb.Count - 2] == States.State11)
        {
          _format = Formats.HHmmssfff;
        }
        else if (_breadcrumb[_breadcrumb.Count - 4] == States.State10)
        {
          _format = Formats.MMddHHmmssfff;
        }
        else if (_breadcrumb[_breadcrumb.Count - 5] == States.State10)
        {
          _format = Formats.yyMMddHHmmssfff;
        }
        else
        {
          _format = Formats.yyyyMMddHHmmssfff;
        }
        _breadcrumb.Add(_state);
      }
      else
      {
        _format = Formats.None;
      }
    }

    public bool IsAcceptable()
    {
      return
        _state == States.State10
        || _state == States.State11
        || _state == States.State17
        || _state == States.State22
        || _state == States.State23
        || _state == States.State24
        || _state == States.State27
        || _state == States.State29;
    }

    public bool IsError()
    {
      return _state == States.Error;
    }

    public bool IsNextError(char ch)
    {
      States next = GetNext(ch);
      return IsError(next);
    }

    public string Current()
    {
      return _sb.ToString();
    }





    States GetNext(char ch)
    {
      States next = States.Error;
      switch (_state)
      {
        case States.State00:
          next = Transition(ch, States.State01, States.Error, States.Error, States.Error, States.Error);
          break;
        case States.State01:
          next = Transition(ch, States.State02, States.Error, States.Error, States.Error, States.Error);
          break;
        case States.State02:
          next = Transition(ch, States.State03, States.State04, States.Error, States.State05, States.Error);
          break;
        case States.State03:
          next = Transition(ch, States.State06, States.Error, States.Error, States.Error, States.Error);
          break;
        case States.State04:
          next = Transition(ch, States.State07, States.Error, States.Error, States.Error, States.Error);
          break;
        case States.State05:
          next = Transition(ch, States.State08, States.Error, States.Error, States.Error, States.Error);
          break;
        case States.State06:
          next = Transition(ch, States.Error, States.State09, States.Error, States.Error, States.Error);
          break;
        case States.State07:
          next = Transition(ch, States.State10, States.Error, States.Error, States.Error, States.Error);
          break;
        case States.State08:
          next = Transition(ch, States.State11, States.Error, States.Error, States.Error, States.Error);
          break;
        case States.State09:
          next = Transition(ch, States.State12, States.Error, States.Error, States.Error, States.Error);
          break;
        case States.State10:
          next = Transition(ch, States.Error, States.State13, States.State14, States.Error, States.Error);
          break;
        case States.State11:
          next = Transition(ch, States.Error, States.Error, States.Error, States.State15, States.State16);
          break;
        case States.State12:
          next = Transition(ch, States.State17, States.Error, States.Error, States.Error, States.Error);
          break;
        case States.State13:
          next = Transition(ch, States.State18, States.Error, States.Error, States.Error, States.Error);
          break;
        case States.State14:
          next = Transition(ch, States.State19, States.Error, States.Error, States.Error, States.Error);
          break;
        case States.State15:
          next = Transition(ch, States.State20, States.Error, States.Error, States.Error, States.Error);
          break;
        case States.State16:
          next = Transition(ch, States.State21, States.Error, States.Error, States.Error, States.Error);
          break;
        case States.State17:
          next = Transition(ch, States.Error, States.State13, States.Error, States.Error, States.Error);
          break;
        case States.State18:
          next = Transition(ch, States.State22, States.Error, States.Error, States.Error, States.Error);
          break;
        case States.State19:
          next = Transition(ch, States.State23, States.Error, States.Error, States.Error, States.Error);
          break;
        case States.State20:
          next = Transition(ch, States.State24, States.Error, States.Error, States.Error, States.Error);
          break;
        case States.State21:
          next = Transition(ch, States.State25, States.Error, States.Error, States.Error, States.Error);
          break;
        case States.State22:
          next = Transition(ch, States.Error, States.Error, States.State14, States.Error, States.Error);
          break;
        case States.State23:
          next = Transition(ch, States.Error, States.Error, States.Error, States.State26, States.Error);
          break;
        case States.State24:
          next = Transition(ch, States.Error, States.Error, States.Error, States.Error, States.State16);
          break;
        case States.State25:
          next = Transition(ch, States.State27, States.Error, States.Error, States.Error, States.Error);
          break;
        case States.State26:
          next = Transition(ch, States.State28, States.Error, States.Error, States.Error, States.Error);
          break;
        case States.State27:
          next = Transition(ch, States.Error, States.Error, States.Error, States.Error, States.Error);
          break;
        case States.State28:
          next = Transition(ch, States.State29, States.Error, States.Error, States.Error, States.Error);
          break;
        case States.State29:
          next = Transition(ch, States.Error, States.Error, States.Error, States.State15, States.Error);
          break;
        default:
          next = States.Error;
          break;
      }
      return next;
    }

    static States Transition(char ch, States whenDigit, States whenSlash, States whenSpace, States whenColon, States whenDot)
    {
      States next = States.Error;

      if (char.IsDigit(ch))
      {
        next = whenDigit;
      }
      else if (ch == '/')
      {
        next = whenSlash;
      }
      else if (ch == ' ')
      {
        next = whenSpace;
      }
      else if (ch == ':')
      {
        next = whenColon;
      }
      else if (ch == '.')
      {
        next = whenDot;
      }
      else
      {
        next = States.Error;
      }

      return next;
    }

    static bool IsError(States state)
    {
      return state == States.Error;
    }
  }
}