C# 如何检查有效的Base64编码字符串

C# 如何检查有效的Base64编码字符串,c#,validation,base64,C#,Validation,Base64,在C#中,除了尝试转换字符串并查看是否存在错误之外,还有什么方法可以查看字符串是否是以64为基数编码的?我有这样的代码: // Convert base64-encoded hash value into a byte array. byte[] HashBytes = Convert.FromBase64String(Value); 我希望避免在值不是有效的Base-64字符串时出现“Base-64字符串中的无效字符”异常。我只想检查并返回false,而不是处理异常,因为我希望有时候这个值不

在C#中,除了尝试转换字符串并查看是否存在错误之外,还有什么方法可以查看字符串是否是以64为基数编码的?我有这样的代码:

// Convert base64-encoded hash value into a byte array.
byte[] HashBytes = Convert.FromBase64String(Value);
我希望避免在值不是有效的Base-64字符串时出现“Base-64字符串中的无效字符”异常。我只想检查并返回false,而不是处理异常,因为我希望有时候这个值不是一个base64字符串。在使用Convert.FromBase64String函数之前,是否有方法进行检查

谢谢

更新:
谢谢你的回答。这里有一个扩展方法,到目前为止,您都可以使用它来确保字符串将毫无例外地通过Convert.FromBase64String。NET在转换为基64时似乎忽略了所有尾随和结尾空格,因此“1234”和“1234”都是有效的

对于那些想知道测试与捕获和异常的性能的人来说,在大多数情况下,对于这个基本64的东西,在达到一定长度之前,检查比捕获异常要快。长度越小,速度越快

在我非常不科学的测试中: 对于字符长度100000-110000的10000次迭代,首先测试的速度要快2.7倍

对于长度为1-16个字符的1000次迭代,总共16000次测试的速度提高了10.9倍


我确信在某种程度上,使用基于异常的方法进行测试会变得更好。我只是不知道这是什么意思。

我建议创建一个正则表达式来完成这项工作。 您必须检查以下内容:[a-zA-Z0-9+/=] 您还必须检查字符串的长度。我不确定这一点,但我很确定如果有什么东西被修剪(除了填充“=”),它会爆炸


或者最好去看看

当然。只需确保每个字符都在
a-z
a-z
0-9
/
+
范围内,并且字符串以
=
结尾。(至少,这是最常见的Base64实现。您可能会发现一些实现在最后两个字符中使用了与
/
+
不同的字符。)

更新:对于较新版本的C#,有一个更好的选择,请参考下面Tomas的答案


识别Base64字符串非常容易,因为它将只由字符组成
'a'..'Z',a'..'Z',0'..'9','+','/'
,并且通常在末尾填充多达三个'=',以使长度为4的倍数。但是,如果发生异常,您最好忽略异常,而不是比较这些异常。

是的,因为使用有限的字符集将二进制数据编码为ASCII字符串,所以您可以使用以下正则表达式进行检查:

/^[A-Za-z0-9\=\+\/\s\n]+$/s


这将确保字符串只包含A-Z、A-Z、0-9、“+”、“/”、“=”和空格。

为什么不捕获异常并返回False


这避免了常见情况下的额外开销。

我知道您说过不想捕获异常。但是,由于捕获异常更可靠,我将继续发布此答案

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

更新:为了进一步提高可靠性,我更新了条件。

答案必须取决于字符串的使用情况。根据一些海报所建议的语法,有许多字符串可能是“validbase64”,但它们可能“正确”地解码为垃圾。示例:8char字符串
Portland
是有效的Base64。说明这是有效的Base64有什么意义?我猜在某个时候,您可能想知道这个字符串应该或者不应该被Base64解码

在我的例子中,Oracle连接字符串可能是纯文本,如:

