2013-02-26 209 views
6

我基本上試圖讀取一個XML文件。其中一個值具有後綴,例如「30d」。這意味着'30天'。所以我試圖將其轉換爲DateTime.Now.AddDays(30)。要閱讀的XML這個領域,我決定使用一個枚舉:枚舉和字符串匹配

enum DurationType { Min = "m", Hours = "h", Days = "d" } 

現在我不能完全確定究竟是如何有效地處理這個(我有點愚蠢,當談到枚舉)。我是否應該先將字尾中的後綴「d」分開,然後嘗試使用switch聲明在enum中匹配它?

我想如果你貶低我的問題,它會是:什麼是從30dDateTime.Now.AddDays(30)的最佳途徑?

+0

該值是否可能有混合,如「1d 12h」? – 2013-02-26 00:39:23

+0

你有沒有30d5h6m或25d7m的後綴? – 2013-02-26 00:39:40

+0

它只會是XXY:其中X = int,而y =後綴:d/m/h – jzm 2013-02-26 00:54:43

回答

6

你可以做一個ExtensionMethod解析字符串,返回日期時間你想

喜歡的東西:

public static DateTime AddDuration(this DateTime datetime, string str) 
    { 
     int value = 0; 
     int mutiplier = str.EndsWith("d") ? 1440 : str.EndsWith("h") ? 60 : 1; 
     if (int.TryParse(str.TrimEnd(new char[]{'m','h','d'}), out value)) 
     { 
      return datetime.AddMinutes(value * mutiplier); 
     } 
     return datetime; 
    } 

用法:

var date = DateTime.Now.AddDuration("2d"); 
+0

我喜歡這一點。沒有正則表達式,沒有枚舉,只有幾行代碼。簡單而重要。做得好。 – NotMe 2013-02-26 01:00:18

+2

這裏絕對是最簡單的答案。只有我會改變的是將字符串拆分爲「」並通過foreach循環運行,以便它可以接受像DateTime.Now.AddDuration(「12d 2h 30m」)這樣的值; – Corylulu 2013-02-26 01:13:30

3

更新: 不要爲此投票。我只是因爲它是一種替代方法而離開它。相反看看sa_ddam213和Wily博士的學徒的答案。

我應該分開後綴,在這種情況下,「d」,出字符串的 ,然後再嘗試使用switch語句中枚舉搭配呢?

是的。

對於一個完全工作的例子:

private void button1_Click(object sender, EventArgs e) { 
    String value = "30d"; 

    Duration d = (Duration)Enum.Parse(typeof(Duration), value.Substring(value.Length - 1, 1).ToUpper()); 
    DateTime result = d.From(new DateTime(), value); 

    MessageBox.Show(result.ToString()); 
} 



enum Duration { D, W, M, Y }; 

static class DurationExtensions { 
    public static DateTime From(this Duration duration, DateTime dateTime, Int32 period) { 
     switch (duration) 
     { 
      case Duration.D: return dateTime.AddDays(period); 
      case Duration.W: return dateTime.AddDays((period*7)); 
      case Duration.M: return dateTime.AddMonths(period); 
      case Duration.Y: return dateTime.AddYears(period); 

      default: throw new ArgumentOutOfRangeException("duration"); 
     } 
    } 
    public static DateTime From(this Duration duration, DateTime dateTime, String fullValue) { 
     Int32 period = Convert.ToInt32(fullValue.ToUpper().Replace(duration.ToString(), String.Empty)); 
     return From(duration, dateTime, period); 
    } 
} 
+0

是的'開關'你打敗了我。它的一個字的答案! – 2013-02-26 00:38:00

+0

我必須引用這個問題才能超過最小字符「功能」。 ;) – NotMe 2013-02-26 00:39:36

+0

刪除我的答案,你在我之前完成了。我認爲RegEx可能是最明顯的答案,但這更接近他的目標。儘管如此,你應該使用Enum.TryParse來避免拋出異常。 – Corylulu 2013-02-26 01:03:40

0

枚舉不能與非數字類型來支持,因此基於字符串的枚舉都出來了。有可能你可能正在推翻它。在不瞭解這個問題的情況下,最簡單的解決方案似乎是將最後一個字符分開,將其餘的字符轉換爲int,然後將每個最終字符作爲單獨的大小寫處理。

1

嘗試下面的代碼,假設像「30d」這樣的值在字符串「val」中。

