Быстрая реализация поиска строк в csv/txt(ANSI) файлах (WinAPI + С++)
Реализация для XLL как функция Excel. Часть библиотеки BedvitXLL Особенности реализации: 1.WinAPI: CreateFile + ReadFile 2.Асинхронность в чтения с диска и выполнения рабочего кода. 3.Возможность отключения системного кеширования. 4.Посему чтение блоками, кратно степени двойки - по 16 Мб.(16777216 байт), в большинстве hdd - такого размера свой кеш. 5.Посему адрес принимающего буфера и рабочего выровнен по размеру сектора/страницы - 4096 байт 6.Возможность вывода всех найденных строк или первой найденной (поиск с начала файла) 7.Строка выводится вся, в т.ч. с разделителями. 8.Кодировка csv/txt - ANSI 9.Размер искомой строки и подстроки поиска не может быть больше буфера (16 Мб). 10.Файл может быть любого размера. 11.Поддерживается Стандарт RFC 4180 Функции: 1.GetRowsCountCSVansi(Файл(строка), ОтключениеКешированияФайла(0/1)) - возвращает кол-во строк в файле (учитываются пустые строки, расчет происходит по количеству разделителей строк) 2.GetRowCSVansi(Файл(строка), ПодстрокаДляПоиска(индекс), ОтключениеКешированияФайла(0/1)) - возвращает найденную строку по индексу 3.FindRowsInCSVansi(Файл(строка), ПодстрокаДляПоиска(строка), ВывестиВсеСтроки(0/1), ОтключениеКешированияФайла(0/1)) - возвращает найденную строку/строки по искомой подстроке Код: Кликните здесь для просмотра всего текста
Наблюдения: 1.Если рабочий файл не влезает в системный кеш, отключение кеширования положительно влияет на тайминг, если влезает кеширование серьезно ускоряет процесс (т.е. кеширование не отключаем на малых, отключаем на больших файлах). 2.При документированной скорости чтения SSD - 450 Мб/с, 10 ГБ читается за 22,2 сек, что близко к скорости поиска (25,45 сек. с отключенным кешированием - 40 млн.строк, ищем последнюю). Запуск из VBA
UPD 03/07/2020 v1.0.2.7 Реализация в СОМ: [id(10), helpstring("Get Rows Count in CSV-file encoded ANSI")] HRESULT GetRowsCountCSVansi([in] BSTR FileIn, [in, defaultvalue(0)] LONG FileFlagNoBuffering, [out, retval] LONG* RowsCountOut); [id(11), helpstring("Get Row by index in CSV-file encoded ANSI")] HRESULT GetRowCSVansi([in] BSTR FileIn, [in] LONG IndexString, [in, defaultvalue(0)] LONG FileFlagNoBuffering, [out, retval] BSTR* StringOut); [id(12), helpstring("Find Rows/String by substring in CSV-file encoded ANSI")] HRESULT FindRowsInCSVansi([in] BSTR FileIn, [in] BSTR FindString, [in, defaultvalue(0)] LONG MultiLine, [in, defaultvalue(0)] LONG FileFlagNoBuffering, [out, retval] BSTR* StringOut); Запуск из VBA
|
Всего комментариев 37
Комментарии
-
Это не С++ это Си ...
Ну или отличный пример С++гавнокода.
(простыни, goto, магические числа).
Попробуйте переписать код с использованием std::unique_ptr<> для хендлов, возможно получиться лучше.Запись от Avazart размещена 07.06.2020 в 00:08
Обновил(-а) Avazart 07.06.2020 в 11:28 -
Да, в основном это Си, здесь мне так и посоветовали. У вас другая точка зрения, обоснуйте. Можете привести свой пример, который будет эффективнее.
Запись от bedvit размещена 07.06.2020 в 12:58 -
Цитата:Да, в основном это Си,
1. Не важно Си или С++ - функция неоправданно объемная как в длину так и в ширину т.е. простыня. Так пишут студенты не научившиеся разбивать код на -функции и модули (в С, а в С++ еще и классы)
2. В коде есть магические числа, что такое в коде 4096 ? Почему не 666 ?
Почему некоторые переменные названы капсом? Капс - для макросов.
3. В С++ есть классы и исключения (и есть break и continue) что позволяет писать лаконичнее и избегать goto
4. И я же посоветовал. Вы про идиому RAII слышали? Ну и где оно?Запись от Avazart размещена 07.06.2020 в 14:01
Обновил(-а) Avazart 07.06.2020 в 14:11 -
Что мешало вместо
Использовать std::string или std::vector<char> ?C++ 1
char* notAlignBuf = new char[nNumberOfBytesToRead + 4096]; //буфер
Цитата:C++ 1 2 3 4 5 6 7 8
// проверяем на успешное открытие if (hFile == INVALID_HANDLE_VALUE) { CloseHandle(hEndRead); delete[] notAlignBuf; delete[] notAlignBufBufWork; return OperOut; }
Запись от Avazart размещена 07.06.2020 в 14:15
Обновил(-а) Avazart 07.06.2020 в 14:18 -
Спасибо за вашу оценку, благодаря вам мы становимся лучше.
Цитата:
Цитата:
Цитата:
Мне нужно была простая и быстрая реализация. Не вижу зачем здесь идиома RAII и умные указатели.Запись от bedvit размещена 07.06.2020 в 15:40 -
Цитата:
Цитата:
строка кода 3-5:
C++ 1 2 3
LPXLOPER12 OperOut = new XLOPER12; OperOut->xltype = xltypeErr; OperOut->val.err = xlerrValue;
Запись от bedvit размещена 07.06.2020 в 15:45 -
Цитата:не вижу целесообразности что-то выделять в класс.
Цитата:Если бы вы прочитали инфо, по ссылке мной ранее высланной, у вас бы не было этого вопроса. Эти магические числа хочет CreateFile+FILE_FLAG_NO_BUFFER ING, вопрос не ко мне. Капсом - это тоже не ко мне, а к microsoft. У меня нет таких переменных.
Я шучу по тому что это явный зашквар и я не понимаю как реагировать если человек не понимает что магические числа это плохо.
Строка #23
А это что? Пришел Майкрософт и нагадил Вам в код? Носом его носом ... кто это сделал ? Фу...C++ 1
bool ERR_HANDLE_EOF = false; //метка конца файла
Цитата:Опять же, если вы посмотрите тему по ссылке выше - там будет реализация на std::string - это медленно.
Цитата:Вы не любите кошек? Вы просто не умеете их готовить.Запись от Avazart размещена 07.06.2020 в 15:52
Обновил(-а) Avazart 07.06.2020 в 16:30 -
Запись от bedvit размещена 07.06.2020 в 16:00 -
bedvit, а для чего тут асинхронное чтение? Вроде нет другого потока, нет петли обработки сообщений. Функция линейная
Или я что-то не увидел?
std::string, std::ifstream - портянка сократится в несколько раз
Цитата:std::string - это медленноЗапись от Алексей1153 размещена 07.06.2020 в 16:04 -
Запись от Алексей1153 размещена 07.06.2020 в 16:11
Обновил(-а) Алексей1153 07.06.2020 в 16:14 -
Цитата:Так покажите, как надо
Цитата:Мне нужно была простая и быстрая реализация. Не вижу зачем здесь идиома RAII и умные указатели.
Был бы "RAII и умные указатели" код был бы проще. А так получили теш из goto и копипасты.
Цитата:Верно, поэтому там ошибка и выводится.
Цитата:а если уж WinAPI, то проще создать проекцию файла CreateFileMapping/OpenFileMapping и найти строку std::search
Да и просто std::search не подходит нужно на его основе чет построить что бы "детектить" частичное совпадение и докачивать след. кусок файла.
Цитата:bedvit, а для чего тут асинхронное чтение? Вроде нет другого потока, нет петли обработки сообщений.Запись от Avazart размещена 07.06.2020 в 16:14
Обновил(-а) Avazart 07.06.2020 в 16:33 -
Запись от Avazart размещена 07.06.2020 в 16:18
Обновил(-а) Avazart 07.06.2020 в 16:33 -
Коллеги, прошу прочитать тему, https://www.cyberforum.ru/blog... g6617.html
тогда и вопросы можно обсудить. Сейчас как-то в молоко, извините.
Алексей1153, асинхронность есть, смотрите внимательнее. Про MMF, вот цитата из той же темы, и мне она представляется верной.
Цитата:Ибо при MMF сразу всего гигового файла - будет page fault на каждой новой 4кб-странице памяти. Т.к. замапленная память выделяется сначала виртуально - и лишь при состоявшемся обращении она будет сопоставляться со страницей реальной физической памяти и в неё будет грузиться содержимое куска файла. Т.е. MMF здесь к чтению данных добавляет кучу сработок процессорного/системного обработчика исключений.
А если мапить кусками - к чтению данных (невидимому нам при MMF) добавляются регулярные вызовы MapViewOfFile/UnmapViewOfFile на каждом таком куске. Т.е. затраты на передачу аргументов, на вызовы функций, затем на проверку аргументов в них, на выполнение нужной логики. Ну и нафиг оно - всё это лишнее, когда можно обойтись просто одним вызовом обычной читалки ReadFile()?Запись от bedvit размещена 07.06.2020 в 16:29 -
Цитата:Опять же, если вы посмотрите тему по ссылке выше - там будет реализация на std::string - это медленно.
то решил проверить ваше утверждение на вашем же тесте:
https://www.cyberforum.ru/cpp-... st14628185Запись от XLAT размещена 07.06.2020 в 17:10 -
C++ 1 2 3 4 5 6 7 8 9 10
#include <memory> #include <Windows.h> int main() { std::unique_ptr<void, decltype(&CloseHandle)> event(CreateEvent(NULL, FALSE, FALSE, NULL),&CloseHandle); return 0; }
https://ru. stackoverflow.com/questions/423359/Как-использовать-unique-ptr-для-хендлов
Вместо Си-строк - контейнеры и код вида:
не нужно копипастить при каждой обработке ошибок и goto не нужен.C++ 1 2 3 4 5 6 7
if (hFile == INVALID_HANDLE_VALUE) { CloseHandle(hEndRead); delete[] notAlignBuf; delete[] notAlignBufBufWork; return OperOut; }
Кстати
Не удаляется создавая утечку?C++ 1
LPXLOPER12 OperOut = new XLOPER12;
Запись от Avazart размещена 07.06.2020 в 18:04
Обновил(-а) Avazart 07.06.2020 в 18:46 -
Запись от bedvit размещена 07.06.2020 в 19:36 -
Цитата:так как я в прошлом тоже ассемблердрочер, то вопрос эффективности у меня не на последнем месте,
то решил проверить ваше утверждение на вашем же тесте:
https://www.cyberforum.ru/cpp-... st14628185Запись от bedvit размещена 07.06.2020 в 19:38 -
Запись от bedvit размещена 07.06.2020 в 19:58 -
Запись от Avazart размещена 07.06.2020 в 20:26
Обновил(-а) Avazart 07.06.2020 в 20:27 -
Запись от bedvit размещена 07.06.2020 в 23:25