문자열을 nullable int로 구문 분석하는 방법


300

C #에서 문자열을 nullable int로 구문 분석하고 싶습니다. 즉. 문자열의 int 값을 반환하거나 구문 분석 할 수없는 경우 null을 반환하고 싶습니다.

나는 이것이 효과가 있기를 바랐다.

int? val = stringVal as int?;

그러나 그것은 작동하지 않으므로 지금하는 방법은이 확장 방법을 작성하는 것입니다

public static int? ParseNullableInt(this string value)
{
    if (value == null || value.Trim() == string.Empty)
    {
        return null;
    }
    else
    {
        try
        {
            return int.Parse(value);
        }
        catch
        {
            return null;
        }
    }
}   

더 좋은 방법이 있습니까?

편집 : TryParse 제안에 감사드립니다. 나는 그것에 대해 알고 있었지만 거의 동일하게 작동했습니다. nullable int로 직접 구문 분석 할 기본 제공 프레임 워크 방법이 있는지 알고 싶습니다.


1
string.IsNullOrEmpty (value)를 사용하여 if 행을 더 명확하게 얻을 수 있습니다.
Özgür Kaplan

답변:


352

int.TryParse 아마 좀 더 쉬울 것입니다 :

public static int? ToNullableInt(this string s)
{
    int i;
    if (int.TryParse(s, out i)) return i;
    return null;
}

편집 @Glenn int.TryParse은 "프레임 워크에 내장되어 있습니다". 그것은하고 int.Parse있습니다 의 int로 문자열을 구문 분석하는 방법.


82
한 줄 이하 : return Int32.TryParse (s, out i)? i : null;
Chris Shouts

2
"a"는 null을 반환하지만 int가 아니며 예외를 던져야합니다
Arsen Mkrtchyan

54
@Chris, 컴파일러는 인라인 if 문을 좋아하지 않습니다 (이 유형은 호환되지 않습니다 : 'int': 'null'). Int32.TryParse (s, out i)를 반환하도록 수정해야 했습니까? (int?) i : null;
death_au

8
Int32는 int의 별칭입니다. int.TryParse를 사용하여 형식을 정렬 상태로 유지합니다. int를 사용하여 다른 비트 길이 정수 (발생)를 나타내는 경우 Int32는 int와 정렬되지 않습니다.
Richard Collette

4
int.TryParse (s, out i)를 반환합니까? (int?) i : null;
Nick Spreitzer

177

조건부 연산자와 null널 입력 가능 유형으로 캐스트 할 수 있다는 사실을 사용하여 한 행으로이를 수행 할 수 있습니다 (기존 int가없는 경우 두 행 TryParse) 의 출력에 재사용 할 수 있습니다 .

사전 C # 7 :

int tempVal;
int? val = Int32.TryParse(stringVal, out tempVal) ? Int32.Parse(stringVal) : (int?)null;

메소드 호출에서 출력 변수를 선언 할 수있는 C # 7의 업데이트 된 구문을 사용하면 훨씬 간단 해집니다.

int? val = Int32.TryParse(stringVal, out var tempVal) ? tempVal : (int?)null;

4
그것은 조건부 연산자에 대한 귀하의 견해에 달려 있다고 생각합니다. 내 정신 모델은 if-else와 동등한 구문 설탕이라는 것입니다.이 경우 내 버전과 Matt는 더 명확하고 더 많은 cmopact로 동일합니다.
McKenzieG1

11
여기에는 평가 순서 부작용이 없습니다. 모든 단계는 명시 적으로 정렬되고 정확합니다.
Jon Hanna

22
returnint.TryParse(val, out i) ? i : default(int?);
Bart Calixto

7
@Bart의 "답변"이 최고입니다!
Andre Figueiredo

4
이제 C # 6에서는 한 줄이 될 수 있습니다! Int32.TryParse (stringVal, out var tempVal)? tempVal : (int?) null;
MerickOWA

34

