Чтение файлов крупного текста с потоками в C#

У меня есть прекрасная задача разработки, как обработать большие файлы, загружаемые в редактор сценариев нашего приложения (это похоже на VBA для нашего внутреннего продукта для быстрых макросов). Большинство файлов составляет приблизительно 300-400 КБ, который является прекрасной загрузкой. Но когда они идут вне 100 МБ, процессу приходится нелегко (как Вы ожидали бы).

То, что происходит, - то, что файл читают и пихают в RichTextBox, по которому затем перемещаются - не волнуются слишком много об этой части.

Разработчик, который написал первоначальный код, просто использует StreamReader и выполнение

[Reader].ReadToEnd()

который мог занять долгое время для завершения.

Моя задача состоит в том, чтобы разбить этот бит кода, считать его в блоках в буфер и показать progressbar с опцией отменить его.

Некоторые предположения:

  • Большинство файлов составит 30-40 МБ
  • Содержание файла является текстом (не двоичный), некоторые - формат Unix, некоторые - DOS.
  • Однажды содержание получен, мы разрабатываем, какой разделитель используется.
  • Ничье соответствующее, после того как это загрузило время, которое требуется для рендеринга в richtextbox. Это - просто начальная загрузка текста.

Теперь для вопросов:

  • Я могу просто использовать StreamReader, затем проверить свойство Length (так ProgressMax) и выпустить Чтение для размера буфера набора и выполнить итерации через в некоторое время цикле, ПОКА во второстепенном рабочем, таким образом, это не блокирует основной поток UI? Затем возвратите stringbuilder основному потоку, после того как он завершается.
  • Содержание будет идти в StringBuilder., я могу инициализировать StringBuilder с размером потока, если длина доступна?

Это (по Вашим профессиональным мнениям) хорошие идеи? У меня было несколько проблем в прошлом с чтением содержания от Потоков, потому что оно будет всегда пропускать последние несколько байтов или что-то, но я задам другой вопрос, если это верно.

89
задан Peter Mortensen 28 June 2015 в 21:59
поделиться

6 ответов

Вы говорите, что вам попросили показать панель прогресса, пока загружается большой файл. Это потому, что пользователи действительно хотят видеть точные% загрузки файла или только потому, что они хотят, чтобы они хотели, чтобы что-то происходит?

Если последнее верно, то решение становится намного проще. Просто сделайте Reader.readteend () на фоновой резьбе и отобразить панель прогресса типа шарвита вместо правильного.

Я поднимаю эту точку, потому что в моем опыте это часто бывает так. Когда вы пишете программу обработки данных, пользователи определенно будут заинтересованы в полной фигуре%, но для простых новых обновлений пользовательского интерфейса они чаще просто хотят знать, что компьютер не разбился. : -)

15
ответ дан 24 November 2019 в 07:12
поделиться

ответ baretta решил k __ раздувание BackingField для меня. Просто крошечное дополнение, которое вы можете украсить этот класс для автоматической сериализации в XML или JSON аналогичным образом:

[Serializable, XmlRoot, DataContract]
public class Cat
{
  [XmlElement]
  [DataMember]
  public string Name { get; set; }
  [XmlElement]
  [DataMember]
  public string Breed { get; set; }
}

... а затем используйте DataContractJsonSerializer или GroupSerializer, чтобы подготовить его для конечной точки.

-121--1062122-

Этого должно быть достаточно для начала работы.

class Program
{        
    static void Main(String[] args)
    {
        const int bufferSize = 1024;

        var sb = new StringBuilder();
        var buffer = new Char[bufferSize];
        var length = 0L;
        var totalRead = 0L;
        var count = bufferSize; 

        using (var sr = new StreamReader(@"C:\Temp\file.txt"))
        {
            length = sr.BaseStream.Length;               
            while (count > 0)
            {                    
                count = sr.Read(buffer, 0, bufferSize);
                sb.Append(buffer, 0, count);
                totalRead += count;
            }                
        }

        Console.ReadKey();
    }
}
5
ответ дан 24 November 2019 в 07:12
поделиться

