Как проверить правильность строки в кодировке Base64
есть ли способ в C# увидеть, закодирована ли строка Base 64, кроме как просто попытаться преобразовать ее и посмотреть, есть ли ошибка? У меня есть такой код:
// Convert base64-encoded hash value into a byte array.
byte[] HashBytes = Convert.FromBase64String(Value);
Я хочу избежать исключения" недопустимый символ в строке Base-64", которое происходит, если значение не является допустимой строкой base 64. Я хочу просто проверить и вернуть false вместо обработки исключения, потому что я ожидаю, что иногда это значение не будет строкой base 64. Есть ли способ проверить перед использованием преобразования.Функция FromBase64String?
спасибо!
обновление:
Спасибо за все ваши ответы. Вот метод расширения, который вы можете использовать до сих пор, похоже, чтобы убедиться, что ваша строка пройдет преобразование.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);
}
для тех, кто интересуется производительностью тестирование против ловли и исключения, в большинстве случаев для этой базовой 64 вещи быстрее проверить, чем поймать исключение, пока вы не достигнете определенной длины. Чем меньше длина быстрее это
в моем очень ненаучном тестировании:
Для 10000 итераций для длины символов 100,000-110000 это было в 2,7 раза быстрее, чтобы проверить первый.
для 1000 итераций для символов длиной 1-16 символов в общей сложности 16 000 тестов это было в 10,9 раза быстрее.
Я уверен существует точка, где становится лучше протестировать метод на основе исключения. Я просто не знаю в какой момент это.
15 ответов:
довольно легко распознать строку Base64, так как она будет состоять только из символов
'A'..'Z', 'a'..'z', '0'..'9', '+', '/'и он часто дополняется в конце до двух'=', чтобы сделать длину кратной 4. Но вместо того, чтобы сравнивать их, вам было бы лучше игнорировать исключение, если оно произойдет.
Я знаю, что вы сказали, что не хотите поймать исключение. Но, поскольку поймать исключение более надежно, я пойду вперед и опубликую этот ответ.
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; }обновление: я обновил состояние благодаря Ойбек для дальнейшего повышения надежности.
Я считаю, что регулярное выражение должно быть:
Regex.IsMatch(s, @"^[a-zA-Z0-9\+/]*={0,2}$")только соответствие один или два трейлинг ' = ' знаки, а не три.
sдолжна быть строка, которая будет проверяться.Regexявляется частьюSystem.Text.RegularExpressionsпространство имен.
Почему бы просто не поймать исключение и не вернуть False?
Это позволяет избежать дополнительных накладных расходов в общем случае.
просто ради полноты я хочу обеспечить некоторую реализацию. Вообще говоря, регулярное выражение-это дорогостоящий подход, особенно если строка большая (что происходит при передаче больших файлов). Следующий подход сначала пробует самые быстрые способы обнаружения.
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; } }EDIT
как было предложено Сэм, вы также можете немного изменить исходный код. Он обеспечивает более эффективный подход для последнего шага тесты. Рутина
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]))полный исходный код с аксессуарами от Сэм будет выглядеть так (удалены комментарии для ясности)
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, что, конечно, быстрее, чем любой выше метод.
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; }
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; } }
Я буду использовать так, чтобы мне не нужно было снова вызывать метод 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; }
конечно. Просто убедитесь, что каждый символ находится в пределах
a-z,A-Z,0-9,/или+и заканчивается строкой==. (По крайней мере, это наиболее распространенная реализация Base64. Вы можете найти некоторые реализации, которые используют символы, отличные от/или+для последних двух символов.)
да, начиная с Base64 двоичные данные в строку ASCII, используя ограниченный набор символов, вы можете просто проверить его с помощью этого регулярного выражения:
/ ^[A-Za-z0-9\=\+\/\s\n]+$/s
что гарантирует, что строка содержит только A-Z, a-z, 0-9, '+', '/', '=', и пробелы.
Я бы предложил создать регулярное выражение для выполнения этой работы. Вам нужно будет проверить что-то вроде этого: [a-zA-Z0-9+ / =] Вы также должны будете проверить длину строки. Я не уверен в этом, но я уверен, что если что-то будет обрезано (кроме заполнения"="), это взорвется.
или еще лучше проверить этот вопрос stackoverflow
Мне нравится идея проверки регулярного выражения. Регулярные выражения могут быть быстрыми и иногда экономить накладные расходы на кодирование. в исходном запросе было обновление, которое сделало именно это. Я нахожу, однако, что я никогда не могу предположить, что строки не будут null. Я бы развернул функцию расширения, чтобы проверить исходную строку на null или только пробелы.
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); }
у меня только что было очень похожее требование, когда я позволяю пользователю делать некоторые манипуляции с изображениями в
<canvas>элемент и затем отправка результирующего изображения, полученного с помощью.toDataURL()к backend. Я хотел сделать некоторую проверку сервера перед сохранением изображения и реализовал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, которая по умолчанию возвращается
<canvas>при использовании.toDataURL().
имхо это на самом деле не возможно. все решения размещены не для строк типа "тест" и так далее. Если они могут быть разделены через 4, не являются нулевыми или пустыми, и если они являются допустимым символом base64, они пройдут все тесты. Это может быть много строк ...
таким образом, нет никакого реального решения, кроме зная, что это в кодировке base 64 строку. Вот что я придумал:
if (base64DecodedString.StartsWith("<xml>") { // This was really a base64 encoded string I was expecting. Yippie! } else { // This is gibberish. }Я ожидаю, что расшифровали строка начинается с определенной структуры, поэтому я проверяю это.
Comments