Дано:
куча файлов вида (Untitled001.pdf, Untitled002.pdf, Untitled003.pdf ...) в количестве более 800 штук;
строки вида (I-А-1, I-А-2, I-А-3, I-А-4, I-А-5, ... ), каждая из которых содержится в одном из кучи файлов.
Найти:
в каком файле содержится какая строка, например "I-А-1 Untitled012.pdf, I-А-2 Untitled117.pdf, ..." и вывести это в файл (исключительно для удобства)
Мой (нерабочий) вариант решения проблемы:
#include <iostream>
#include <cstring>
#include <ctime>
#include <direct.h>
#include <string.h>
#include <cstdlib>
#include <cstdio>
#include <fstream>
#include <io.h>
#include <stdio.h>
using namespace std;
void main()
{
//файл, куда будет выводится инфа
FILE* rslt = _fsopen("E:\\TEST\\rslt.txt", "w", _SH_DENYNO);
for(int i = 0; i < 1000; i ++)
{
//лепим имя файлаchar fp[256] = {0};
if (i < 9)
strcpy_s(fp,256,"E:\\TEST\\det\\Untitled00");
else if (i < 99)
strcpy_s(fp,256,"E:\\TEST\\det\\Untitled0");
else
strcpy_s(fp,256,"E:\\TEST\\det\\Untitled");
char buf[256] = {0};
_itoa_s(i+1, buf, 256, 10);
strcat_s(fp,256,buf);
strcat_s(fp,256,".pdf\0");
//на выходе имеем путь к файлу вида "E:\\TEST\\det\\Untitled001.pdf"
//открываем файл
FILE* fn = _fsopen(fp, "r", _SH_DENYNO);
//пытаемся определить его объём - количество символов файлаint a = _fileno(fn); //именно тут происходим какая-то ошибка с памятьюlong size = _filelength(_fileno(fn));
//переводим все содержимое файла в буферchar* buffer = new char [size];
fgets(buffer, size, _fsopen(fp, "r", _SH_DENYNO));
for (int j = 0; j < 1000; j ++)
{
//формируем искомую подстрокуchar numb[10] = {0};
_itoa_s(j+1, numb, 256, 10);
char name[256] = {0};
strcat_s(name, 256, "I-А-");
strcat_s(name, 256, numb);
//на выходе имеем строку вида "I-А-1"
//находим вхождение искомой строки в буфферном массиве и если есть - записываем в файл соответствие if (strstr(buffer, name) != NULL)
{
char temp[256] = {0};
strcat_s(temp, 256, name);
strcat_s(temp, 256, "\t");
strcat_s(temp, 256, fp);
fputs(temp, rslt);
}
}
fclose(fn);
}
_fcloseall();
}
Здравствуйте, alexelev, Вы писали:
A>текст ошибки:
A>Debug Assertion Failed! A>Program: D:\TEST\Test.exe A>File: f:\dd\vctools\crt_bld\self_x86\crt\src\fileno.c A>Line: 41
A>Expression: (stream != NULL)
A>For information on your program can cause an assertion failure, A>see the Visual C++ documentation on asserts.
A>(Press Retry to debug the application)
A>компилятор MS VS 2012 Express A>OS Windows 7
Ну так ясно же говорится, что fn у тебя NULL. Для начала я бы разобрался, почему этот код возвращает NULL?
Здравствуйте, alexelev, Вы писали:
A>Дано: A>куча файлов вида (Untitled001.pdf, Untitled002.pdf, Untitled003.pdf ...) в количестве более 800 штук; A>строки вида (I-А-1, I-А-2, I-А-3, I-А-4, I-А-5, ... ), каждая из которых содержится в одном из кучи файлов.
A>Найти: A>в каком файле содержится какая строка, например "I-А-1 Untitled012.pdf, I-А-2 Untitled117.pdf, ..." и вывести это в файл (исключительно для удобства)
A>Мой (нерабочий) вариант решения проблемы:
Да, тут слишком много ошибок. И в основном двух типов: отсутствует проверка возвращаемых значений функций и отсутствует код освобождения захваченных ресурсов.
A> FILE* rslt = _fsopen("E:\\TEST\\rslt.txt", "w", _SH_DENYNO);
Нет проверки результата.
A> if (i < 9)
A> strcpy_s(fp,256,"E:\\TEST\\det\\Untitled00");
A> else if (i < 99)
A> strcpy_s(fp,256,"E:\\TEST\\det\\Untitled0");
A> else
A> strcpy_s(fp,256,"E:\\TEST\\det\\Untitled");
A> char buf[256] = {0};
A> _itoa_s(i+1, buf, 256, 10);
A> strcat_s(fp,256,buf);
A> strcat_s(fp,256,".pdf\0");
A> //на выходе имеем путь к файлу вида "E:\\TEST\\det\\Untitled001.pdf"
Жуть. Если уж делаешь вид, что пишешь на С, то вместо этого здоровенного куска напиши просто
Конечно, ещё лучше будет потом проверить результат, который вернула функция snprintf.
A> //открываем файл
A> FILE* fn = _fsopen(fp, "r", _SH_DENYNO);
Нет проверки результата. И это приводит к наблюдаемой тобою ошибке.
A> //пытаемся определить его объём - количество символов файла
A> int a = _fileno(fn); //именно тут происходим какая-то ошибка с памятью
Не с памятью.
A> //переводим все содержимое файла в буфер
A> char* buffer = new char [size];
Память нигде не освобождается. Утечка.
A> fgets(buffer, size, _fsopen(fp, "r", _SH_DENYNO));
Открытый файл нигде не закрывается. Утечка. Также приводит к наблюдаемой тобою ошибке.
Кроме того, функция fgets работает не так, и не способна прочитать весь файл. В твоём буфере оказывается мусор.
A> for (int j = 0; j < 1000; j ++)
A> {
A> //формируем искомую подстроку
A> char numb[10] = {0};
A> _itoa_s(j+1, numb, 256, 10);
A> char name[256] = {0};
A> strcat_s(name, 256, "I-А-");
A> strcat_s(name, 256, numb);
A> //на выходе имеем строку вида "I-А-1"
Аналогично, замени хотя бы на
snprintf(name, 256, "I-A-%d", j);
A> //находим вхождение искомой строки в буфферном массиве и если есть - записываем в файл соответствие
A> if (strstr(buffer, name) != NULL)
Функция strstr не может найти образец в файле в общем случае, так как некоторые символы ею трактуются особым образом. Похожая, но правильная функция называется memmem.
Хотя, судя по всему, у тебя студия, а там memmem нет. В этом случае можешь взять её откуда-нибудь (хотя по ссылке приведена и наивная реализация, но она хотя бы даёт верный ответ).
А вообще, лучше не перебирать все возможные варианты, а сначала сразу искать подстроку "I-A-", и лишь потом уточнять какие же цифра за ней идут. Хотя, конечно, лучше сначала добиться правильности работы, а оптимизацию отложить на потом.
A> {
A> char temp[256] = {0};
A> strcat_s(temp, 256, name);
A> strcat_s(temp, 256, "\t");
A> strcat_s(temp, 256, fp);
A> fputs(temp, rslt);
A> }
Замени
fprintf(rslt, "%s\t%s\n", name, fp);
A>Хотя код следующего вида выполняется на ура:
A>
A>cout << _filelength(_fileno(_fsopen("E:\\TEST\\det\\Untitled001.pdf", "r", _SH_DENYRD))) << endl;
A>
Этот код приводит к утечки ресурсов. Попробуй выполнить его в цикле и программа упадёт.
Здравствуйте, alexelev, Вы писали:
A>Дано: A>куча файлов вида (Untitled001.pdf, Untitled002.pdf, Untitled003.pdf ...) в количестве более 800 штук; A>строки вида (I-А-1, I-А-2, I-А-3, I-А-4, I-А-5, ... ), каждая из которых содержится в одном из кучи файлов.
A>Найти: A>в каком файле содержится какая строка, например "I-А-1 Untitled012.pdf, I-А-2 Untitled117.pdf, ..." и вывести это в файл (исключительно для удобства)
A>Мой (нерабочий) вариант решения проблемы:
Попытка рабочего кода:
1. Прочитаем список искомых строк из файла (можем — из командной строки) и построим таблицу.
(Для множественного поиска подойдёт префиксное дерево, но исходно мы можем себе позволить мега-медленный алгоритм).
2. Прочитаем список файлов из другого файла (можем — из stdin) и будем последовательно обрабатывать.
3. Выводить результат можем смело в stdout, дальше в командной строке можем куда угодно перенаправлять.
MyTool patterns.txt names.txt the_dir .pdf > results.txt
del %TEMP%\names.txt
#include <string>
#include <vector>
struct Patterns
{
std::vector<std::string> lines; //TODO сделать префиксное дерево (сами строки целиком тоже пригодятся - для вывода)
};
void build_patterns(const char* patternsfile, Patterns& patterns)
{
assert(patternsfile);
std::ifstream input(patternsfile); // из C++ного потока удобнее читатьif(!input) { fprintf(stderr, "cannot open patterns file '%s'\n", patternsfile);
while(input)
{
std::string line;
std::getline(input, line);
if(line.back()=='\n') line.pop_back(); // у всех строк, кроме последней, в конце есть символ LF
patterns.lines.push_back(line); //TODO строить префиксное дерево
}
}
void scan_file(const Patterns& patterns, const char* dirname, const char* filename, const char* ext)
{
std::vector<char> filepath_buffer(MAX_PATH, (char)0);
char* filepath = &filepath_buffer.front();
_mkpath(filepath, dirname, filename, ext);
FILE* fp = fopen(filepath, "r");
if(!fp) { fprintf(stderr, "cannot open file '%s'\n", filepath); }
//TODO переделать на посимвольное чтение - когда будет готово префиксное дерево
size_t const len = _filelength(_fileno(fp)); // опасность нас ждёт на файлах > 4Гб
std::vector<char> buffer(len);
char* const contents = &buffer.front();
fread(contents, 1, len, fp);
for(int i=0; i!=patterns.lines.size(); ++i) // в файл может входить несколько строк сразуif(strstr(contents, patterns.lines[i].c_str()))
fprintf(stdout, "'%s' in '%s'\n", patterns.lines[i].c_str(), filename);
fclose(fp);
}
void scan_files(const Patterns& patterns, const char* filesfile, const char* dirname, const char* ext)
{
assert(filesfile);
std::ifstream input(filesfile); // из C++ного потока удобнее читатьif(!input) { fprintf(stderr, "cannot open files file '%s'\n", filesfile);
while(input)
{
std::string line;
std::getline(input, line);
if(line.back()=='\n') line.pop_back(); // у всех строк, кроме последней, в конце есть символ LF
scan_file(patterns, dirname, line.c_str(), ext);
}
}
int main(int argc, char argv[])
{
if(argc != 5)
{
fprintf(stderr, "usage: MyTool patternsfile filesfile workdir ext\n");
return 1;
}
Patterns patterns;
build_patterns(argv[1], patterns);
if(patterns.lines.empty())
{
fprintf(stderr, "no patterns - no matches\n");
return 0; // это правильный, хотя и бессмысленный результат (пустой список на выходе)
}
scan_files(patterns, argv[2], argv[3], argv[4]);
}
Ну а если это программа из серии выкраси-и-выброси, то я бы сделал проще.
Поставил бы себе любое юникс-окружение (msys, cygwin и т.п.) (оно и в будущем пригодится) и наваял бы шелл-скрипт с использованием grep.
for filename in *.pdf
do
for pattern in `cat patterns`
do
grep -o -H -m1 "$pattern" $filename
done
done
Да, это будет неспешно работать, зато сделает своё дело.
Здравствуйте, alexelev, Вы писали:
A>Дано: A>куча файлов вида (Untitled001.pdf, Untitled002.pdf, Untitled003.pdf ...) в количестве более 800 штук; A>строки вида (I-А-1, I-А-2, I-А-3, I-А-4, I-А-5, ... ), каждая из которых содержится в одном из кучи файлов.
A>Найти: A>в каком файле содержится какая строка, например "I-А-1 Untitled012.pdf, I-А-2 Untitled117.pdf, ..." и вывести это в файл (исключительно для удобства)
A>Мой (нерабочий) вариант решения проблемы:
A>
A>#include <iostream>
A>#include <cstring>
A>#include <ctime>
A>#include <direct.h>
A>#include <string.h>
A>#include <cstdlib>
A>#include <cstdio>
A>#include <fstream>
A>#include <io.h>
A>#include <stdio.h>
A>using namespace std;
A>void main()
A>{
A> //файл, куда будет выводится инфа
A> FILE* rslt = _fsopen("E:\\TEST\\rslt.txt", "w", _SH_DENYNO);
A> for(int i = 0; i < 1000; i ++)
A> {
A> //лепим имя файла
A> char fp[256] = {0};
A> if (i < 9)
A> strcpy_s(fp,256,"E:\\TEST\\det\\Untitled00");
A> else if (i < 99)
A> strcpy_s(fp,256,"E:\\TEST\\det\\Untitled0");
A> else
A> strcpy_s(fp,256,"E:\\TEST\\det\\Untitled");
A> char buf[256] = {0};
A> _itoa_s(i+1, buf, 256, 10);
A> strcat_s(fp,256,buf);
A> strcat_s(fp,256,".pdf\0");
A> //на выходе имеем путь к файлу вида "E:\\TEST\\det\\Untitled001.pdf"
A> //открываем файл
A> FILE* fn = _fsopen(fp, "r", _SH_DENYNO);
A> //пытаемся определить его объём - количество символов файла
A> int a = _fileno(fn); //именно тут происходим какая-то ошибка с памятью
A> long size = _filelength(_fileno(fn));
A> //переводим все содержимое файла в буфер
A> char* buffer = new char [size];
A> fgets(buffer, size, _fsopen(fp, "r", _SH_DENYNO));
A> for (int j = 0; j < 1000; j ++)
A> {
A> //формируем искомую подстроку
A> char numb[10] = {0};
A> _itoa_s(j+1, numb, 256, 10);
A> char name[256] = {0};
A> strcat_s(name, 256, "I-А-");
A> strcat_s(name, 256, numb);
A> //на выходе имеем строку вида "I-А-1"
A> //находим вхождение искомой строки в буфферном массиве и если есть - записываем в файл соответствие
A> if (strstr(buffer, name) != NULL)
A> {
A> char temp[256] = {0};
A> strcat_s(temp, 256, name);
A> strcat_s(temp, 256, "\t");
A> strcat_s(temp, 256, fp);
A> fputs(temp, rslt);
A> }
A> }
A> fclose(fn);
A> }
A> _fcloseall();
A>}
A>
A>Хотя код следующего вида выполняется на ура:
A>
A>cout << _filelength(_fileno(_fsopen("E:\\TEST\\det\\Untitled001.pdf", "r", _SH_DENYRD))) << endl;
A>
переписал код:
#include <iostream>
#include <cstdio>
#include <io.h>
#include <Windows.h>
#include <ctime>
void main()
{
SetConsoleCP(1251);
SetConsoleOutputCP(1251);
time_t start, end, result;
struct _finddata_t txt_file;
long hFile;
int count = 0;
time(&start);
FILE* rslt = _fsopen("E:\\TEST\\result.txt", "w", _SH_DENYNO);
/* определяем количество .txt файлов */if( (hFile = _findfirst( "E:\\TEST\\det_text\\*.txt", &txt_file )) == -1 )
printf( "No *.txt files in current directory!\n" );
else
{
count = 1;
//printf_s("%s\n", txt_file.name);
/* Find the rest of the .txt files */while( _findnext( hFile, &txt_file ) == 0 )
{
count ++;
//printf_s("%s\n", txt_file.name);
}
_findclose( hFile );
printf_s("найдено %d файлов для поиска\n", count);
}
for (int i = 0; i < count; i ++)
{
char fn[256] = {0};
if(sprintf_s(fn, "E:\\TEST\\det_txt_ansi\\Untitled%03d.txt", i + 1) == 0 &&
sprintf_s(fn, "E:\\TEST\\det_txt_ansi\\Untitled%03d.txt", i + 1) == -1)
{
printf_s("Ошибка создания пути файла Untitled%03d.txt при поиске\n", i + 1);
break;
}
FILE* f = _fsopen(fn, "r", _SH_DENYRD);
if(!f)
{
printf_s("Ошибка открытия файла Untitled%03d.txt\n", i + 1);
break;
}
int size = _filelength(_fileno(f));
/*переписываем содержимое файла в буферную строку*/char* tmp = new char [size];
for (int j = 0; j < size && tmp[j] != EOF; j ++)
{
tmp[j] = fgetc(f);
}
fclose(f);
/*поиск искомой строки и считывание следующих символов до конца строки*/for (int k = 0; k < size; k ++)
{
char SubStr[20] = "I-А-";
if(tmp[k] == SubStr[0])
{
for(int n = 1; n < size; n ++)
{
if(SubStr[n] == 0 && tmp[k + n] != 10)
SubStr[n] = tmp[k + n];
else if(tmp[k + n] == '\n' && SubStr[n] == 0)
{
fprintf_s(rslt, "%s\t\t%s\n", SubStr, fn);
break;
}
if (tmp[k + n] != SubStr[n])
break;
}
}
}
delete [] tmp;
}
_fcloseall();
time(&end);
result = end - start;
printf_s("РАБОТА ПРОГРАММЫ УСПЕШНО ЗАВЕРШЕНА!\nвремя работы программы %d сек\n", result);
}
Благодарю особо @Кодт и @watch-maker за помощь и направление движения мыслей. Хотя остается еще ряд нерешенных проблем. В таком виде, как я формулировал задачу, выполнить ее я не смог по причине того, что обрабатываемые файлы — .pdf. Как выяснилось методом проб и ошибок пдф-файлы открываются как текстовые, но искомый ключ в них не находится, а при выводе считанного из пдф-файла получается какой-то мусор. Так что пришлось конвертировать пдф в текст, текст получался юникод, так что приходилось его переконвертировать в ANSI, только тогда данный способ работает. Теперь ломаю голову, как бы сделать так, чтобы избежать всех этих многочисленных переконвертаций, а читать и искать сразу непосредственно в пдф-файлах. Может кто-нибудь подсказать, куда копать для этого?
З.Ы. @Кодт указал прелестный способ для юникс-окружения, а можно что-либо подобное выполнить в cmd Windows используя команды find/findstr?