Есть ли способ обратить вспять каждые 2 байта файла?

#c#

#c#

Вопрос:

Краткое изложение того, чего я пытаюсь достичь в этой идее (насколько я знаю, для выполнения того, что я делаю, нет точной функции).

Что мне нужно сделать, так это идея обратить вспять каждые 2 байта файла. Чтение байтов файла и обращение вспять каждые 2 байта.

 Example: 05 04 82 FF
Output: 04 05 FF 82
 

У меня есть некоторое представление об этом. Но я знаю, что мои попытки далеки.

Чтобы уточнить.

  1. Я пытаюсь взять файл bin.
  2. Прочитайте байты внутри файла.
  3. И переверните каждые 2 внутри этого файла и закройте его.

Если кто-нибудь может очистить этот сложный путь, это было бы здорово?

Комментарии:

1. Как вы пытались это сделать конкретно? «Чтение байтов файла и обращение вспять каждые 2 байта» звучит разумно само по себе, но детали могут пойти не так

2. @harold Сначала я использовал Array. Reverse(); для изменения байтов файла, но на самом деле это меняет ВСЕ байты файла. Что, конечно, не то, чего я хотел. Я использовал класс Binaryreader, чтобы помочь с этим. Но это ужасно не удалось, лол. Поэтому я удалил его.

3. Прочитайте два отдельных байта. Запишите каждый из них в порядке, противоположном тому, как они были прочитаны. Повторяйте, пока не дойдете до конца файла. Или прочитать весь файл, а затем обработать его по два байта за раз, меняя их местами, а затем снова записать весь файл обратно. Или прочитайте фрагмент файла (например, 1 КБ), а затем просматривайте по два байта за раз, меняя местами каждую пару байтов, а затем запишите фрагмент в новый файл и повторите процесс. Вы знаете, что выполнение того, что вы делаете, может повредить файл, верно?

4. @KenWhite я очень осведомлен. Но да, у вас есть точное представление о том, что мне нужно.

5. Похоже , вы, вероятно, имеете дело с порядковым номером UTF-16 — это текстовый файл? Честно говоря, если это так: в .NET доступны обе кодировки UTF-16 в конце; возможно, просто прочитайте это в одном и запишите в другом? Это будет не так эффективно, как потоковый подход на уровне байтов, но это будет 5 строк кода вместо 50, и, вероятно, это будет правильно с первого раза (что имеет значение)

Ответ №1:

Есть много подходов, которые вы могли бы предпринять для достижения этой цели.

Вот довольно эффективный подход к потоковой передаче с низким выделением ресурсов, использующий все символы, которые мы знаем и любим …. ArrayPool , Span<T> , и FileStream .

Примечание 1. При необходимости отрегулируйте размер буфера так, чтобы он соответствовал вашему оборудованию.

Примечание 2: здесь отсутствуют базовые проверки работоспособности и отказоустойчивости, он также с треском умрет, если размер файла не определяется 2 .

Учитывая

 private static ArrayPool<byte> pool = ArrayPool<byte>.Shared;
private const int BufferSize = 4096;

public static void Swap(string fileName)
{
   var tempFileName = Path.ChangeExtension(fileName, "bob");   
   var buffer = pool.Rent(BufferSize);

   try
   {
      var span = new Span<byte>(buffer); 
      using var oldFs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.None, BufferSize);
      using var newFs = new FileStream(tempFileName, FileMode.Create, FileAccess.Write, FileShare.None, BufferSize);

      var size = 0;
      while ((size = oldFs.Read(span)) > 0)
      {
         for (var i = 0; i < size; i  = 2)
         {
            var temp = span[i];
            span[i] = span[i   1];
            span[i   1] = temp;
         }   
         newFs.Write(span.Slice(0,size));
      }
   }
   finally
   {
      pool.Return(buffer);
   }

   File.Move(tempFileName, fileName,true);
}
 

Тест

 File.WriteAllText(@"D:Test1.txt","1234567890abcdef");
Swap(@"D:Test1.txt");

var result = File.ReadAllText(@"D:Test1.txt");

Console.WriteLine(result == "2143658709badcfe");
 

Вывод

 True
 

Тесты

Это был всего лишь простой тест, сравнивающий текущее решение с простым подходом к массиву и указателями, изменяя размер буфера, который вы могли бы сделать, чтобы увеличить пропускную способность жесткого диска. Технически это всего лишь сравнительный анализ одного прогона блока данных размером 10 МБ, однако распределение взлетит до небес, если методы будут запущены более одного раза.