Посмотрите на следующий фрагмент кода. Вы упомянули Большинство файлов составит 30-40 МБ . Это претензию на прочесть 180 МБ за 1,4 секунды на четырехъядерном ядре Intel:

private int _bufferSize = 16384;

private void ReadFile(string filename)
{
    StringBuilder stringBuilder = new StringBuilder();
    FileStream fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read);

    using (StreamReader streamReader = new StreamReader(fileStream))
    {
        char[] fileContents = new char[_bufferSize];
        int charsRead = streamReader.Read(fileContents, 0, _bufferSize);

        // Can't do much with 0 bytes
        if (charsRead == 0)
            throw new Exception("File is 0 bytes");

        while (charsRead > 0)
        {
            stringBuilder.Append(fileContents);
            charsRead = streamReader.Read(fileContents, 0, _bufferSize);
        }
    }
}

Оригинальная статья

4
ответ дан 24 November 2019 в 07:12
поделиться

Возможно, вам лучше использовать файлы с отображением на карту памяти, обрабатывающие здесь ... Поддержка файлов с отображением в памяти будет в .NET 4 (я думаю...я слышал это через кого-то другого, кто говорил об этом), поэтому эта обертка, которая использует p/invokes для выполнения той же самой работы...

Правка: Смотрите здесь, на MSDN, как это работает, вот запись блога , указывающая, как это делается в грядущем .NET 4, когда он выйдет в качестве релиза. Ссылка, которую я дал ранее, является оберткой вокруг pinvoke для достижения этой цели. Вы можете отобразить весь файл в памяти и просматривать его как скользящее окно при прокрутке файла.

3
ответ дан 24 November 2019 в 07:12
поделиться

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

Я просто интересовался, возможно ли это для замены загрузчика (исполняемая программа загрузчик не загрузчик) Операционная система (Windows - моя выбор).

Нет, в процессе создания окон загрузчик пользовательского режима в ntdll связаны друг с другом (PsCreateProcess будет непосредственно отображаться в ntdll и переходить к нему, чтобы он мог завершить разрешение модулей и настройку процесса), вы не можете заменить его.

, но имеются доступные ресурсы, описывающие формат и загрузку процессов.

Вот довольно старая, но все еще актуальная статья MSDN относительно файлов PE (exe + dll)

  1. Первая часть. Всесторонний Взгляд в Портативный Исполняемый файл Win32 Формат Мэтта Пьетрека (MSDN Журнал, февраль 2002)
  2. вторая часть. Всесторонний взгляд в портативный исполняемый файл Win32 Формат Мэтта Пьетрека (MSDN Журнал, март 2002)

Эту информацию можно использовать для создания приложения, запускающего данный исполняемый файл.

Если вас больше интересует linux и эльфийский формат, вы найдете все необходимое в google.

Есть ли способ, с помощью которого я могу получить контроль над загрузчиком ОС? Я имею в виду, я хочу то, что он делает с быть видимым для меня (каждый шаг).

В Windows можно получить некоторое представление о загрузчике при работе, включив функцию «Привязка загрузчика». Это можно сделать с помощью gflags.exe (часть инструментов отладки для Windows). Есть хороший gflags.exe ссылка http://www.osronline.com/DDKx/ddtools/gflags_4n77.htm . Если включена функция «Показать снимки загрузчика», можно просмотреть сообщения трассировки загрузчика, запустив приложение под отладчиком (WinDBG).

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

Загрузчик является частью kernal - но, поскольку у вас есть доступ ко всему материнскому источнику, вы можете играть с ним до ваших сердец содержание.

Загрузчики для различных двоичных форматов находятся в fs/binfmt _ * .c в источнике Linux ( fs/binfmt _ elf.c - загрузчик, используемый для исполняемых файлов в формате ELF - т. е. подавляющее большинство).

Динамический загрузчик /lib {, 64 }/ld-linux.so.2 также используется для динамически связанных двоичных файлов - это пример «интерпретатора», на который ссылается код в binfmt_elf.c.

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

Модуль binfmt _ misc позволяет записывать пользовательские загрузчики для исполняемых программ полностью в пользовательском пространстве; обычно используется для выполнения неродных двоичных файлов или интерпретируемых двоичных файлов, таких как Java, исполняемые файлы CLR и т.д.

