Программа копирования файлов неправильно копирует файл

Здравствуйте

Я работал над приложением, похожим на терминал, чтобы лучше программировать на С#, просто чтобы помочь мне учиться. Я решил добавить функцию, которая будет копировать файл точно так же, как он есть, в новый файл... Кажется, он работает почти идеально. При открытии в Notepad ++ файл имеет длину всего в несколько строк и очень, очень близок к тому, что касается фактического размера файла. Однако дублированная копия файла никогда не запускается. Пишет, что файл поврежден. У меня такое ощущение, что это в методах чтения и перезаписи двоичных файлов в файлы, которые я создал. Код выглядит следующим образом, спасибо за помощь. Извините и за спагетти-код, я становлюсь немного неаккуратным, когда возюсь с новыми идеями.

Класс, который обрабатывает копирование/запись файлов

using System;
using System.IO;
//using System.Collections.Generic;
namespace ConsoleFileExplorer
{
class FileTransfer
{
    private BinaryWriter writer;
    private BinaryReader reader;
    private FileStream fsc;    // file to be duplicated
    private FileStream fsn;    // new location of file 

    int[] fileData;
    private string _file;

    public FileTransfer(String file)
    {
        _file = file;
        fsc = new FileStream(file, FileMode.Open);
        reader = new BinaryReader(fsc);
    }

    // Reads all the original files data to an array of bytes 
    public byte[] ReadAllDataToArray() 
    {
        byte[] bytes = reader.ReadBytes((int)fsc.Length); // reading bytes from the original file
        return bytes;
    }

    // writes the array of original byte data to a new file
    public void WriteDataFromArray(byte[] fileData, string path) // got a feeling this is the problem :p
    {
        fsn = new FileStream(path, FileMode.Create);
        writer = new BinaryWriter(fsn);
        int i = 0;
        while(i < fileData.Length)
        {
            writer.Write(fileData[i]);
            i++;
      }
    }
  }
}

Код, взаимодействующий с этим классом.

(Sleep(5000) вызвано тем, что я ожидал ошибки при первой попытке...

                    case '3':
                    Console.Write("Enter source file: ");
                    string sourceFile = Console.ReadLine();
                    if (sourceFile == "")
                    {
                        Console.Clear();
                        Console.ForegroundColor = ConsoleColor.DarkRed;
                        Console.Error.WriteLine("Must input a proper file path.\n");
                        Console.ForegroundColor = ConsoleColor.White;
                        Menu();
                    } else {
                        Console.WriteLine("Copying Data"); System.Threading.Thread.Sleep(5000);
                        FileTransfer trans = new FileTransfer(sourceFile);

                        //copying the original files data
                        byte[] data = trans.ReadAllDataToArray();

                        Console.Write("Enter Location to store data: ");
                        string newPath = Console.ReadLine();

                        // Just for me to make sure it doesnt exit if i forget
                        if(newPath == "")
                        {
                            Console.Clear();
                            Console.ForegroundColor = ConsoleColor.DarkRed;
                            Console.Error.WriteLine("Cannot have empty path.");
                            Console.ForegroundColor = ConsoleColor.White;
                            Menu();
                        } else
                        {
                            Console.WriteLine("Writing data to file"); System.Threading.Thread.Sleep(5000);
                            trans.WriteDataFromArray(data, newPath);
                            Console.WriteLine("File stored.");
                            Console.ReadLine();
                            Console.Clear();
                            Menu();
                        }
                    }
                break;

Файл по сравнению с новым файлом щелкните правой кнопкой мыши -> открыть в новой вкладке, вероятно, это хорошая идея

Исходный файл

Новый файл


person Corderro Artz    schedule 18.03.2016    source источник


Ответы (2)


Вы неправильно распоряжаетесь файловыми потоками и бинарным писателем. Оба имеют тенденцию к буферизации данных (что хорошо, особенно когда вы пишете по одному байту за раз). Используйте using, и ваша проблема должна исчезнуть. Если, конечно, кто-то не редактирует файл, пока вы его читаете.

BinaryReader и BinaryWriter не просто записывают "сырые данные". Они также добавляют метаданные по мере необходимости — они предназначены для сериализации и десериализации, а не для чтения и записи байтов. Теперь, в конкретном случае использования ReadBytes и Write(byte[]), в частности, это просто необработанные байты; но нет особого смысла использовать эти классы только для этого. Чтение и запись байтов — это то, что дает вам каждый Stream, включая FileStream. Нет причин использовать здесь BinaryReader/BinaryWriter - файловые потоки дают вам все, что вам нужно.

Лучшим подходом было бы просто использовать

using (var fsn = ...)
{
  fsn.Write(fileData, 0, fileData.Length);
}

или даже просто

File.WriteAllBytes(fileName, fileData);

Возможно, вы думаете, что запись по байту за раз ближе к «металлу», но это просто не так. Ни в коем случае во время этого ЦП не передает байт за раз на жесткий диск. Вместо этого жесткий диск копирует данные напрямую из ОЗУ без вмешательства ЦП. И большинство жестких дисков по-прежнему не могут записывать (или читать) произвольные объемы данных с физического носителя — вместо этого вы читаете и записываете целые сектора. Если бы система действительно записывала байт за раз, вы бы просто продолжали перезаписывать один и тот же сектор снова и снова, просто чтобы записать еще один байт.

Еще лучшим подходом было бы использовать тот факт, что у вас открыты файловые потоки, и передавать файлы от источника к месту назначения, а не сначала считывать все в память, а затем записывать обратно на диск.

person Luaan    schedule 18.03.2016
comment
Может быть, вы думаете, что запись по байту за раз ближе к железу. Забавно, я именно так и думал. Я разработал этот проект, чтобы помочь мне учиться на своих ошибках и совершенствоваться в программировании, поэтому я стараюсь делать почти все, что трудный путь... У меня есть еще один вопрос. Разве вызов Close() и Dispose() для каждого из упомянутых не будет таким же (по сути, а не механически), как принудительное использование оператора? Я хотел поставить их, но, должно быть, забыл. - person Corderro Artz; 18.03.2016
comment
@CorderroArtz Да, но using предпочтительнее, если у вас есть определенная область действия — он более устойчив (try...finally), его легко читать и понимать. Например, если бы вам нужно было держать поток открытым в поле (что не является необходимым или даже желательным в вашем случае), вы бы пометили весь свой класс как IDisposable и вызвали там соответствующий метод Dispose. И, конечно же, вы бы использовали using в экземпляре родительского класса (или, опять же, в классе, который реализует IDisposable и т. д.). Нет необходимости вызывать Close - Dispose правильно все закроет. - person Luaan; 18.03.2016

В C# есть метод File.Copy(), вы можете увидеть его здесь https://msdn.microsoft.com/ru-ru/library/c6cfw35a(v=vs.110).aspx

Если вы хотите реализовать это самостоятельно, попробуйте поставить точку останова внутри своих методов и использовать отладку. Это как сказка о рыбаке и боге, который дал рыбаку удочку - чтобы поймать рыбу, а не именно ту рыбу.

Кроме того, посмотрите на int[] fileData и byte[] fileData внутри последнего метода, возможно, это проблема.

person Alex Sh    schedule 18.03.2016