Окружающая среда

 BenchmarkDotNet=v0.12.1, OS=Windows 10.0.18363.1198 (1909/November2018Update/19H2)
AMD Ryzen 9 3900X, 1 CPU, 24 logical and 12 physical cores
.NET Core SDK=5.0.100
  [Host]        : .NET Core 5.0.0 (CoreCLR 5.0.20.51904, CoreFX 5.0.20.51904), X64 RyuJIT  [AttachedDebugger]
  .NET Core 5.0 : .NET Core 5.0.0 (CoreCLR 5.0.20.51904, CoreFX 5.0.20.51904), X64 RyuJIT

Job=.NET Core 5.0  Runtime=.NET Core 5.0
 

Результаты

 |       Method |     N |        Mean |    Error |   StdDev |  Gen 0 | Gen 1 | Gen 2 | Allocated |
|------------- |------ |------------:|---------:|---------:|-------:|------:|------:|----------:|
| SwapSpanPool |  4094 |    25.89 ns | 0.078 ns | 0.069 ns |      - |     - |     - |         - |
|    SwapArray |  4094 |   157.70 ns | 0.516 ns | 0.483 ns | 0.4923 |     - |     - |    4120 B |
|   SwapUnsafe |  4094 |   154.71 ns | 0.293 ns | 0.274 ns | 0.4923 |     - |     - |    4120 B |
|------------- |------ |------------:|---------:|---------:|-------:|------:|------:|----------:|
| SwapSpanPool | 16384 |    25.82 ns | 0.048 ns | 0.043 ns |      - |     - |     - |         - |
|    SwapArray | 16384 |   520.62 ns | 1.186 ns | 1.109 ns | 1.9569 |     - |     - |   16408 B |
|   SwapUnsafe | 16384 |   518.82 ns | 1.361 ns | 1.273 ns | 1.9569 |     - |     - |   16408 B |
|------------- |------ |------------:|---------:|---------:|-------:|------:|------:|----------:|
| SwapSpanPool | 65536 |    25.81 ns | 0.049 ns | 0.043 ns |      - |     - |     - |         - |
|    SwapArray | 65536 | 1,840.41 ns | 5.792 ns | 5.418 ns | 7.8106 |     - |     - |   65560 B |
|   SwapUnsafe | 65536 | 1,846.57 ns | 3.715 ns | 3.475 ns | 7.8106 |     - |     - |   65560 B |
 

Настройка

 [MemoryDiagnoser]
[SimpleJob(RuntimeMoniker.NetCoreApp50)]
public class DumbTest
{
   private static readonly ArrayPool<byte> pool = ArrayPool<byte>.Shared;

   private MemoryStream ms1;
   private MemoryStream ms2;

   [Params(4094, 16384, 65536)] public int N;

   [GlobalSetup]
   public void Setup()
   {
      var data = new byte[10 * 1024 * 1024];
      new Random(42).NextBytes(data);
      ms1 = new MemoryStream(data);
      ms2 = new MemoryStream(new byte[10 * 1024 * 1024]);
   }

   public void SpanPool()
   {
      var buffer = pool.Rent(N);
      try
      {
         var span = new Span<byte>(buffer);

         var size = 0;
         while ((size = ms1.Read(span)) > 0)
         {
            for (var i = 0; i < size; i  = 2)
            {
               var temp = span[i];
               span[i] = span[i   1];
               span[i   1] = temp;
            }

            ms2.Write(span.Slice(0, size));
         }
      }
      finally
      {
         pool.Return(buffer);
      }
   }

   public void Array()
   {
      var buffer = new byte[N];
      var size = 0;
      while ((size = ms1.Read(buffer)) > 0)
      {
         for (var i = 0; i < size; i  = 2)
         {
            var temp = buffer[i];
            buffer[i] = buffer[i   1];
            buffer[i   1] = temp;
         }

         ms2.Write(buffer, 0, size);
      }
   }

   public unsafe void Unsafe()
   {
      var buffer = new byte[N];

      fixed (byte* p = buffer)
      {
         var size = 0;
         while ((size = ms1.Read(buffer)) > 0)
         {
            for (var i = 0; i < size; i  = 2)
            {
               var temp = buffer[i];
               p[i] = p[i   1];
               p[i   1] = temp;
            }

            ms2.Write(buffer, 0, size);
         }
      }
   }

   [Benchmark]
   public void SwapSpanPool()
   {
      SpanPool();
   }

   [Benchmark]
   public void SwapArray()
   {
      Array();
   }

   [Benchmark]
   public void SwapUnsafe()
   {
      Unsafe();
   }
}