С другой стороны, если вы хотите заменить загрузчик ELF чем-то другим, вы можете создать модуль binfmt непосредственно в ядре.Примеры см. в разделе fs/binfmt _ * . Сам ЭЛФ-погрузчик там.

-121--4213447-

Некоторые распространенные применения можно найти в Literals класса в качестве токенов типа среды выполнения .

-121--1016082-

Используйте фоновый рабочий и прочитайте только ограниченное число строк. Дополнительная информация доступна только при прокрутке пользователем.

И постарайтесь никогда не использовать ReadToEnd (). Это одна из функций, которые вы думаете, «почему они сделали это?»; это помощник по сценарию ", который хорошо справляется с мелочами, но, как видите, он отстой для больших файлов...

Те ребята, которые говорят вам использовать StringBuilder, должны чаще читать MSDN:

Соображения по производительности
Методы Concat и AppendFormat соединяют новые данные с существующим объектом String или StringBuilder. Операция конкатенации последовательностей всегда создает новый объект из существующей последовательности и новых данных. Объект StringBuilder поддерживает буфер для согласования новых данных. Новые данные добавляются в конец буфера, если имеется комната; в противном случае выделяется новый больший буфер, данные из исходного буфера копируются в новый буфер, затем новые данные добавляются в новый буфер. Производительность операции конкатенации для объекта String или StringBuilder зависит от частоты выделения памяти.
Операция конкатенации Последовательности всегда выделяет память, в то время как операция конкатенации StringBuilder выделяет память только в том случае, если буфер объекта StringBuilder слишком мал для размещения новых данных. Следовательно, класс String предпочтительнее для операции конкатенации, если объединено фиксированное число последовательностей. В этом случае отдельные операции конкатенации могут даже объединяться компилятором в одну операцию. Объект StringBuilder предпочтителен для операции конкатенации, если сцеплено произвольное число последовательностей; например, если цикл объединяет случайное число последовательностей пользовательского ввода.

Это означает огромное выделение памяти, то, что становится большим использованием системы файлов подкачки, которая имитирует секции жесткого диска, чтобы действовать как память ОЗУ, но жесткий диск очень медленный.

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

6
ответ дан 24 November 2019 в 07:12
поделиться

Итератор может быть идеальным для этого типа работы:

public static IEnumerable<int> LoadFileWithProgress(string filename, StringBuilder stringData)
{
    const int charBufferSize = 4096;
    using (FileStream fs = File.OpenRead(filename))
    {
        using (BinaryReader br = new BinaryReader(fs))
        {
            long length = fs.Length;
            int numberOfChunks = Convert.ToInt32((length / charBufferSize)) + 1;
            double iter = 100 / Convert.ToDouble(numberOfChunks);
            double currentIter = 0;
            yield return Convert.ToInt32(currentIter);
            while (true)
            {
                char[] buffer = br.ReadChars(charBufferSize);
                if (buffer.Length == 0) break;
                stringData.Append(buffer);
                currentIter += iter;
                yield return Convert.ToInt32(currentIter);
            }
        }
    }
}

Вы можете вызвать его с помощью следующее:

string filename = "C:\\myfile.txt";
StringBuilder sb = new StringBuilder();
foreach (int progress in LoadFileWithProgress(filename, sb))
{
    // Update your progress counter here!
}
string fileData = sb.ToString();

По мере загрузки файла итератор будет возвращать номер выполнения от 0 до 100, который вы можете использовать для обновления индикатора выполнения. После завершения цикла StringBuilder будет содержать содержимое текстового файла.

Кроме того, поскольку вам нужен текст, мы можем просто использовать BinaryReader для чтения символов, что обеспечит правильное выравнивание ваших буферов при чтении любых многобайтовых символов ( UTF-8 , UTF-16 и т. Д.).

Все это делается без использования фоновых задач, потоков или сложных настраиваемых конечных автоматов.

1
ответ дан 24 November 2019 в 07:12
поделиться
Другие вопросы по тегам:

Похожие вопросы: