Эффективный способ читать и вырезать файл - PullRequest
2 голосов
/ 11 февраля 2011

Что мне нужно сделать, так это то, что у меня есть несколько файлов (txt) по 2 ГБ каждый.Мне нужно вырезать файлы, скажем, когда появляется отметка '%% XGF NEW_SET' , мне нужно создать новый файл и сохранить его.Я думаю, что этот знак появляется примерно каждые 40-50 строк.Каждая строка имеет от 4 до 20 символов.Поэтому мне нужно разрезать большие файлы на тысячи маленьких, а затем обработать их позже.Я подумал о таком примере кода.

        DirectoryInfo di = new DirectoryInfo(ConfigurationManager.AppSettings["BilixFilesDir"]);
        var files = di.GetFiles();
        int count = 0;
        bool hasObject = false;
        StringBuilder sb = new StringBuilder();
        string line = "";
        foreach (var file in files)
        {
            using (StreamReader sr = new StreamReader(file.FullName,Encoding.GetEncoding(1250)))
            {
                while ((line = sr.ReadLine()) != null)
                {
                    //when new file starts
                    if (line.Contains("%%XGF NEW_SET"))
                    {
                        //when new file existed I need to store old one
                        if (hasObject)
                        {
                            File.WriteAllText(string.Format("{0}/{1}-{2}", ConfigurationManager.AppSettings["OutputFilesDir"], count++, file.Name), sb.ToString());
                            sb.Length = 0;
                            sb.Capacity = 0;

                        }
                        //setting exist flag 
                        hasObject = true;
                    }
                    //when there is no new object
                    else
                        //when object exists adding new lines
                        if (hasObject)
                            sb.AppendLine(line);
                }
                //when all work done saving last object
                if (hasObject)
                {
                    File.WriteAllText(string.Format("{0}/{1}-{2}", ConfigurationManager.AppSettings["OutputFilesDir"], count++, file.Name), sb.ToString());
                    sb.Length = 0;
                    sb.Capacity = 0;
                }
            }
        }
    }

Так что мой пример выглядит так, но мне нужна высокая эффективность.Любые идеи, как я могу улучшить свое решение?Спасибо

Ответы [ 4 ]

2 голосов
/ 11 февраля 2011

Какая эффективность вам нужна по сравнению с тем, что дает ваш текущий код?

Лично я, вероятно, сделал бы это немного по-другому - держал бы читателя и писателя открытым все время и писал бы каждую строку, которую вы читали, если только это не строка "вырезки", в этом случае вы просто закрываете существующего писателя и начать новый. Хотя я бы не ожидал, что там будет другая эффективность.

1 голос
/ 11 февраля 2011

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

0 голосов
/ 04 марта 2011

Существует много разных способов чтения и записи файлов в .NET. Я написал тестовую программу и опубликовал результаты в своем блоге:

http://designingefficientsoftware.wordpress.com/2011/03/03/efficient-file-io-from-csharp

Я рекомендую использовать методы Windows ReadFile и WriteFile, если вам нужна производительность. Избегайте любых асинхронных методов, так как мои результаты тестов показывают, что вы получаете лучшую производительность с синхронными методами ввода-вывода - по крайней мере для FileStream, который является самым быстрым классом .NET для чтения файлов в. Я написал класс в C #, который инкапсулирует функциональность функциональность ReadFile и WriteFile, которая делает его довольно простым в использовании.

Другим интересным результатом является то, что он выглядит как чтение строк .vs. чтение данных в блоках по 65 536 байт каждый и разбивка их на строки. Оказывается, что чтение данных в блоках и последующее их разбивание на строки внутри вашей программы более эффективно. В моей загрузке есть несколько примеров того, как это сделать.

Мне бы очень понравилось, если бы вы скачали его, попробовали и сообщили об этом либо здесь, либо оставили комментарий в моем блоге, если он быстрее, чем StreamReader. Согласно моим ограниченным тестам, это значительно быстрее.

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

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

Боб Брайан MCSD

0 голосов
/ 14 февраля 2011

Спасибо за все советы.После этого я изменил свой код на что-то вроде этого:

DirectoryInfo di = new DirectoryInfo(ConfigurationManager.AppSettings["BilixFilesDir"]);
//getting all files from dir
var files = di.GetFiles();
int count = 0;
bool hasObject = false;
string line = "";
StreamWriter sw = null;
foreach (var file in files)
{
    using (StreamReader sr = new StreamReader(file.FullName, Encoding.GetEncoding(1250)))
    {
        while ((line = sr.ReadLine()) != null)
        {
            //when new file starts
            if (line.Contains("%%XGF NEW_SET"))
            {
                //when new file existed I need to store old one
                if (hasObject)
                {
                    sw.Close();
                }
                else
                {
                    //creating new file and setting exist flag
                    hasObject = true;
                    sw = new StreamWriter(string.Format("{0}/{1}-{2}", ConfigurationManager.AppSettings["OutputFilesDir"], count++, file.Name));
                    //Bill bill = new Bill();                              
                }
            }
            else
                //when object exists adding new lines
                if (hasObject)
                    sw.WriteLine(line);
        }
        //when all work done saving last object
        if (hasObject)
        {
            sw.Close();
            hasObject = false;
        }
    }
}
sw.Dispose();

Что вы думаете о чем-то подобном?

Еще одна вещь, которую мне нужно сделать: мой большойВ файле могут храниться разные документы.Все они имеют разную маркировку для запуска.Допустим, есть 20 видов документов.Иногда начинается одинаковая маркировка, но внутри документа есть дополнительные отметки, которые позволяют мне распознать тип документа.Я имею в виду, что, например, 2 документа имеют одинаковую начальную маркировку, например "%% XGF NEW_SET" , но одна имеет последнюю отметку, например "BILL_A" , а другая нет.И мне нужно создать еще один файл для каждого вырезанного файла с некоторыми индексами из документа и строкой, которая содержит тип.Поэтому, прежде чем сохранить свой StreamWriter, мне нужно извлечь все эти индексы и тип документа, как я думал о StringBuilder.Так что это следующее место, когда мне нужна эта высокая эффективность.Есть хорошие советы?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...