2011-06-10 167 views
71

有没有在C#中看到一个字符串是否是Base 64编码的方法,而不是只是试图转换它,看看是否有错误?我有代码这样的代码:如何检查一个有效的Base64编码字符串

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

我要避免“一中的Base-64字符串的字符无效”的异常出现这种情况,如果值是无效的基地64串。我只想检查并返回false而不是处理异常,因为我期望有时这个值不会是基本的64字符串。在使用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); 

} 

对于那些想知道关于测试的性能VS捕捉和例外,在大多数情况下这个基础64的事情比检查异常直到你达到一定的长度检查要快。

在我非常不科学的测试中: 对于字符长度为100,000 - 110000的10000次迭代,测试速度是第一次测试的2.7倍。

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

我确定有一点可以使用基于异常的方法进行测试。我只是不知道那是什么。

+1

这取决于你想要检查的“彻底”。您可以使用正则表达式进行一些预验证,如其他人已经回答的那样,但这不是唯一的指标。 base64编码在某些情况下需要使用“=”符号进行填充。如果填充错误,即使输入与表达式匹配,也会发生错误。 – vcsjones 2011-06-10 16:41:48

+0

您的情况并不完全满足base64字符串。考虑字符串'\ n \ fLE16' - 你的方法会产生误判。对于任何人阅读和寻找一种万无一失的方法;我会建议捕捉FormatException或使用特定的RegEx,请参阅http://stackoverflow.com/questions/475074/regex-to-parse-or-validate-base64-data。 – nullable 2017-05-18 20:21:27

+0

如果上述方法返回false,如何将字符串填充到正确的长度? – 2017-07-07 09:55:36

回答

30

它很容易识别Base64字符串,因为它只会被编写的字符'A'..'Z', 'a'..'z', '0'..'9', '+', '/',它通常在最后以两个'='填充,以使长度为4的倍数。但是不要比较这些字符,如果它发生,最好忽略异常。

+1

我认为你是在正确的轨道上。我做了一些测试,看起来它是4的倍数而不是3。 – 2011-06-10 17:24:10

+1

它的长度需要是编码时的3的倍数,以便编码成功!对不起... ...是的,你是对的...编码字符串的长度是4的倍数。这就是为什么我们要填充3'='。 – 2011-06-10 17:27:24

+4

标记为正确,因为您首先提到了多重事件。如果您发现任何问题,我通过实施解决方案更新了我的问题。 – 2011-06-10 18:05:25

0

我会建议创建一个正则表达式来完成这项工作。 你必须检查这样的事情:[a-zA-Z0-9 +/=] 你还必须检查字符串的长度。我不确定这一个,但我很确定是否有东西被修剪(除了填充“=”)它会炸毁。

或更好的结算this stackoverflow question

0

当然可以。只要确保每个字符在a-z,A-Z,0-9,/+之内,并且字符串以==结尾。 (至少,这是最常见的Base64实现,你可能会发现一些实现使用与/+不同的字符作为最后两个字符)。

0

是的,因为Base64二进制数据编码成ASCII字符串使用一组有限的字符,可以简单地与该正则表达式检查:

/^ [A-ZA-Z0-9 \ = \ + \/\ s \ n] + $/s

这将确保字符串只包含AZ,az,0-9,'+','/','='和空格。

+0

这并不总是一个可靠的解释方式。 Base64在最后使用'='字符为你做一些填充。如果该填充无效,则它不是正确的base64编码,即使它与您的正则表达式匹配。您可以通过在末尾找到一个带有1或2'='的基本64字符串来演示它,然后将其删除并尝试对其进行解码。 – vcsjones 2011-06-10 16:40:15

+0

我相信OP要求陷害非法角色,如果str是合法的Base64,则不行。如果是后者,你是正确的,尽管Base64中的填充错误更容易使用异常来捕获。 – 2011-06-10 16:42:08

+0

不正确,至少base64分析器的.Net版本会完全忽略填充。 – Jay 2011-06-10 16:44:43

6

为什么不只是捕捉异常并返回False?

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

+1

这是一个不寻常的情况,我想我要去哪里使用该值更可能不是基数64,所以我宁愿避免异常的开销。以前检查要快得多。我试图将我从明文密码继承的旧系统转换为哈希值。 – 2011-06-10 17:22:55

+1

正则表达式永远不会比泰勒建议的速度快。 – 2011-06-10 17:33:29

+0

查看我的帖子底部的评论。我认为,根据您使用的字符串的长度,首先测试会更快,尤其是对于像散列密码这样的小字符串。该字符串必须是4的倍数才能进入正则表达式,然后对一个小字符串的正则表达式比对一个非常大的字符串要快。 – 2011-06-10 18:25:18

21

我知道你说过你不想发生异常。但是,因为捕捉异常更可靠,我会继续并发布这个答案。

public static bool IsBase64(this string base64String) { 
    // Credit: oybek https://stackoverflow.com/users/794764/oybek 
    if (base64String== null || base64String.Length == 0 || 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,进一步提高了可靠性。

+0

多次调用'base64String.Contains'可能导致'base64String'成为一个大字符串,导致性能不佳。 – NucS 2015-07-23 11:46:09

+0

@NucS你说得对,我们可以在这里使用一个编译好的正则表达式。 – harsimranb 2016-01-14 19:21:52

+1

你可以检查'base64String == null || base64String.Length == 0'与'string.IsNullOrEmpty(base64String)' – 2017-04-13 13:54:19

4

答案必须取决于字符串的用法。根据几个海报提出的语法,有很多字符串可能是“有效的base64”,但可能“正确”解码,无一例外地是垃圾。例如:8char字符串Portland是有效的Base64。说这是有效的Base64有什么意义?我想在某些时候你想知道这个字符串应该或不应该被Base64解码。

在我的情况,我有Oracle连接字符串可以是纯文本,如:

​​

或类似

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

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

+0

同意,具体案件也施加一些额外的快速检查。就像明文连接字符串vs base64编码一样。 – Oybek 2014-11-24 05:11:24

3

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

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

Knibb高足球规则!

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

它避免了昂贵的异常,正则表达式,也避免了通过字符集循环,而是使用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; 
    } 
0

我喜欢正则表达式检查的想法。正则表达式可以很快,并且有时节省编码开销。原来的询问,有一个更新,就是这样做的。我发现虽然,我永远不能假定字符串不会为空。我将展开扩展函数来检查源字符串是否为空字符或仅空白字符。

public static bool IsBase64String(this string s) 
    { 
     if (string.IsNullOrWhiteSpace(s)) 
      return false; 

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

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

我相信,正则表达式应该是:

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

只有匹配一个或两个尾 '=' 的标志,而不是三个。

1

我会用这样让我不需要调用转换方法再次

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

我刚刚我在哪里让用户做一些图像处理的<canvas>一个非常类似的要求元素,然后将检索结果图像.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; 
     } 
    } 
} 

正如你可以看到我期待的图像/ PNG类型的字符串,这是默认当使用.toDataURL()时由<canvas>返回。

0

Imho这不是真的可能。所有发布的解决方案都失败,如“测试”等字符串。如果它们可以通过4分割,不为空或空,并且如果它们是有效的base64字符,则它们将通过所有测试。这可能是很多字符串...

因此,除了知道这是一个基本的64编码字符串,以外,没有真正的解决方案。我想到的是这样的:

if (base64DecodedString.StartsWith("<xml>") 
{ 
    // This was really a base64 encoded string I was expecting. Yippie! 
} 
else 
{ 
    // This is gibberish. 
} 

我认为解码后的字符串以特定的结构开始,所以我检查一下。

相关问题