[ @sblom의 제안에 따라 최신 C #을 사용하도록 업데이트 됨 ]

나는이 문제가 있었고 결국 이것으로 끝났다 (결국 a if와 2 return는 너무 오래 감았 다!).

int? ToNullableInt (string val)
    => int.TryParse (val, out var i) ? (int?) i : null;

더 심각한 int것은 C # 키워드 인 Int32. .NET Framework BCL 유형 인 을 혼합하지 마십시오. 작동하지만 코드가 지저분 해 보입니다.


3
이것이 실제로 컴파일 된
후에

1
C # 7에서 더 간결한 내용 : int i;줄을 삭제하고 다음과 같이 진행하십시오.return int.TryParse (val, out var i) ? (int?) i : null;
sblom

2
완전성을 위해 ;-)int? ParseNInt (string val) => int.TryParse (val, out var i) ? (int?) i : null;
Duckboy

C # 6을 사용하면 한 줄로 줄일 수 있습니다. return int.TryParse (value, out var result)? 결과 : (int?) null;
MeanGreen

16

Glenn Slaven : nullable int로 직접 구문 분석하는 내장 프레임 워크 방법이 있는지 알고 싶습니다.

값이 null 또는 빈 문자열처럼 유효하지만 유효하지 않은 값에 대한 예외를 throw하므로 예외를 잡아서 기본값을 반환 해야하는 경우이 방법은 null이 가능한 int (및 int가 아닌)로 직접 구문 분석됩니다 그러한 상황에서 :

public static T Parse<T>(object value)
{
    try { return (T)System.ComponentModel.TypeDescriptor.GetConverter(typeof(T)).ConvertFrom(value.ToString()); }
    catch { return default(T); }
}

이 접근 방식은 널 입력 가능 구문 분석 및 널 입력 가능에 여전히 사용할 수 있습니다.

enum Fruit { Orange, Apple }
var res1 = Parse<Fruit>("Apple");
var res2 = Parse<Fruit?>("Banana");
var res3 = Parse<int?>("100") ?? 5; //use this for non-zero default
var res4 = Parse<Unit>("45%");

주의 : 변환기에는 예외를 캡처하는 대신 사용할 수있는 IsValid 메서드가 있습니다 (예외가 발생 하면 예상 한 경우 불필요한 오버 헤드 가 발생 함 ). 불행히도 .NET 4 이후에만 작동하지만 올바른 DateTime 형식의 유효성을 검사 할 때 로케일을 확인하지 못하는 문제가 여전히 있습니다 ( 버그 93559 참조) .


나는 이것을 정수로 테스트했고 int.TryParse ((string) value, out var result)보다 훨씬 느리다? 결과 : default (int?);
Wouter

12
var result = int.TryParse(foo, out var f) ? f : default(int?);

출처 :


이게 어떻게 작동할까요? Tryparse가 작동하지 않거나 널 입력 가능 변수가 아니므로 예제에서 f는 널 입력 가능해야합니다.
John Lord

@JohnLord
Jaa H

tryparse는 nullable이 아닌 변수에 포함될 것으로 예상하므로 default (int?) 강제 var이 nullable이 아닙니까?
John Lord

@JohnLord 아마도 이것은 당신이 무슨 일이 일어나고 있는지 이해하는 데 도움이 될 것입니다 stackoverflow.com/questions/3632918/…
Jaa H

9

오래된 주제이지만 어떻습니까?

public static int? ParseToNullableInt(this string value)
{
     return String.IsNullOrEmpty(value) ? null : (int.Parse(value) as int?);
}

나는 null을 구문 분석 할 requriement, TryParse 버전이 ToNullableInt32 (XXX)에서 오류를 발생시키지 않는 것이 좋습니다. 원치 않는 자동 오류가 발생할 수 있습니다.


1
정확히 요점입니다-문자열을 파싱 할 수 없으면 예외를 throw하지 않고 int반환해야합니다 null.
svick

1
value가 숫자가 아닌 경우, int.Parse는 예외를 발생시킵니다. 이는 null을 반환하는 것과 다릅니다.
phu

8

이 시도:

public static int? ParseNullableInt(this string value)
{
    int intValue;
    if (int.TryParse(value, out intValue))
        return intValue;
    return null;
}

5

내 솔루션이 매우 깨끗하고 좋은 솔루션이라고 생각합니다.

public static T? NullableParse<T>(string s) where T : struct
{
    try
    {
        return (T)typeof(T).GetMethod("Parse", new[] {typeof(string)}).Invoke(null, new[] { s });
    }
    catch (Exception)
    {
        return null;
    }
}

