Как проверить действительную строку с кодировкой Base64
Есть ли способ в С#, чтобы узнать, не закодирована ли строка Base 64, кроме как просто ее конвертировать и посмотреть, есть ли ошибка? У меня есть код такой:
// Convert base64-encoded hash value into a byte array.
byte[] HashBytes = Convert.FromBase64String(Value);
Я хочу исключить исключение "Недопустимый символ в строке Base-64", если это значение недопустимо. Я хочу просто проверить и вернуть false вместо обработки исключения, потому что я ожидаю, что иногда это значение не будет базой 64. Есть ли способ проверить перед использованием функции Convert.FromBase64String?
Спасибо!
Update:
Спасибо за все ваши ответы. Вот метод расширения, который вы все можете использовать до сих пор, кажется, что ваша строка будет передавать 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);
}
Для тех, кто задается вопросом о производительности тестирования, а также об уловах и исключениях, в большинстве случаев для этой базы 64 быстрее проверять, чем перехватывать исключение, пока вы не достигнете определенной длины. Чем меньше длина, тем быстрее
В моем очень ненаучном тестировании:
Для 10000 итераций для длины символа 100 000 - 110000 вначале это было в 2,7 раза быстрее.
Для 1000 итераций для символов длиной 1 - 16 символов для общей сложности 16 000 тестов это было в 10,9 раза быстрее.
Я уверен, что есть точка, где лучше тестировать метод, основанный на исключении. Я просто не знаю, в какой момент.
Ответы
Ответ 1
Довольно легко распознать строку Base64, так как она будет состоять только из символов 'A'..'Z', 'a'..'z', '0'..'9', '+', '/'
, и она в конце концов дополняется до двух "=", чтобы сделать длину кратной 4. Но вместо этого сравнивая их, вы бы лучше проигнорировали исключение, если это произойдет.
Ответ 2
Я знаю, ты сказал, что не хотел ловить исключение. Но, поскольку ловить исключение более надежно, я отправлю этот ответ.
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;
}
Обновление: Я обновил состояние благодаря ойбеку для дальнейшего повышения надежности.
Ответ 3
Я считаю, что регулярное выражение должно быть:
Regex.IsMatch(s, @"^[a-zA-Z0-9\+/]*={0,2}$")
Соответствует только одному или двум знакам "=", а не трем.
s
должна быть строкой, которая будет проверяться. Regex
является частью пространства имен System.Text.RegularExpressions
.
Ответ 4
Почему бы просто не поймать исключение и не вернуть False?
Это позволяет избежать дополнительных накладных расходов в общем случае.
Ответ 5
Просто для полноты я хочу обеспечить некоторую реализацию.
Вообще говоря, 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 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;
}
}
Ответ 6
Ответ должен зависеть от использования строки. Существует много строк, которые могут быть "действительными base64" в соответствии с синтаксисом, предложенным несколькими плакатами, но которые могут "правильно" декодировать без исключения нежелательные сообщения. Пример: строка 8char Portland
действительна Base64. Какой смысл заявлять, что это действительно Base64? Я предполагаю, что в какой-то момент вы захотите узнать, что эта строка должна или не должна быть декодирована Base64.
В моем случае у меня есть строки соединения Oracle, которые могут быть в виде обычного текста, например:
Data source=mydb/DBNAME;User Id=Roland;Password=.....`
или в base64, например
VXNlciBJZD1sa.....................................==
Мне просто нужно проверить наличие точки с запятой, потому что это доказывает, что это НЕ base64, что, конечно, быстрее, чем любой вышеописанный метод.
Ответ 7
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;
}
Ответ 8
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;
}
}
Ответ 9
Я буду использовать это так, чтобы мне не нужно снова вызвать метод 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;
}
Ответ 10
Представленное решение не работает, см. изображение ниже.
Значение "9230" не является строкой base64, а регулярное выражение возвращает TRUE.
![enter image description here]()
Ответ 11
Я бы предложил создать регулярное выражение для выполнения задания.
Вам нужно будет проверить что-то вроде этого: [a-zA-Z0-9 +/=]
Вам также нужно будет проверить длину строки. Я не уверен в этом, но я уверен, что если что-то обрезается (кроме прокладки "=" ), он взорвется.
Или еще лучше проверить fooobar.com/questions/82598/...
Ответ 12
Конечно. Просто убедитесь, что каждый символ находится в пределах a-z
, a-z
, 0-9
, /
или +
, а строка заканчивается на ==
. (По крайней мере, это самая распространенная реализация Base64. Вы можете найти некоторые реализации, которые используют символы, отличные от /
или +
для двух последних символов.)
Ответ 13
Да, поскольку Base64 кодирует двоичные данные в строки ASCII с использованием ограниченного набора символов, вы можете просто проверить его с помощью этого регулярного выражения
/^ [A-Za-z0-9\=\+ \/\ с\п] + $/с
который гарантирует, что строка содержит только A-Z, a-z, 0-9, '+', '/', '=' и пробелы.
Ответ 14
Мне нравится идея регулярного выражения. Регулярные выражения могут быть быстрыми, и время от времени сохраняйте накладные расходы на кодирование. Первоначальный запрос, имел обновление, которое сделало именно это. Я считаю, что я никогда не могу предположить, что строки не будут равны нулю. Я бы расширил функцию Extension, чтобы проверить исходную строку для нулевых или простых пробелов.
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);
}
Ответ 15
У меня только что было очень похожее требование, когда я разрешаю пользователю делать некоторые манипуляции с изображениями в элементе <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;
}
}
}
Как вы можете видеть, я ожидаю строку типа image/png, которая по умолчанию возвращается <canvas>
при использовании .toDataURL()
.
Ответ 16
Имхо это не реально. Все опубликованные решения не работают для строк, таких как "тест" и т.д. Если их можно разделить на 4, они не являются нулевыми или пустыми, и если они являются допустимым символом base64, они пройдут все тесты. Это может быть много строк...
Таким образом, нет реального решения, кроме , зная, что это строковая кодировка с базой 64. Я придумал следующее:
if (base64DecodedString.StartsWith("<xml>")
{
// This was really a base64 encoded string I was expecting. Yippie!
}
else
{
// This is gibberish.
}
Я ожидаю, что декодированная строка начнется с определенной структуры, поэтому я проверю это.
Ответ 17
Используйте Convert.TryFromBase64String из С# 7.2
public static bool IsBase64String(string base64)
{
Span<byte> buffer = new Span<byte>(new byte[base64.Length]);
return Convert.TryFromBase64String(base64, buffer , out int bytesParsed);
}