1. C# / Говнокод #11621

    +135

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    public int getFileRowsCount(string pathToFile)
    {
           System.IO.TextReader streamReader = new System.IO.StreamReader(pathToFile);
           int rowsCounter = 0;
           while ((streamReader.ReadLine()) != null)
           {
               rowsCounter++;
           }
           streamReader.Close();
           return rowsCounter;
    }

    Из http://habrahabr.ru/post/149877/
    И коммент афтора - "Здесь всё просто: пока не дойдём до пустой строки, прибавляем к счётчику строк единичку. Функция возвращает количество строк."

    Запостил: phoenixx, 20 Августа 2012

    Комментарии (62) RSS

    • А ты как предлагаешь?
      Ответить
      • Глупо как-то вычитывать весь файл в память, чтобы посчитать кол-во переносов.
        Правильней было бы вычитывать поблочно, подсчитывая кол-во \r или \n.
        Ответить
        • Уточню, так как коммент получился двусмысленным. Проблема кода, в том что после вычитывания файла, весь файл останется в памяти в ввиде многих неиспользуемых string-ов, пока GC по ним не пройдется. Смысла в этом мало.
          И, как по мне, правильным подходом было бы вычитывание блоков byte, проверка есть ли в них \r либо \n, заполнение новыми данными того же блока, проверка, и так по кругу.
          Ответить
          • Посимвольно до eof, сосчитать '\n'.
            Ответить
            • Use LINQ, guys:
              File.ReadAllLines(pathToFile).Count()
              Ответить
              • вот это загрузит всё в память. и это ниразу не LINQ-way: тут ниодной лямбды.
                Ответить
              • У вас ус отклеился.
                Ответить
              • Почти.

                File.ReadAllLines вернёт массив строк - string[]. Т. е. в памяти будет держаться весь файл.

                Нужно использовать File.ReadLines - это вернёт IEnumerable<string>. Т. е. чтение будет ленивое, в памяти будет держаться всего по одной строке.
                Ответить
                • Так а разница в чем в данном случае? Всеравно все строки нужно перебрать, и все они так же само будут в памяти.
                  Ответить
                  • В первом случае массив строк будет храниться в памяти целиком, пока к нему не будет вызван Count, и лишь после этого массив может удалить GC. Таким образом, если файл большой - несколько гигабайт - то может банально не хватить памяти.

                    Во втором случае памяти хватит гарантированно - сработает GC и удалит ставшие ненужными строки. А удаление короткоживущего объекта очень дёшево.
                    Ответить
                    • Да, исходя из теста GC очень неплох для мелких обьектов. Я раньше думал что это не так, спасибо за пояснение.
                      Ответить
                      • Так это ты меня тута минусовал
                        http://govnokod.ru/11619#comment151397

                        Эх. Любители "оптимизаций" для GC. Смешнее только пыхапешник экономящий на спичках.
                        Кстати алок объекта в современных VM - 10 машинных инструкций, что совсем немного.
                        Ответить
                        • Я не минусую комменты, которые относятся к теме. В отличии от всяких "первыхнах", "+10050", и подобных.
                          Ответить
                • ReadLines так же вызывает reader.ReadLine(), только лениво, и каждый раз создаётся новый экземпляр строки, и он же и возвращяется
                  Ответить
                  • Предлагаю не страдать словоблудием, а взять достаточно большой файл. Написать код для замера производительности. И поделиться с нами результатами эксперимента во всех приведенных в этом топике вариантах (оригинал, ваш код с буфером и вариант с ReadLines).

                    P.S. GC для мелких короткоживущих объектов крайне эффективен. Чуть-чуть медленнее стековых переменных ;) Фишка в том, что new это тупой инкремент с проверкой оставшейся памяти, а при сборке мусора копирующий GC будет "спасать" только живые объекты, а кучи погибших строчек он даже не заметит.
                    Ответить
                    • Дано - файл на 147мб текста в ASCII с 1 505 233 строками
                      Замерял разницу в памяти, и время выполнения, каждый тест выполнялся отдельно от остальных.

                      Мой вариант с буфером - 556kb и 2994ms
                      Оригинал - 4820kb и 1087ms
                      Вариант с ReadLines(some).Count() - 5496kb и 1113ms

                      Вариант с буфером менее использует память, но и самый медленный. Походу для большинства задач ReadLines(..) должно хватить с головой.
                      Ответить
                      • Я не понял. Где твой вариант с буфером? Запости свой вариант с буфером.
                        Ответить
                        • Ниже он его постил.
                          Ответить
                        • А вообще вариант с ReadLines(some).Count() - 5496kb и 1113ms лучший пока из тех, что здесь запостили. Не смотря на как-бы большое потребление памяти, фактически потребление памяти очень низкое и если строки в файле не будут слишком длинные, то этот вариант запуститься фактически на любом компьютере с любым количеством памяти. (Оговорюсь: допускающим нормальную работу среды .NET и программ для этой среды.) и при работе с любыми файлами.

                          Более того это самый короткий вариант.
                          Ответить
        • тут всё правильно сделано: происходит поточное чтение, строка ниразу не сохраняется на стеке и сразу отправляется в GC.
          ReadLine() уже читает поток пока не встретит Environment.NewLine
          Ответить
          • "и сразу отправляется в GC" она останется жить в памяти до следующей сборки. Каждая строчка файла останется жить до сборки, что в общем-то глупо.
            Да и зачем, если можно сделать это используя один набор byte[]
            Ответить
            • и как будет выглядеть код лучше с вашей точки зрения?
              Ответить
              • Я уже описал мое предложение пару комментами выше - http://govnokod.ru/11621#comment151430
                Ответить
                • я имею в виду в виде кода
                  Ответить
                  • К примеру так:

                    public static int GetFilesLinesCount(string fileName)
                    {
                    	const byte RDelimiter = (byte)'\r';
                    	const byte NDelimiter = (byte)'\n';
                    	const int BufferSize = 4096;
                    	int count = 0;
                    	bool ignoreNext = false;
                    	using (var reader = new StreamReader(fileName, Encoding.Default, true, BufferSize))
                    	{
                    		while(!reader.EndOfStream)
                    		{ 
                    			var symb = reader.Read();
                    			if (ignoreNext)
                    			{
                    				ignoreNext = false;
                    				continue;
                    			}
                    
                    			if (symb == RDelimiter || symb == NDelimiter)
                    			{
                    				count++;
                    				ignoreNext = true;
                    			}
                    		}
                    	}
                    	return count;
                    }


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

                    Да, здесь могут быть еще нюансы, если в файле идут только \r разделители, и два переноса идут подряд, то тогда подсчет будет неправильным. Но ведь это только сэмпл.
                    Ответить
                    • т.е. "\n\n\n\n" - это джве строки?
                      Ответить
                      • да :) я добавил сноску в конце коммента :)
                        Ответить
                        • > Но ведь это только сэмпл.
                          прототип? но это наш продукт?
                          Ответить
                          • Это просто пример как правильней считать переносы в файле в отличии от оригинала. Он не идеальный, так как сделан на коленке, и в нём не учитываются подряд \n. Так же не понятно как он будет определять Encoding. В теории StreamReader должен определять сам правильный Encoding, но я не проверял.
                            Ответить
                            • но это именно абсолютно наш продукт, который будет выпускаться на наших предприятиях?
                              Ответить
                            • >> тогда подсчет будет неправильным
                              > пример как правильней считать переносы в файле
                              > в отличии от оригинала
                              Вот в оригинале-то пока что правильней (что и ваш тест выше подтвердил)...
                              Ответить
                              • Оригинал - нет, так как там может вывалиться ошибка и StreamReader не закроется.

                                А вот в сравнении с ReadLines - наверно. Но я бы сказал это зависит от задачи. Если нужно перебирать N гиговые текстовые файлы, и это может быть узким местом, то что мой вариант, что ReadLines могут не подойти.
                                Ответить
                    • >using (var reader = new StreamReader
                      Плюсую вот за это.
                      Ответить
                    • Одна строка линка заменяет этот код. Удаление короткоживущих объектов (строк) - очень дешёвая операция. До поколения 1 они не доживут.

                      Имхо, такую портянку кода следует писать лишь в том случае, если профайлер покажет, что затык именно в нагрузке на сборщик мусора. Уверен на 99,99% что проблем с этим не будет. Поэтому лучше потратить время на оптимизацию действительно проблемного куска кода (по результатам профайлера).
                      Ответить
                      • >>>Одна строка линка заменяет этот код. Удаление короткоживущих объектов (строк) - очень дешёвая операция.
                        Ну наконец-то здравое мнение ИТТ.

                        >>Нужно использовать File.ReadLines - это вернёт IEnumerable<string>.
                        Да я так и хотел. Какой же LINQ без IEnumerable?
                        Ответить
          • "пока не встретит Environment.NewLine". Вообще-то там в стримридере case на \r и \n. Environment.NewLine там вообще не используется, ибо там два символа, а последовательность \r\n в зависимости от OS в которой создан этот файл может отличаться.
            Ответить
      • ПРИШЛО ВРЕМЯ ЗАКРЫВАТЬ StreamЯeader
        StreamЯeader САМ НЕ ЗАКРОЕТСЯ
        ЗАКРОЙ ЕГО, ЗАКРОЙ ЕГО ЕЩЕ РАЗ
        ЗАЧЕМ МНЕ НУЖЕН FINALLY, У МЕНЯ НЕТ ВРЕМЕНИ ЧТОБЫ ПИСАТЬ ЕГО
        ЛУЧШЕ ЕЩЕ РАЗ ЗАКРЫТЬ StreamЯeader
        Я ЗАКРЫВАЮ StreamЯeader ПО 3 РАЗА В ДЕНЬ
        КАЖДЫЙ ЭКСЕПШЕН И ОН ЗАНИМАЕТ ЦЕННЫЕ РЕСУРСЫ
        Я ЖИВУ АКТИВНОЙ И ПОЛНОЦЕННОЙ ЖИЗНЬЮ
        Я УСПЕШЕН И ПОЭТОМУ ЦЕЛЫЙ ДЕНЬ ПИШУ ГОВНО
        А ПОСЛЕ ЭТОГО ЗАКРЫВАЮ StreamЯeader
        ТУПЫЕ ЖАВИСТЫ ОДЕРЖИМЫ CHECKED EXCEPTIONАМИ
        А Я СВОБОДНЫЙ ОТ ЗАДРОТСТВО ЧЕЛОВЕК
        СКОЧАТЬ БЕЗПЛАТНО РУССКАЯ ВИЗУАЛ СТУДИЯ
        PDF СИШАРП ЗА 21 ДЕНЬ
        ЛУЧШЕ Я ЗАКРОЮ ЕЩЕ РАЗ StreamЯeader
        СТАБИЛЬНОСТЬ НЕ НУЖНА
        Я НЕ ЗАКРЫВАЛ StreamЯeader НЕДЕЛЮ
        ПОЙДУ ЗАКРОЮ
        В С# ВСЕ ПРОСТО И ПОНЯТНО
        ОШИБКА STOP 0x0000000A. НЕПОНЯТНО ПОЧЕМУ ОНО ВЫЛЕЗЛО
        ПРИШЛО ВРЕМЯ ЗАКРЫВАТЬ StreamЯeader
        ККОКОКОКОКОКОКО
        C#.NET LINQ ПИТУХИ
        КОКОКОКОКОКОКО
        Ответить
        • Больше капса! больше эмоций! :)
          Ответить
          • Тут не хватает ИТАЛИКА И КРАСНОГО ЦВЕТА!
            Ответить
            • Сейчас будут тебе италик с красным, парень.
              Пользователь krypt получает предупреждение согласно правил:
              Цитата:
              Неуместное использование красного цвета обычными пользователями запрещено.
              Данное право закрепляется исключительно за модераторcким составом.
              
              Ответить
              • Больше кгови для кговавого б-га.
                Ответить
              • Здесь нет еды
                Ответить
              • О, страйко заимплементил нормальное цитирование. Ура, товарищи!
                Цитата:

                Пользователь krypt получает предупреждение согласно правил:
                Имхо, "согласно правил" как-то не по русски звучит. Согласно правилам, или в соответствии с правилами.
                Ответить
                • >как-то не по русски
                  Тоже как-то не по-русски
                  Ответить
              • Мочерация, LINQ... Блин я аж прослезился с ностальгии. Раньше гк был лучше. Сейчас-то что? Споры с 3_14dарами, бампы и прочая чушь.
                Ответить
                • >Споры с 3_14dарами
                  Самокритичненько Главпетух, наджаву, 3.14159265дар!
                  Ответить
        • У 3.14159265дара еще в 2012 бомбило от того, что люди не юзают using. как же так, у него его нет, у них нет и они его не юзают.
          Ответить
    • Типичное нубское говно шарписта.
      Кстати посмотрев на высеры до-диезников я понимаю почему в жабе ввели крайне неудобные для нормальных людей checked exceptions.

      Да и вообще в мире C# так не принято считать. Есть же LINQ.
      Ответить
      • выпад в сторону LINQ не засчитан: аргументы отсутствуют.
        у well-known exceptions есть как плюсы так и минусы, ты их перечислил, дай посчитаю... ровно ноль.
        Ответить
        • А это как раз и не было выпадом в сторону LINQ. Там хоть ресурсы закрывались.
          Ответить
      • Ну, LINQ существует в дотнете не изначально. Он появился относительно недавно. Старые дотнетчики привыкли обходиться без него. Существует масса легаси кода, написанного без линка.

        Замечание про незакрытый поток - тебе плюс.

        А вообще, Linq и AsParallel - устаревающие мемы. На подходе C#5 и .Net4.5 - встречаем async и await! (Ой, чую, говна с ними будет написано столько...)
        Ответить
        • Просветите в 2х словах об этих явлениях (async и await), я за шарпом не слежу последние 2 года.
          Ответить
          • В двух словах, насколько я понимаю, async и await упрощают работу с асинхроностью. Вместо этих конструкций компилятор создаёт гору кода, и генерит такой себе конечный автомат. К примеру можно сделать async метод, который будет возвращять нечто с await, и оно в процессе вызова начнет обрабатываться. И когда мы обратимся к результату этого метода, а его еще не будет, то он сделает Wait, пока не прийдет результат

            Вот тут можно почитать - http://msdn.microsoft.com/ru-ru/magazine/hh456403.aspx
            PS. На практике не использовал и могу ошибаться в деталях.
            Ответить
            • Сильно похоже на java FutureTask. Просто java.util.concurent был написан после того как вышел шарп. Вот и не успели стырить. Да и await в жабовском Conditione есть.

              > Старые дотнетчики привыкли обходиться без него.
              Старые диезники таких ошибок не делают
              И зачем пытаться написать кривой велосипед, если уже за тебя подумали и всё сделали? Тем более если не умеешь делать велосипеды лучше, чем те что есть?
              Ответить
            • >Ой, чую, говна с ними будет написано столько.
              Та может и не будет. Там думать надо.

              И что же это получается AssParallel не дает хорошей, истинной асинхронности и параллельности, раз asyncи всякие мудрят?

              PS. Почитал подробней. Не в жаве такого нету. Но непонятно зачем они мутят? Итак язык сложный стал.
              Ответить
              • > И что же это получается AssParallel не дает хорошей, истинной асинхронности и параллельности, раз asyncи всякие мудрят?

                Параллельность даёт хорошую. Применять легко. Отчасти, поэтому его могут пихать куда попало (в реальности этим только на ГК грешат).
                Асинхронность - другое дело. Но с использованием async/await это становится также легко. Поэтому, боюсь, могут начать применять не там, где это реально нужно.
                Ответить
          • > Просветите в 2х словах об этих явлениях (async и await), я за шарпом не слежу последние 2 года.

            Может с F# знаком? В F# такое существует давно. Синтаксис, конечно, другой, но суть та же.

            В двух словах это очень сильно упрощает написание асинхронного кода. В разы! Достаточно понимать работу Task. Впрочем, с использованием тасков с ContinueWith и так сильно упрощается код даже без суперновых фич.

            Пока нет онлайн-компиляторов C#5, поэтому не получится привести пример кода с выполнением.

            Если кто интересуется реально, то советую книгу C# 5.0 in a Nutshell (на английском, найти и скачать - легко). В ней асинхронность хорошо описана.
            Для поверхностного знакомства, например, это: http://nesteruk.wordpress.com/2010/10/31/async-await-csharp5/
            Ответить
        • >Старые дотнетчики привыкли обходиться без него.

          Старая школа:
          System.IO.TextReader reader = 
          try{
            ...
          }finally{
              streamReader.close();
          }

          Школа поновее
          using (var reader = new StreamReader..
          	{
          }

          LINQ-школa
          File.ReadLines(pathToFile).Count()


          Писать можно как угодно, главное делать это с умом.
          Ответить
        • Цитата
          А вообще, Linq и AsParallel - устаревающие мемы.
          Кастую в тред @moderatorа, согласно правилу 1.9.0 'Надругательство над святынями'.
          Ответить
    • По блокам ли или построчно но думаю StringBuilder лучше чем string тут будет
      не будет каждый раз создавать новый string
      Ответить

    Добавить комментарий