이것은 물론 generics 인수에 정적 메소드 "Parse (string)"만 있으면되는 일반적인 솔루션입니다. 숫자, 부울, DateTime 등에서 작동합니다.


5

다른 모든 답변을 잊을 수 있습니다-훌륭한 일반적인 해결책이 있습니다 : http://cleansharp.de/wordpress/2011/05/generischer-typeconverter/

이를 통해 다음과 같이 매우 깨끗한 코드를 작성할 수 있습니다.

string value = null;
int? x = value.ConvertOrDefault();

그리고 또한:

object obj = 1;  

string value = null;
int x = 5;
if (value.TryConvert(out x))
    Console.WriteLine("TryConvert example: " + x); 

bool boolean = "false".ConvertOrDefault();
bool? nullableBoolean = "".ConvertOrDefault();
int integer = obj.ConvertOrDefault();
int negativeInteger = "-12123".ConvertOrDefault();
int? nullableInteger = value.ConvertOrDefault();
MyEnum enumValue = "SecondValue".ConvertOrDefault();

MyObjectBase myObject = new MyObjectClassA();
MyObjectClassA myObjectClassA = myObject.ConvertOrDefault();

1
이것은 실제로 매우 유용합니다. 내 의견으로는 모든 프로그램에서 변환이 매우 일반적이기 때문에 표준 c # 라이브러리에 있어야합니다.)
BigChief

이것은 매우 훌륭하고 유용하지만 큰 항목 모음의 각 요소를 변환해야 할 때 속도가 매우 느리다고 덧붙일 수 있습니다. 이 방법을 사용하면 각 항목의 8 개 속성을 변환하는 데 전체 수집을 완료하는 데 최대 1 시간이 걸립니다. 동일한 샘플 데이터를 사용하지만 Matt Hamilton의 접근 방식을 사용 하면 완료하는 데 몇 초 정도 걸립니다.
zed

3

다음은 모든 구조체 유형에 적용됩니다. 이것은 MSDN 포럼의 Matt Manela의 코드를 기반으로합니다 . Murph가 지적한 것처럼 예외 처리는 Types 전용 TryParse 메서드를 사용하는 것보다 비쌀 수 있습니다.

        public static bool TryParseStruct<T>(this string value, out Nullable<T> result)
            where T: struct 
        {
            if (string.IsNullOrEmpty(value))
            {
                result = new Nullable<T>();

                return true;
            }

            result = default(T);
            try
            {
                IConvertible convertibleString = (IConvertible)value;
                result = new Nullable<T>((T)convertibleString.ToType(typeof(T), System.Globalization.CultureInfo.CurrentCulture));
            }
            catch(InvalidCastException)
            {
                return false;
            }
            catch (FormatException)
            {
                return false;
            }

           return true;
        }

이것들은 내가 사용한 기본 테스트 사례입니다.

        string parseOne = "1";
        int? resultOne;
        bool successOne = parseOne.TryParseStruct<int>(out resultOne);
        Assert.IsTrue(successOne);
        Assert.AreEqual(1, resultOne);

        string parseEmpty = string.Empty;
        int? resultEmpty;
        bool successEmpty = parseEmpty.TryParseStruct<int>(out resultEmpty);
        Assert.IsTrue(successEmpty);
        Assert.IsFalse(resultEmpty.HasValue);

        string parseNull = null;
        int? resultNull;
        bool successNull = parseNull.TryParseStruct<int>(out resultNull);
        Assert.IsTrue(successNull);
        Assert.IsFalse(resultNull.HasValue);

        string parseInvalid = "FooBar";
        int? resultInvalid;
        bool successInvalid = parseInvalid.TryParseStruct<int>(out resultInvalid);
        Assert.IsFalse(successInvalid);

3

구문 분석이 불가능한 경우 기본값을 정의 할 수있는 기능으로 문자열 구문 분석을 위해 다음 확장 메소드를 int 값으로 제안하는 것이 좋습니다.

public static int ParseInt(this string value, int defaultIntValue = 0)
        {
            return int.TryParse(value, out var parsedInt) ? parsedInt : defaultIntValue;
        }

public static int? ParseNullableInt(this string value)
        {
            if (string.IsNullOrEmpty(value))
                return null;

            return value.ParseInt();
        }

