Страницы

Поиск по вопросам

вторник, 31 декабря 2019 г.

Текст из файла в строку

#c_sharp


Программа должна считывать из текстового файла (.txt) весь текст и записывать его
в массив строк по 100 символов. Файлы большого объема (от 10 Мбайт). Использую File.ReadAllText
для загона всего текста в одну строку, а потом ее бью на части и загоняю в массив строк.
Интерфейс программы сразу же "не отвечает". Можно это сделать как-то быстрее и проще?
Или тогда лучше использовать async/await?
    


Ответы

Ответ 1



Вот, отличное решение, через метод-расширение: public static class StringExtensions { public static IEnumerable SplitChunk(this string s, int chunkSize) { int chunkCount = s.Length / chunkSize; for (int i = 0; i < chunkCount; i++) yield return s.Substring(i * chunkSize, chunkSize); if (chunkSize * chunkCount < s.Length) yield return s.Substring(chunkSize * chunkCount); } } private async Task> GetAllStrings(int chunkSize, string filePath) { using (var sr = File.OpenText(filePath)) { var rawText = await sr.ReadToEndAsync(); var text = rawText.Replace(Environment.NewLine, ""); return text.SplitChunk(chunkSize).ToList(); } } Использование: var listChunk = await GetAllStrings(100,"c:\\temp\\2.txt");

Ответ 2



Ещё одно улучшение вдвое по скорости, за счёт увеличения буфера до 4К: List SplitByChars(int chunkSize, string path) { List result = new List(); using (var f = File.OpenText(path)) { var buffer = new char[4096]; StringBuilder currentLine = new StringBuilder(chunkSize); int readCount; do { readCount = f.ReadBlock(buffer, 0, buffer.Length); for (int i = 0; i < readCount; i++) { char c = buffer[i]; if (c == '\r' || c == '\n') continue; currentLine.Append(c); if (currentLine.Length == chunkSize) { result.Add(currentLine.ToString()); currentLine.Clear(); } } } while (readCount > 0); if (currentLine.Length > 0) result.Add(currentLine.ToString()); } return result; } Старый вариант: IEnumerable SplitByChars(int chunkSize, string path) { List result = new List(); using (var f = File.OpenText(path)) { var buffer = new char[chunkSize]; StringBuilder currentLine = new StringBuilder(chunkSize); int readCount; do { var oldLength = currentLine.Length; var rest = chunkSize - currentLine.Length; readCount = f.ReadBlock(buffer, 0, rest); currentLine.Append(buffer, 0, rest); currentLine.Replace("\r", "", oldLength, rest); currentLine.Replace("\n", "", oldLength, currentLine.Length - oldLength); if ((readCount == 0 && currentLine.Length > 0) || currentLine.Length == chunkSize) { result.Add(currentLine.ToString()); currentLine.Clear(); } } while (readCount > 0); } return result; } Я старался не грузить память чтением длинных строк, максимальное потребление памяти — один буфер размером в chunkSize и один StringBuilder с capacity в chunkSize. Пользоваться так: var result = await Task.Run(() => SplitByChars(100, path)); Предыдущий вариант с ReadBlockAsync был примерно втрое медленнее. Здесь слишком много работы «вручную», возможно, есть более изящное решение.

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

Отправить комментарий