[C#] 유효한 Base64 인코딩 문자열을 확인하는 방법

C #에서 문자열이 Base 64로 인코딩되었는지 확인하는 방법이 있습니까? 변환을 시도하고 오류가 있는지 확인하는 것 외에는 무엇입니까? 다음과 같은 코드 코드가 있습니다.

// Convert base64-encoded hash value into a byte array.
byte[] HashBytes = Convert.FromBase64String(Value);

값이 유효한 base 64 문자열이 아닌 경우 발생하는 “Invalid character in a Base-64 string”예외를 피하고 싶습니다. 때때로이 값이 기본 64 문자열이 아닐 것으로 예상하기 때문에 예외를 처리하는 대신 false를 확인하고 반환하고 싶습니다. Convert.FromBase64String 함수를 사용하기 전에 확인할 방법이 있습니까?

감사!

업데이트 :
모든 답변에 감사드립니다. 다음은 지금까지 사용할 수있는 확장 메서드입니다. 문자열이 예외없이 Convert.FromBase64String을 전달하는지 확인하는 것 같습니다. .NET은 기본 64로 변환 할 때 모든 후행 및 끝 공백을 무시하는 것처럼 보이므로 “1234”가 유효하고 “1234”도 유효합니다.

public static bool IsBase64String(this string s)
{
    s = s.Trim();
    return (s.Length % 4 == 0) && Regex.IsMatch(s, @"^[a-zA-Z0-9\+/]*={0,3}$", RegexOptions.None);

}

테스트 대 catch 및 예외의 성능에 대해 궁금한 사람들을 위해 대부분의 경우이 base 64의 경우 특정 길이에 도달 할 때까지 예외를 포착하는 것보다 확인하는 것이 더 빠릅니다. 길이가 작을수록 빨라집니다

내 매우 비과학적인 테스트에서 : 문자 길이 100,000-110000에 대해 10000 번 반복하는 경우 먼저 테스트하는 것이 2.7 배 더 빨랐습니다.

총 16,000 개의 테스트에서 1-16 자 길이의 문자 길이에 대해 1000 번 반복하면 10.9 배 더 빨라졌습니다.

예외 기반 방법으로 테스트하는 것이 더 나아지는 지점이 있다고 확신합니다. 나는 그것이 어떤 시점인지 모른다.



답변

Base64 문자열은 문자로만 구성되며 'A'..'Z', 'a'..'z', '0'..'9', '+', '/'길이를 4의 배수로 만들기 위해 끝에 최대 3 개의 ‘=’가 채워지는 경우가 많기 때문에 인식하기가 매우 쉽습니다 . d 예외가 발생하면 무시하는 것이 좋습니다.


답변

C # 7.2에서 Convert.TryFromBase64String 사용

public static bool IsBase64String(string base64)
{
   Span<byte> buffer = new Span<byte>(new byte[base64.Length]);
   return Convert.TryFromBase64String(base64, buffer , out int bytesParsed);
}


답변

예외를 포착하고 싶지 않다고 말한 것을 알고 있습니다. 그러나 예외를 잡는 것이 더 신뢰할 수 있기 때문에 계속해서이 답변을 게시하겠습니다.

public static bool IsBase64(this string base64String) {
     // Credit: oybek https://stackoverflow.com/users/794764/oybek
     if (string.IsNullOrEmpty(base64String) || base64String.Length % 4 != 0
        || base64String.Contains(" ") || base64String.Contains("\t") || base64String.Contains("\r") || base64String.Contains("\n"))
        return false;

     try{
         Convert.FromBase64String(base64String);
         return true;
     }
     catch(Exception exception){
     // Handle the exception
     }
     return false;
}

업데이트 : 신뢰성을 더욱 향상시키기 위해 oybek 덕분에 상태를 업데이트했습니다 .


답변

정규식은 다음과 같아야한다고 생각합니다.

    Regex.IsMatch(s, @"^[a-zA-Z0-9\+/]*={0,2}$")

3 개가 아닌 하나 또는 2 개의 후행 ‘=’기호 만 일치합니다.

s검사 할 문자열이어야합니다. 네임 스페이스의 Regex일부입니다 System.Text.RegularExpressions.


답변

왜 예외를 잡아서 False를 반환하지 않습니까?

이것은 일반적인 경우에 추가 오버 헤드를 방지합니다.


답변

완전성을 위해 일부 구현을 제공하고 싶습니다. 일반적으로 Regex는 비용이 많이 드는 접근 방식입니다. 특히 문자열이 큰 경우 (큰 파일을 전송할 때 발생). 다음 접근 방식은 가장 빠른 검색 방법을 먼저 시도합니다.

public static class HelperExtensions {
    // Characters that are used in base64 strings.
    private static Char[] Base64Chars = new[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' };
    /// <summary>
    /// Extension method to test whether the value is a base64 string
    /// </summary>
    /// <param name="value">Value to test</param>
    /// <returns>Boolean value, true if the string is base64, otherwise false</returns>
    public static Boolean IsBase64String(this String value) {

        // The quickest test. If the value is null or is equal to 0 it is not base64
        // Base64 string's length is always divisible by four, i.e. 8, 16, 20 etc. 
        // If it is not you can return false. Quite effective
        // Further, if it meets the above criterias, then test for spaces.
        // If it contains spaces, it is not base64
        if (value == null || value.Length == 0 || value.Length % 4 != 0
            || value.Contains(' ') || value.Contains('\t') || value.Contains('\r') || value.Contains('\n'))
            return false;

        // 98% of all non base64 values are invalidated by this time.
        var index = value.Length - 1;

        // if there is padding step back
        if (value[index] == '=')
            index--;

        // if there are two padding chars step back a second time
        if (value[index] == '=')
            index--;

        // Now traverse over characters
        // You should note that I'm not creating any copy of the existing strings, 
        // assuming that they may be quite large
        for (var i = 0; i <= index; i++)
            // If any of the character is not from the allowed list
            if (!Base64Chars.Contains(value[i]))
                // return false
                return false;

        // If we got here, then the value is a valid base64 string
        return true;
    }
}

편집하다

Sam이 제안한대로 소스 코드를 약간 변경할 수도 있습니다. 그는 테스트의 마지막 단계에 대해 더 나은 성능을 제공합니다. 루틴

    private static Boolean IsInvalid(char value) {
        var intValue = (Int32)value;

        // 1 - 9
        if (intValue >= 48 && intValue <= 57)
            return false;

        // A - Z
        if (intValue >= 65 && intValue <= 90)
            return false;

        // a - z
        if (intValue >= 97 && intValue <= 122)
            return false;

        // + or /
        return intValue != 43 && intValue != 47;
    } 

if (!Base64Chars.Contains(value[i]))줄을 다음 으로 대체하는 데 사용할 수 있습니다.if (IsInvalid(value[i]))

Sam의 향상된 기능이 포함 된 전체 소스 코드 는 다음과 같습니다 (명확성을 위해 주석 제거).

public static class HelperExtensions {
    public static Boolean IsBase64String(this String value) {
        if (value == null || value.Length == 0 || value.Length % 4 != 0
            || value.Contains(' ') || value.Contains('\t') || value.Contains('\r') || value.Contains('\n'))
            return false;
        var index = value.Length - 1;
        if (value[index] == '=')
            index--;
        if (value[index] == '=')
            index--;
        for (var i = 0; i <= index; i++)
            if (IsInvalid(value[i]))
                return false;
        return true;
    }
    // Make it private as there is the name makes no sense for an outside caller
    private static Boolean IsInvalid(char value) {
        var intValue = (Int32)value;
        if (intValue >= 48 && intValue <= 57)
            return false;
        if (intValue >= 65 && intValue <= 90)
            return false;
        if (intValue >= 97 && intValue <= 122)
            return false;
        return intValue != 43 && intValue != 47;
    }
}


답변

대답은 문자열의 사용법에 따라 달라져야합니다. 여러 포스터에서 제안한 구문에 따르면 “유효한 base64″일 수 있지만 예외없이 정크로 “올바르게”디코딩 할 수있는 문자열이 많이 있습니다. 예 : 8char 문자열Portland 은 유효한 Base64입니다. 이것이 유효한 Base64라는 점은 무엇입니까? 어느 시점에서이 문자열이 Base64로 디코딩되어야하는지 여부를 알고 싶을 것 같습니다.

제 경우에는 다음과 같은 일반 텍스트 일 ​​수있는 Oracle 연결 문자열이 있습니다.

Data source=mydb/DBNAME;User Id=Roland;Password=.....`

또는 base64에서

VXNlciBJZD1sa.....................................==

세미콜론이 있는지 확인하기 만하면됩니다. 이는 그것이 base64가 아니라는 것을 증명하기 때문입니다. 물론 위의 방법보다 빠릅니다.