이미 너무나 많은 찬성에 대한 답변이 있습니다. 귀하의 답변이 필요하다고 생각하고이 게시물에 새로운 품질이 추가됩니까?
L. Guthardt

1
@ L.Guthardt 그렇습니다. 제 답변이 해당 문제를 설명하는보다 보편적 인 방법을 제시한다고 생각합니다. 감사합니다.
Aleksandr Neizvestnyi

2

이 솔루션은 리플렉션 오버 헤드없이 일반적입니다.

public static Nullable<T> ParseNullable<T>(string s, Func<string, T> parser) where T : struct
{
    if (string.IsNullOrEmpty(s) || string.IsNullOrEmpty(s.Trim())) return null;
    else return parser(s);
}

static void Main(string[] args)
{
    Nullable<int> i = ParseNullable("-1", int.Parse);
    Nullable<float> dt = ParseNullable("3.14", float.Parse);
}

나는 당신이 바꿀 수 있다고 생각 IsNullOrEmptyIsNullOrWhitespace
NibblyPig


1

좀 더 일반적인 광산을 공유해야한다고 느꼈습니다.

용법:

var result = "123".ParseBy(int.Parse);

var result2 = "123".ParseBy<int>(int.TryParse);

해결책:

public static class NullableParse
{
    public static Nullable<T> ParseBy<T>(this string input, Func<string, T> parser)
        where T : struct
    {
        try
        {
            return parser(input);
        }
        catch (Exception exc)
        {
            return null;
        }
    }

    public delegate bool TryParseDelegate<T>(string input, out T result);

    public static Nullable<T> ParseBy<T>(this string input, TryParseDelegate<T> parser)
        where T : struct
    {
        T t;
        if (parser(input, out t)) return t;
        return null;
    }
}

첫 번째 버전은 try-catch가 필요하지만 속도가 느리기 때문에 속도가 느립니다. 유효하지 않은 문자열로 여러 번 호출되지 않으면 그렇게 중요하지 않습니다. 성능에 문제가있는 경우 TryParse 메서드를 사용할 때는 컴파일러에서 유추 할 수 없으므로 ParseBy의 형식 매개 변수를 지정해야합니다. 또한 Func <> 내에서 out 키워드를 사용할 수 없으므로 대리자를 정의해야했지만 적어도 이번에는 컴파일러에 명시 적 인스턴스가 필요하지 않습니다.

마지막으로 다른 구조체와 함께 사용할 수도 있습니다 (예 : decimal, DateTime, Guid 등).


1

Generic NullableParser 클래스에 대한 코드를 찾아서 수정했습니다. 전체 코드는 내 블로그 Nullable TryParse에 있습니다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
namespace SomeNamespace
{
    /// <summary>
    /// A parser for nullable types. Will return null when parsing fails.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    ///
    public static class NullableParser<T> where T : struct
    {
        public delegate bool TryParseDelegate(string s, out T result);
        /// <summary>
        /// A generic Nullable Parser. Supports parsing of all types that implements the tryParse method;
        /// </summary>
        /// <param name="text">Text to be parsed</param>
        /// <param name="result">Value is true for parse succeeded</param>
        /// <returns>bool</returns>
        public static bool TryParse(string s, out Nullable<T> result)
        {
            bool success = false;
            try
            {
                if (string.IsNullOrEmpty(s))
                {
                    result = null;
                    success = true;
                }
                else
                {
                    IConvertible convertableString = s as IConvertible;
                    if (convertableString != null)
                    {
                        result = new Nullable<T>((T)convertableString.ToType(typeof(T),
                            CultureInfo.CurrentCulture));
                        success = true;
                    }
                    else
                    {
                        success = false;
                        result = null;
                    }
                }
            }
            catch
            {
                success = false;
                result = null;
            }
            return success;
        }
    }
}

1
404 찾을 수 없음. 링크를 제공하는 것은 좋은 습관이 아닙니다.
Dirty-flow

전체 코드로 @ Dirty-flow 업데이트에 대해 죄송합니다. :)
John Dauphine

