如何检查一个有效的Base64编码的string

在C#中有没有一种方法来查看一个string是否是Base 64编码,而不是仅仅试图转换它,看看是否有错误? 我有这样的代码:

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

我想避免“Base-64string中的无效字符”exception,如果该值不是有效的base 64string。 我想检查并返回false而不是处理exception,因为我期望有时这个值不会是一个基本的64string。 使用Convert.FromBase64String函数之前有什么方法可以检查吗?

谢谢!

更新:
感谢您的所有答案。 这是一个你可以使用的扩展方法到目前为止,似乎确保你的string将传递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); } 

对于那些想testingvs捕捉和exception性能的人来说,在大多数情况下,对于这个64位的东西来说,检查比捕捉exception要快,直到达到一定的长度。 长度越小,速度越快

在我非常不科学的testing中:对于字符长度为10万到11万次的10000次迭代,先testing快2.7倍。

对于1000个字符长度的迭代,1-16个字符对于总共16000个testing来说是10.9倍。

我相信有一个地方,用基于exception的方法进行testing会更好。 我只是不知道那是什么。

因为它只能由字符'A'..'Z', 'a'..'z', '0'..'9', '+', '/'组成,所以很容易识别Base64string'A'..'Z', 'a'..'z', '0'..'9', '+', '/' ,最后经常用两个'='填充长度,使长度为4的倍数。但是不要比较这些,如果发生exception,最好忽略exception。

我知道你说过你不想要发生exception。 但是,由于捕捉exception更可靠,我会继续发布这个答案。

 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进一步提高可靠性。

为什么不只是赶上例外,并返回False?

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

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

在我的情况下,我有Oracle连接string可能是纯文本的,如:

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

或在base64中

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

我只需要检查是否存在分号,因为这certificate它不是base64,这当然比上面的方法更快。

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

 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, ie 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所build议的那样,您还可以稍微更改源代码。 他为最后一步的testing提供了一个更好的performance方法。 例程

  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]))replaceif (!Base64Chars.Contains(value[i]))

来自Sam的完整源代码和增强function看起来是这样的(为了清晰起见删除了注释)

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

我相信这个正则expression式应该是:

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

只匹配一个或两个尾随的“=”号,而不是三个。

Knibb高足球规则!

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

它避免了昂贵的exception,正则expression式,也避免了通过字符集循环,而是使用ascii范围进行validation。

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

我会这样使用,所以我不需要再次调用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; } 

提出的解决scheme不会工作,请参阅下面的图片。

值“9230”不是base64string,正则expression式返回TRUE。

在这里输入图像说明

当然。 只要确保每个字符在azAZ0-9/ ,或+ ,并且string以==结尾。 (至less,这是最常见的Base64实现,可能会发现一些实现使用不同于/+的字符作为最后两个字符。)

是的,由于Base64使用有限的字符集将二进制数据编码为ASCIIstring,因此可以使用以下正则expression式对其进行检查:

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

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

我会build议创build一个正则expression式来完成这项工作。 你必须检查这样的事情:[a-zA-Z0-9 + / =]你还必须检查string的长度。 我不确定这一个,但我敢肯定,如果有东西被修剪(除了填充“=”)它会炸毁。

或者更好,但看看这个stackoverflow问题

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

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

我刚刚有一个非常类似的要求,我让用户在<canvas>元素中进行一些image processing,然后将检索结果图像与.toDataURL()发送到后端。 我想在保存图像之前进行一些服务器validation,并使用其他答案的一些代码实现了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 / pngtypes的string,这是在使用.toDataURL()时由<canvas>返回的默认值。

Imho这是不可能的。 所有发布的解决scheme都失败了像“testing”等string。 如果他们可以通过4进行划分,不为空或空,如果他们是有效的base64字符,他们将通过所有testing。 这可以是很多string…

所以除了知道这是一个基本的64位编码的string之外没有真正的解决scheme。 我想到的是这样的:

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

我期望解码后的string以一定的结构开始,所以我检查一下。