2013-02-26 3 views
6

본질적으로 xml 파일을 읽으려고합니다. 값 중 하나에 접미사가 있습니다 (예 : "30d". 이것은 '30 일 '을 의미합니다. 그래서 이것을 DateTime.Now.AddDays(30)으로 변환하려고합니다. 열거 형과 문자열 일치

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

지금 나는 (그것을 열거에 올 때 내가 조금 바보 야) 효율적으로이 문제를 접근하는 방법을 정확히 정확히 모르겠어요 다음 XML이 필드를 읽으려면, 내가 열거를 사용하기로 결정했다. 이 접미사 (이 경우 "d")를 먼저 문자열에서 분리해야하나요? switch 문을 사용하여 enum에 시도해보십시오.

내 질문에 멍청한 사람이라면 다음과 같을 것입니다. 30d에서 DateTime.Now.AddDays(30)으로가는 가장 좋은 방법은 무엇입니까?

+0

값이 "1d 12h"와 같은 혼합 값을 가질 가능성이 있습니까? –

+0

30d5h6m 또는 25d7m과 같은 접미사를 사용할 수 있습니까? –

+0

X = int이고 y = 접미사 : d/m/h – jzm

답변

6

:

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

+2

확실히 가장 간단한 대답입니다. 내가 바꿀 유일한 것은 문자열을 ""로 나눠서 foreach 루프를 통해 실행함으로써 DateTime.Now.AddDuration ("12d 2h 30m")과 같은 값을 받아 들일 수 있습니다. – Corylulu

3

업데이트 : 투표하지 마십시오. 나는 대체 접근법이기 때문에 간단히 남겨두고있다. 대신 sa_ddam213과 Dr. Wily 's Apprentice의 답변을보십시오.

나는 문자열에서,이 경우, "D"접미사를 분리해야 첫째, 다음 시도하고 스위치 문을 사용하여 열거에 일치?

예. 완벽하게 작동 예를 들어

:

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

네가 '스위치'네가 나를 때렸어. 그리고 그것의 한 단어 대답! –

+0

필자는이 질문을 인용하여 "문자"의 최소 문자를 넘어야했습니다. ;) – NotMe

+0

내 대답을 삭제했습니다. 전에 끝내 셨습니다. RegEx가 아마도 가장 확실한 대답 일 것 같지만 이것이 그가 가고있는 일에 더 가깝습니다. 일반적으로 예외를 throw하는 것을 피하기 위해 Enum.TryParse를 사용해야합니다. – Corylulu

0

열거 형은 숫자가 아닌 유형의 백업, 그래서 문자열 기반 열거 형은 밖으로 할 수 없습니다. 그것은 당신이 그것을 overthinking 수 있습니다. 문제에 대해 더 이상 알지 못하면 가장 간단한 해결책은 나머지 문자를 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의 대답 참조)를 사용할 수 있습니다. 당신은 ExtensionMethod 문자열을 구문 분석 할 수 있도록하고

뭔가처럼 원하는 날짜 시간을 반환 할 수

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 함수를 안전하게 사용할 수 있습니다. 입력이 적어도 하나의 숫자와 최대 10 개로 구성된다는 것을 알고 있기 때문입니다 (전혀 캡처하지 않은 경우가 아니면 빈 문자열이됩니다). 따라서이 함수의 목적은 다음과 같습니다. 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

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(); 
    } 
} 

은 접미어를 정의하는 속성을 사용하여, 당신의 오프셋을 정의하는 ENUM 값을 사용한다.

이 필요합니다 :

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

이 그것은 열거 정수 값을 사용하는 해킹 고려 될 수 있지만,이 예제는 여전히 구문 분석하게됩니다 모든 열거 형 작은 비틀기와 (스위치 케이스와 같은 다른 사용).

관련 문제