1
    public static void Main(string[] args)
    {

        var myString = "abc";

        int? myInt = ParseOnlyInt(myString);
        // null

        myString = "1234";

        myInt = ParseOnlyInt(myString);
        // 1234
    }
    private static int? ParseOnlyInt(string s)
    {
        return int.TryParse(s, out var i) ? i : (int?)null;
    }

1
myString이 숫자가 아닌 경우, int.Parse는 예외를 발생시킵니다. 이는 null을 반환하는 것과 다릅니다.
phu

0

필요하지 않은 경우 예외를 사용 해서는 안됩니다 . 오버 헤드는 끔찍합니다.

TryParse의 변형은 문제를 해결합니다. 코드를 더 우아하게 보이도록 창의적으로 만들려면 3.5의 확장 메서드를 사용하여 무언가를 수행 할 수 있지만 코드는 거의 동일합니다.


0

델리게이트를 사용하여 다음 코드는 둘 이상의 구조 유형에 대해 널 입력 가능 구문 분석이 필요한 경우 재사용 성을 제공 할 수 있습니다. .Parse () 및 .TryParse () 버전을 모두 여기에 표시했습니다.

다음은 사용법 예입니다.

NullableParser.TryParseInt(ViewState["Id"] as string);

그리고 여기에 당신을 데려 오는 코드가 있습니다 ...

public class NullableParser
  {
    public delegate T ParseDelegate<T>(string input) where T : struct;
    public delegate bool TryParseDelegate<T>(string input, out T outtie) where T : struct;
    private static T? Parse<T>(string input, ParseDelegate<T> DelegateTheParse) where T : struct
    {
      if (string.IsNullOrEmpty(input)) return null;
      return DelegateTheParse(input);
    }
    private static T? TryParse<T>(string input, TryParseDelegate<T> DelegateTheTryParse) where T : struct
    {
      T x;
      if (DelegateTheTryParse(input, out x)) return x;
      return null;
    }
    public static int? ParseInt(string input)
    {
      return Parse<int>(input, new ParseDelegate<int>(int.Parse));
    }
    public static int? TryParseInt(string input)
    {
      return TryParse<int>(input, new TryParseDelegate<int>(int.TryParse));
    }
    public static bool? TryParseBool(string input)
    {
      return TryParse<bool>(input, new TryParseDelegate<bool>(bool.TryParse));
    }
    public static DateTime? TryParseDateTime(string input)
    {
      return TryParse<DateTime>(input, new TryParseDelegate<DateTime>(DateTime.TryParse));
    }
  }


0

나는 이것을 요구했다. 내 요구 사항을 만족시켰다. (확장 방법이 프레임 워크의 TryParse의 리턴을 최대한 가깝게 모방하려고했지만 try {} catch {} 블록이없고 컴파일러가 유추에 대해 불평하지 않고 프레임 워크 메소드 내에서 널 입력 가능 유형)

private static bool TryParseNullableInt(this string s, out int? result)
{
    int i;
    result = int.TryParse(s, out i) ? (int?)i : null;
    return result != null;
}

0

다음 코드를 제안합니다. 변환 오류가 발생하면 예외로 작업 할 수 있습니다.

public static class Utils {      
public static bool TryParse<Tin, Tout>(this Tin obj, Func<Tin, Tout> onConvert, Action<Tout> onFill, Action<Exception> onError) {
  Tout value = default(Tout);
  bool ret = true;
  try {
    value = onConvert(obj);
  }
  catch (Exception exc) {
    onError(exc);
    ret = false;
  }
  if (ret)
    onFill(value);
  return ret;
}

public static bool TryParse(this string str, Action<int?> onFill, Action<Exception> onError) {
  return Utils.TryParse(str
    , s => string.IsNullOrEmpty(s) ? null : (int?)int.Parse(s)
    , onFill
    , onError);
}
public static bool TryParse(this string str, Action<int> onFill, Action<Exception> onError) {
  return Utils.TryParse(str
    , s => int.Parse(s)
    , onFill
    , onError);
}
}

코드에서이 확장 메소드를 사용하십시오 (fill int? 개인 클래스의 Age 특성).

string ageStr = AgeTextBox.Text;
Utils.TryParse(ageStr, i => person.Age = i, exc => { MessageBox.Show(exc.Message); });

또는

AgeTextBox.Text.TryParse(i => person.Age = i, exc => { MessageBox.Show(exc.Message); });
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.