Как использовать Span в Convert.TryFromBase64String()?

Я пытался написать try catch за Convert.FromBase64String() и я узнал, что у него уже есть TryFromBase64String() метод. Но для этого нужно 3 аргумента:

public static bool TryFromBase64String(string s, Span<byte> bytes, out int bytesWritten);

Так как я могу использовать Span<byte> bytes там?

Я нашел это только в документах, но без надлежащего описания. Может быть, это слишком очевидно.

https://docs.microsoft.com/en-us/dotnet/api/system.convert.tryfrombase64string?view=netcore-2.1

Благодаря @Damien_The_Unbeliever и ЭТОЙ статье я узнал больше о Span, Так...

Span используется для экономии памяти и не вызывает GC так часто. Он может хранить массивы или часть массива, но я все еще не могу понять, как использовать его в этом методе.

4 ответа

Решение

Как написано в связанных вопросах, System.Span<T> это новая функция C# 7.2 (и Convert.TryFromBase64String более новая функция.NET Core)

Использовать System.Span<> Вы должны установить пакет nuget:

Install-Package System.Memory

Тогда использовать это:

byte[] buffer = new byte[((b64string.Length * 3) + 3) / 4 -
    (b64string.Length > 0 && b64string[b64string.Length - 1] == '=' ?
        b64string.Length > 1 && b64string[b64string.Length - 2] == '=' ?
            2 : 1 : 0)];

int written;
bool success = Convert.TryFromBase64String(b64string, buffer, out written);

куда b64string ваша строка base-64. Слишком сложный размер для buffer должна быть точная длина буфера на основе длины b64string,

Вот еще один подход с использованием ArrayPool, если вам нужен буфер только временно:

      // Minimum length that is sure to fit all the data.
// We don't need to be 100% accurate here,
// because ArrayPool might return a larger buffer anyway.
var length = ((value.Length * 3) + 3) / 4;
var buffer = ArrayPool<byte>.Shared.Rent(length);
try
{
    // (buffer is implicitly cast to Span<byte>)
    if (Convert.TryFromBase64String(value, buffer, out var bytesWritten))
    {
        // do something with it...
        return Encoding.UTF8.GetString(buffer, 0, bytesWritten);
    }
    throw new FormatException("Invalid base-64 sequence.");
}
finally
{
    ArrayPool<byte>.Shared.Return(buffer);
}

Вы могли бы использовать это так, используя все TryFromBase64String аргументы:

public string DecodeUtf8Base64(string input)
{
  var bytes = new Span<byte>(new byte[256]); // 256 is arbitrary

  if (!Convert.TryFromBase64String(input, bytes, out var bytesWritten))
  {
    throw new InvalidOperationException("The input is not a valid base64 string");
  }

  return Encoding.UTF8.GetString(bytes.Slice(0, bytesWritten));
}

Я использовал это так:

      string base64String = 'somebase64';

Span<byte> bytesBuffer = stackalloc byte[base64String.Length];

if (!Convert.TryFromBase64String(base64String, bytesBuffer, out int bytesWritten))
{
    return false;
}

ReadOnlySpan<byte> actualBytes = bytesBuffer[..bytesWritten];

ОБНОВЛЯТЬ:

более точный способ подсчета байтов

      const int bitsEncodedPerChar = 6; 

int bytesExpected = (base64String.Length * bitsEncodedPerChar) >> 3; // divide by 8 bits in a byte

см. https://en.wikipedia.org/wiki/Base64#Base64_table_from_RFC_4648

Другие вопросы по тегам