Data source=mydb/DBNAME;User Id=Roland;Password=.....`
或者像base64一样

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

我只需要检查是否存在分号,因为这证明它不是base64,这当然比上述任何方法都快。

为了完整性,我想提供一些实现。 一般来说,正则表达式是一种昂贵的方法,特别是当字符串很大时(传输大文件时会发生这种情况)。下面的方法首先尝试最快的检测方法

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

高高的足球规则

这应该是相对快速和准确的,但我承认我没有把它通过一个彻底的测试,只有几个

它避免了昂贵的异常,正则表达式,也避免了循环通过字符集,而不是使用ascii范围进行验证

public static bool IsBase64String(string s)
    {
        s = s.Trim();
        int mod4 = s.Length % 4;
        if(mod4!=0){
            return false;
        }
        int i=0;
        bool checkPadding = false;
        int paddingCount = 1;//only applies when the first is encountered.
        for(i=0;i<s.Length;i++){
            char c = s[i];
            if (checkPadding)
            {
                if (c != '=')
                {
                    return false;
                }
                paddingCount++;
                if (paddingCount > 3)
                {
                    return false;
                }
                continue;
            }
            if(c>='A' && c<='z' || c>='0' && c<='9'){
                continue;
            }
            switch(c){ 
              case '+':
              case '/':
                 continue;
              case '=': 
                 checkPadding = true;
                 continue;
            }
            return false;
        }
        //if here
        //, length was correct
        //, there were no invalid characters
        //, padding was correct
        return true;
    }
公共静态bool IsBase64String(字符串s)
{
s=s.修剪();
int mod4=s.长度%4;
如果(mod4!=0){
返回false;
}
int i=0;
bool-checkPadding=false;
int paddingCount=1;//仅在遇到第一个时应用。
对于(i=0;i=3)
{
返回false;
}
继续;
}

如果(c>='A'&&c='0'&&c我认为正则表达式应该是:

    Regex.IsMatch(s, @"^[a-zA-Z0-9\+/]*={0,2}$")
仅匹配一个或两个尾随“=”符号,而不是三个


s
应该是要检查的字符串。
Regex
System.Text.RegularExpressions
命名空间的一部分。

我将这样使用,这样就不需要再次调用convert方法

   public static bool IsBase64(this string base64String,out byte[] bytes)
    {
        bytes = null;
        // Credit: oybek http://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
        {
             bytes=Convert.FromBase64String(base64String);
            return true;
        }
        catch (Exception)
        {
            // Handle the exception
        }

        return false;
    }

我刚刚有一个非常类似的要求,我让用户在
元素中执行一些图像操作,然后发送使用
.toDataURL()检索的结果图像
到后端。我想在保存映像之前进行一些服务器验证,并使用其他答案中的一些代码实现了一个
ValidationAttribute

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
public class Bae64PngImageAttribute : ValidationAttribute
{
    public override bool IsValid(object value)
    {
        if (value == null || string.IsNullOrWhiteSpace(value as string))
            return true; // not concerned with whether or not this field is required
        var base64string = (value as string).Trim();

        // we are expecting a URL type string
        if (!base64string.StartsWith("data:image/png;base64,"))
            return false;

        base64string = base64string.Substring("data:image/png;base64,".Length);

        // match length and regular expression
        if (base64string.Length % 4 != 0 || !Regex.IsMatch(base64string, @"^[a-zA-Z0-9\+/]*={0,3}$", RegexOptions.None))
            return false;

        // finally, try to convert it to a byte array and catch exceptions
        try
        {
            byte[] converted = Convert.FromBase64String(base64string);
            return true;
        }
        catch(Exception)
        {
            return false;
        }
    }
}
正如您所看到的,我需要一个image/png类型的字符串,这是使用
.toDataURL()
Imho时
返回的默认值
public static bool IsBase64String1(string value)
        {
            if (string.IsNullOrEmpty(value))
            {
                return false;
            }
            try
            {
                Convert.FromBase64String(value);
                if (value.EndsWith("="))
                {
                    value = value.Trim();
                    int mod4 = value.Length % 4;
                    if (mod4 != 0)
                    {
                        return false;
                    }
                    return true;
                }
                else
                {

                    return false;
                }
            }
            catch (FormatException)
            {
                return false;
            }
        }
    Regex.IsMatch(s, @"^[a-zA-Z0-9\+/]*={0,2}$")
   public static bool IsBase64(this string base64String,out byte[] bytes)
    {
        bytes = null;
        // Credit: oybek http://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
        {
             bytes=Convert.FromBase64String(base64String);
            return true;
        }
        catch (Exception)
        {
            // Handle the exception
        }

        return false;
    }
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
public class Bae64PngImageAttribute : ValidationAttribute
{
    public override bool IsValid(object value)
    {
        if (value == null || string.IsNullOrWhiteSpace(value as string))
            return true; // not concerned with whether or not this field is required
        var base64string = (value as string).Trim();

        // we are expecting a URL type string
        if (!base64string.StartsWith("data:image/png;base64,"))
            return false;

        base64string = base64string.Substring("data:image/png;base64,".Length);

        // match length and regular expression
        if (base64string.Length % 4 != 0 || !Regex.IsMatch(base64string, @"^[a-zA-Z0-9\+/]*={0,3}$", RegexOptions.None))
            return false;

        // finally, try to convert it to a byte array and catch exceptions
        try
        {
            byte[] converted = Convert.FromBase64String(base64string);
            return true;
        }
        catch(Exception)
        {
            return false;
        }
    }
}
if (base64DecodedString.StartsWith("<xml>")
{
    // This was really a base64 encoded string I was expecting. Yippie!
}
else
{
    // This is gibberish.
}
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 Boolean IsBase64(this String str)
{
    if ((str.Length % 4) != 0)
    {
        return false;
    }

    //decode - encode and compare
    try
    {
        string decoded = System.Text.Encoding.UTF8.GetString(System.Convert.FromBase64String(str));
        string encoded = System.Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(decoded));
        if (str.Equals(encoded, StringComparison.InvariantCultureIgnoreCase))
        {
            return true;
        }
    }
    catch { }
    return false;
}
public bool IsBase64Encoded(String str)
{

 try

  {
    // If no exception is caught, then it is possibly a base64 encoded string
    byte[] data = Convert.FromBase64String(str);
    // The part that checks if the string was properly padded to the
    // correct length was borrowed from d@anish's solution
    return (str.Replace(" ","").Length % 4 == 0);
  }
catch
  {
    // If exception is caught, then it is not a base64 encoded string
   return false;
  }

}
    string encoded = "WW91ckJhc2U2NHN0cmluZw==";
    msgbox("Is string base64=" + IsBase64(encoded));