DateTime ConvertValue(string val) { 
    if (val.Length > 0) { 
     int prefix = Convert.ToInt32(val.Length.Remove(val.Length-1)); 
     switch (val[val.Length-1]) { 
     case 'd': return DateTime.Now.AddDays(prefix); 
     case 'm': return DateTime.Now.AddMonths(prefix); 
     // etc. 
    } 
    throw new ArgumentException("string in unexpected format."); 
} 
0

我建議使用正則表達式到第一條帶的數量和比執行Enum.Parse Method評估枚舉的值。根據解析的數字和枚舉值,您可以使用開關(請參閱Corylulu的答案)來獲得正確的偏移量。

2

我真不明白怎麼使用一個enum幫助這裏。

下面是我可能會接近它的方法。

string s = "30d"; 

int typeIndex = s.IndexOfAny(new char[] { 'd', 'w', 'm' }); 
if (typeIndex > 0) 
{ 
    int value = int.Parse(s.Substring(0, typeIndex)); 
    switch (s[typeIndex]) 
    { 
     case 'd': 
      result = DateTime.Now.AddDays(value); 
      break; 
     case 'w': 
      result = DateTime.Now.AddDays(value * 7); 
      break; 
     case 'm': 
      result = DateTime.Now.AddMonths(value); 
      break; 
    } 
} 

根據您輸入數據的可靠性,您可能需要使用int.TryParse(),而不是int.Parse()。否則,這應該是你所需要的。

注:我還寫了一個sscanf()替代。NET來處理這個很容易。您可以在文章A sscanf() Replacement for .NET中看到該代碼。

5

這似乎是一個使用正則表達式的好地方;特別是捕獲小組。

下面是一個工作示例:

using System; 
using System.Text.RegularExpressions; 

namespace RegexCaptureGroups 
{ 
    class Program 
    { 
     // Below is a breakdown of this regular expression: 
     // First, one or more digits followed by "d" or "D" to represent days. 
     // Second, one or more digits followed by "h" or "H" to represent hours. 
     // Third, one or more digits followed by "m" or "M" to represent minutes. 
     // Each component can be separated by any number of spaces, or none. 
     private static readonly Regex DurationRegex = new Regex(@"((?<Days>\d+)d)?\s*((?<Hours>\d+)h)?\s*((?<Minutes>\d+)m)?", RegexOptions.IgnoreCase); 

     public static TimeSpan ParseDuration(string input) 
     { 
      var match = DurationRegex.Match(input); 

      var days = match.Groups["Days"].Value; 
      var hours = match.Groups["Hours"].Value; 
      var minutes = match.Groups["Minutes"].Value; 

      int daysAsInt32, hoursAsInt32, minutesAsInt32; 

      if (!int.TryParse(days, out daysAsInt32)) 
       daysAsInt32 = 0; 

      if (!int.TryParse(hours, out hoursAsInt32)) 
       hoursAsInt32 = 0; 

      if (!int.TryParse(minutes, out minutesAsInt32)) 
       minutesAsInt32 = 0; 

      return new TimeSpan(daysAsInt32, hoursAsInt32, minutesAsInt32, 0); 
     } 

     static void Main(string[] args) 
     { 
      Console.WriteLine(ParseDuration("30d")); 
      Console.WriteLine(ParseDuration("12h")); 
      Console.WriteLine(ParseDuration("20m")); 
      Console.WriteLine(ParseDuration("1d 12h")); 
      Console.WriteLine(ParseDuration("5d 30m")); 
      Console.WriteLine(ParseDuration("1d 12h 20m")); 

      Console.WriteLine("Press any key to exit."); 
      Console.ReadKey(); 
     } 
    } 
} 

編輯:下面是一個另類,上面的稍微精簡版,雖然我不知道我更喜歡哪一個。我通常不是過分密集代碼的粉絲。 我調整了正則表達式,在每個數字上加上10位數的限制。這使我可以安全地使用int.Parse函數,因爲我知道輸入由至少一個數字和至多十個數字組成(除非它根本不捕獲,在這種情況下它將是空字符串:因此,目的是ParseInt32ZeroIfNullOrEmpty方法)。

 // Below is a breakdown of this regular expression: 
     // First, one to ten digits followed by "d" or "D" to represent days. 
     // Second, one to ten digits followed by "h" or "H" to represent hours. 
     // Third, one to ten digits followed by "m" or "M" to represent minutes. 
     // Each component can be separated by any number of spaces, or none. 
     private static readonly Regex DurationRegex = new Regex(@"((?<Days>\d{1,10})d)?\s*((?<Hours>\d{1,10})h)?\s*((?<Minutes>\d{1,10})m)?", RegexOptions.IgnoreCase); 

     private static int ParseInt32ZeroIfNullOrEmpty(string input) 
     { 
      return string.IsNullOrEmpty(input) ? 0 : int.Parse(input); 
     } 

     public static TimeSpan ParseDuration(string input) 
     { 
      var match = DurationRegex.Match(input); 

      return new TimeSpan(
       ParseInt32ZeroIfNullOrEmpty(match.Groups["Days"].Value), 
       ParseInt32ZeroIfNullOrEmpty(match.Groups["Hours"].Value), 
       ParseInt32ZeroIfNullOrEmpty(match.Groups["Minutes"].Value), 
       0); 
     } 

編輯:只是藉此一個步驟,我已經添加下面的另一個版本,它可以處理天數,小時,分鐘,秒和毫秒,用各種縮寫爲每個。爲了便於閱讀,我將正則表達式分成多行。請注意,我還必須在每個組件末尾使用(\b|(?=[^a-z]))來調整表達式:這是因爲「ms」單位被捕獲爲「m」單位。與「[^ a-z]」一起使用的「?=」的特殊語法表示匹配字符,但不是「消耗」它。控制檯應用程序實例/教程

// Below is a breakdown of this regular expression: 
    // First, one to ten digits followed by "d", "dy", "dys", "day", or "days". 
    // Second, one to ten digits followed by "h", "hr", "hrs", "hour", or "hours". 
    // Third, one to ten digits followed by "m", "min", "minute", or "minutes". 
    // Fourth, one to ten digits followed by "s", "sec", "second", or "seconds". 
    // Fifth, one to ten digits followed by "ms", "msec", "millisec", "millisecond", or "milliseconds". 
    // Each component may be separated by any number of spaces, or none. 
    // The expression is case-insensitive. 
    private static readonly Regex DurationRegex = new Regex(@" 
     ((?<Days>\d{1,10})(d|dy|dys|day|days)(\b|(?=[^a-z])))?\s* 
     ((?<Hours>\d{1,10})(h|hr|hrs|hour|hours)(\b|(?=[^a-z])))?\s* 
     ((?<Minutes>\d{1,10})(m|min|minute|minutes)(\b|(?=[^a-z])))?\s* 
     ((?<Seconds>\d{1,10})(s|sec|second|seconds)(\b|(?=[^a-z])))?\s* 
     ((?<Milliseconds>\d{1,10})(ms|msec|millisec|millisecond|milliseconds)(\b|(?=[^a-z])))?", 
     RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); 

    private static int ParseInt32ZeroIfNullOrEmpty(string input) 
    { 
     return string.IsNullOrEmpty(input) ? 0 : int.Parse(input); 
    } 

    public static TimeSpan ParseDuration(string input) 
    { 
     var match = DurationRegex.Match(input); 

     return new TimeSpan(
      ParseInt32ZeroIfNullOrEmpty(match.Groups["Days"].Value), 
      ParseInt32ZeroIfNullOrEmpty(match.Groups["Hours"].Value), 
      ParseInt32ZeroIfNullOrEmpty(match.Groups["Minutes"].Value), 
      ParseInt32ZeroIfNullOrEmpty(match.Groups["Seconds"].Value), 
      ParseInt32ZeroIfNullOrEmpty(match.Groups["Milliseconds"].Value)); 
    } 
+1

這絕對是最可擴展的方法,可能是一種更普遍的可接受的方式來做OP的要求。 – Corylulu 2013-02-26 01:06:34

1

實施例:

enum DurationType 
{ 
    [DisplayName("m")] 
    Min = 1, 
    [DisplayName("h")] 
    Hours = 1 * 60, 
    [DisplayName("d")] 
    Days = 1 * 60 * 24 
} 

internal class Program 
{ 
    private static void Main(string[] args) 
    { 

     string input1 = "10h"; 
     string input2 = "1d10h3m"; 

     var x = GetOffsetFromDate(DateTime.Now, input1); 
     var y = GetOffsetFromDate(DateTime.Now, input2); 

    } 

    private static Dictionary<string, DurationType> suffixDictionary 
    { 
     get 
     { 
      return Enum 
       .GetValues(typeof (DurationType)) 
       .Cast<DurationType>() 
       .ToDictionary(duration => duration.GetDisplayName(), duration => duration); 
     } 
    } 

    public static DateTime GetOffsetFromDate(DateTime date, string input) 
    { 
     MatchCollection matches = Regex.Matches(input, @"(\d+)([a-zA-Z]+)"); 
     foreach (Match match in matches) 
     { 
      int numberPart = Int32.Parse(match.Groups[1].Value); 
      string suffix = match.Groups[2].Value; 
      date = date.AddMinutes((int)suffixDictionary[suffix]); 
     } 
     return date; 
    } 


} 


[AttributeUsage(AttributeTargets.Field)] 
public class DisplayNameAttribute : Attribute 
{ 
    public DisplayNameAttribute(String name) 
    { 
     this.name = name; 
    } 
    protected String name; 
    public String Name { get { return this.name; } } 
} 

public static class ExtensionClass 
{ 
    public static string GetDisplayName<TValue>(this TValue value) where TValue : struct, IConvertible 
    { 
     FieldInfo fi = typeof(TValue).GetField(value.ToString()); 
     DisplayNameAttribute attribute = (DisplayNameAttribute)fi.GetCustomAttributes(typeof(DisplayNameAttribute), false).FirstOrDefault(); 
     if (attribute != null) 
      return attribute.Name; 
     return value.ToString(); 
    } 
} 

用途的屬性來定義的後綴,使用枚舉值來定義的偏移量。

要求:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Reflection; 
using System.Text.RegularExpressions; 

可以考慮使用枚舉整數值的技巧,但這個例子仍然讓你分析出所有的枚舉(像開關情況下,任何其他用途)與小的調整。