Что творится с памятью?
От: alexelev  
Дата: 08.05.13 18:47
Оценка: -1
Дано:
куча файлов вида (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();
}


Хотя код следующего вида выполняется на ура:


cout << _filelength(_fileno(_fsopen("E:\\TEST\\det\\Untitled001.pdf", "r", _SH_DENYRD))) << endl;
ошибки памяти c++
Re: Что творится с памятью?
От: ArtDenis Россия  
Дата: 08.05.13 19:18
Оценка: 1 (1) +2
А в чём проблема-то (за исключением того, что что код — тихий ужос-ужос)?
[ 🎯 Дартс-лига Уфы | 🌙 Программа для сложения астрофото ]
Re[2]: Что творится с памятью?
От: alexelev  
Дата: 08.05.13 19:29
Оценка:
текст ошибки:

Debug Assertion Failed!
Program: D:\TEST\Test.exe
File: f:\dd\vctools\crt_bld\self_x86\crt\src\fileno.c
Line: 41

Expression: (stream != NULL)

For information on your program can cause an assertion failure,
see the Visual C++ documentation on asserts.

(Press Retry to debug the application)

компилятор MS VS 2012 Express
OS Windows 7
Re[3]: Что творится с памятью?
От: ArtDenis Россия  
Дата: 08.05.13 19:36
Оценка:
Здравствуйте, alexelev, Вы писали:

A>текст ошибки:


A>Debug Assertion Failed!


А при чём здесь ошибка с памятью? Сработал assert внутри fileno. Поставь сразу после FILE* fn = _fsopen(fp, "r", _SH_DENYNO); строку
assert(fn);

А ещё лучше в отладчике посмотри что ты передаёшь в _fileno
[ 🎯 Дартс-лига Уфы | 🌙 Программа для сложения астрофото ]
Re[4]: Что творится с памятью?
От: Logot Украина  
Дата: 08.05.13 19:43
Оценка:
Здравствуйте, 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?

//открываем файл
FILE* fn = _fsopen(fp, "r", _SH_DENYNO);


И перед использованием fn далее, я бы проверил валидный ли он?
Делай все постепенно.
Re[5]: Что творится с памятью?
От: alexelev  
Дата: 08.05.13 19:54
Оценка:
да, вы правы, но путь файла определяется верно, скрин дебаггера прикреплен: http://files.rsdn.ru/104070/Debug.PNG
Re: Что творится с памятью?
От: watch-maker  
Дата: 08.05.13 20:04
Оценка: 3 (1) +2
Здравствуйте, 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(fp, 256, "E:\\TEST\\det\\Untitled%03d.pdf", i);
Конечно, ещё лучше будет потом проверить результат, который вернула функция 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>


Этот код приводит к утечки ресурсов. Попробуй выполнить его в цикле и программа упадёт.
Re[6]: Что творится с памятью?
От: breee breee  
Дата: 10.05.13 10:01
Оценка:
Здравствуйте, alexelev, Вы писали:

A>да, вы правы, но путь файла определяется верно, скрин дебаггера прикреплен: http://files.rsdn.ru/104070/Debug.PNG


Тогда надо смотреть чему равна глобальная переменная _errno.
Re: Что творится с памятью?
От: Кодт Россия  
Дата: 10.05.13 20:51
Оценка: 3 (1) +2
Здравствуйте, 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

Да, это будет неспешно работать, зато сделает своё дело.
Перекуём баги на фичи!
Re: Что творится с памятью?
От: alexelev  
Дата: 01.06.13 14:46
Оценка:
Здравствуйте, 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?
Re[2]: Что творится с памятью?
От: ArtDenis Россия  
Дата: 02.06.13 09:37
Оценка:
Здравствуйте, Кодт, Вы писали:

К>(Для множественного поиска подойдёт префиксное дерево, но исходно мы можем себе позволить мега-медленный алгоритм).


Не надо изобретать велосипеды. Есть алгоритм Ахо — Корасик
[ 🎯 Дартс-лига Уфы | 🌙 Программа для сложения астрофото ]
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.