Компьютерный форум OSzone.net  

Компьютерный форум OSzone.net (http://forum.oszone.net/index.php)
-   Программирование и базы данных (http://forum.oszone.net/forumdisplay.php?f=21)
-   -   Кучка (heap) (http://forum.oszone.net/showthread.php?t=173053)

ManHack 13-04-2010 20:51 1391829

Кучка (heap)
 
Здравствуйте!
Меня интересует как в программе на Си обрабатывать кучу файлов, заданных по маске параметром командной строки как один входной источник? (файлы не копируются друг в друга! в любой момент времени мы должны точно значть из какого именно файла мы считываем)
Синтаксис запуска, например, таков:
myprogram.exe *.TEXT
При таком запуске программа должна обрабатывать в качестве входного потока все файлы с расширением .TEXT, которые лежат в папке с ней.

Товарищи Керниган и Ритчи предлагают только filecopy... ((

DillerInc 13-04-2010 22:05 1391877

ManHack, дык, перечислите все файлы в папке, чтобы узнать их кол-во, а после выделите память(можно и кучи можно через VirtualAlloc, зависит от конкретного примера) для таблицы, которая будет содержать структуры следующего типа:
Код:

opened_file struc
  hFile                        dd  ?  ; Описатель файла, чтобы потом к нему обращаться
  pFileName              dd  ?  ; Указатель на буфер, содержащий путь к файлу
  pFileMem                  dd  ?  ; Указатель на память со считаным файлом
opened_file ends

Для буфера пути файла лучше использовать кучу, т.к. она лучше подходит для выделения небольших размеров памяти.А файл лучше считывать в память, выделенную через VirtualAlloc.
Ну, и далее по обстоятельствам.

ManHack 13-04-2010 23:26 1391933

Вот про указатели на память со считанным файлом можно поподробнее?
Каким образом это реализуется на Си? Я понимаю что есть "указатель на файл", но фраза "указатель на память" вносит некое смятение в мои мысли. Проясните пожалуйста.

DillerInc 14-04-2010 00:19 1391981

ManHack, я вам не скажу, как это делается именно на Си, т.к. не пишу на этом языке, но на WinAPI это делается следующим образом:
1. Открываем файл с помощью CreateFile
2. Узнаём размер файла с помощью GetFileSize
3. Выделяем нужное кол-во памяти через VirtualAlloc, основываясь на полученном размере файла
4. И считываем открытый файл в эту память при помощи ReadFile
Вот у вас и получился указатель на память, содержащую считаный файл.
А что такое указатель на файл? Его описатель(который handle)? Если да, то понятие указатель здесь несовсем подходит.

ManHack 14-04-2010 13:40 1392296

Спасибо! Но я слышал, что в Си можно совершенно стандартным образом обрабатывать кучу файлов по маске.
Например, в Паскале есть команда, возвращающая первый файл из директории, которые подпадает под заданную маску (к примеру, какую-нибудь такую: ABS*.PAS)
Также там есть команда, которая позволяет получить следующий файл, удовлетворяющий такой маске из той же директории. А далее можно организовать цикл...
В Си тоже есть что-то аналогичное и тоже совершенно стандартное, но упоминаний в руководствах об этом я не нашёл.
Хотелось бы разузнать как такие вещи реализуются именно на Си...

pva 14-04-2010 15:33 1392429

Цитата:

Цитата ManHack
Например, в Паскале есть команда, возвращающая первый файл из директории, которые подпадает под заданную маску (к примеру, какую-нибудь такую: ABS*.PAS) »

я так понял что нужны функции FindFirstFile, FindNextFile, FindClose (windows.h)?
либо findfirst, findnext (dir.h)
в любом случае стандартными их назвать можно только для windows и dos. В юниксе не прокатят.
виндовые функции упоминаются в руководстве msdn, а так же помогает поиск в инете по их названиям.
Ещё пишут что аналогично работает opendir, который есть в юниксе, но меня описание не впечатлило.

А я бы посоветовал, если обработка файлов ведётся независимо, сделать в программе так, чтобы она принимала названия файлов со входного потока, и запускать её так:
dir /s /b /a-d *.pas | my_program
один косяк: у винды глюкавый dir. Если он встретит папку 1.pas, то перечислит всё её содержимое. В юниксе таких проблем нет.

Admiral 14-04-2010 15:39 1392436

pva, для Юникса по идеи должен помочь исходник ls.

pva 14-04-2010 20:52 1392673

так и есть, а моя любимая - find и/или grep

ManHack 06-05-2010 22:59 1408336

На славу потрудившись, я написал следующий код:
Код:

void ResetText() {
        WIN32_FIND_DATA FindFileData;
        HANDLE hFind;
        hFind = FindFirstFile( Path , &FindFileData );
        puts("FindFileData.cFileName");
        if( FindFileData.cFileName == NULL ) {
                puts("Формат вызова:\n  O <входной файл>");
                exit(1);
        }
        else if( (f = fopen(FindFileData.cFileName, "r")) == NULL ){
                ResetError = TRUE;
                Message = "Входной файл не найден";
        }
        else {
                ResetError = FALSE; Message = "Ok";
                Pos = 0; Line = 1;
                NextCh();
        }
        do {
                if( hFind  && !( FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY  ) ) {
                        if( FindFileData.cFileName == NULL ) {
                                puts("Формат вызова:\n  O <входной файл>");
                                exit(1);
                        }
                        else if( (f = fopen(FindFileData.cFileName, "r")) == NULL ){
                                ResetError = TRUE;
                                Message = "Входной файл не найден";
                        }
                        else {
                                ResetError = FALSE; Message = "Ok";
                                Pos = 0; Line = 1;
                                NextCh();
                        }
                }                       
        } while( FindNextFile( hFind, &FindFileData ) );
        FindClose(hFind);
}

Невероятно, но он компилируется в MS Visual Studio 2008 как код на Си.
Теперь проблема: скармливаю я ему, значит, консольный ввод:
Код:

MyProgram.exe InputFile.txt
А он мне врёт:
Цитата:

Входной файл не найден
Что я сделал не так?

PS> Как из WIN32_FIND_DATA конвертировать, скажем, в формат массива из символов?

pva 08-05-2010 12:57 1409271

1) что такое Path? откуда он взялся?
2) посоветую изначально занулить WIN32_FIND_DATA
3) насколько я помню, cFileName - это и есть массив из MAX_PATH_LENGTH символов (а не указатель), поэтому
Цитата:

Цитата ManHack
if( FindFileData.cFileName == NULL ) { »

никогда не сработает

ManHack 11-05-2010 20:11 1411152

Написал совсем по-человечески:
O.C
Код:

/* Љ®¬ЇЁ«пв®а п§лЄ* Ћ (o.c) */

#define _CRT_SECURE_NO_WARNINGS

#include <stdlib.h>
#include <stdio.h>
#include <io.h>
#include <time.h>
#include <string.h>

#include "text.h"
#include "scan.h"
#include "scan.h"
#include "location.h"
#include "pars.h"
#include "error.h"

void Init(void) {
        ResetText();
        if( ResetError )
                Error(Message);
        InitScan();
}

void Done(void) {
  CloseText();
}

int main(int argc, char *argv[]) {
  struct _finddata_t c_file;
  intptr_t hFile;

  puts("\n бла-бла-бла");
  if( argc <= 1 )
      Mask = NULL;
  else
      Mask = argv[1];

  hFile = _findfirst( Mask, &c_file );
  if( (hFile = _findfirst( Mask, &c_file )) == -1L ) {
          printf( "%s", "Файлов, удовлетворяющих маске " );
          printf( "%s", Mask );
          printf( "%s", "не найдено.\n" );
  } else {
          do {
                  strcpy(Path, &c_file.name);
                  Init();   
                  Compile();
                          Done();
          } while( _findnext( hFile, &c_file ) == 0 );
          _findclose( hFile );
  }

  return 0;
}

Этот самый ResetText():
Код:

char ResetError = TRUE;
char* Message = "”*©« *Ґ ®вЄалв";
int Ch = chEOT;

static FILE *f;

void NextCh() {
  if( (Ch = fgetc(f)) == EOF )
      Ch = chEOT;
  else if( Ch == '\n' ) {
        //readln(f);// ??
          // puts("");
      Line++; Pos = 0; Ch = chEOL;
      }
  else if( Ch == '\r' )
      NextCh();
  else if( Ch != '\t' ) {
      //  putchar(Ch);
          Pos++;
      }
    // else
      // do
      //    putchar(' ');
      //  while( ++Pos % TABSIZE );
}

void ResetText() {
        if( Path == NULL ) {
                puts("Запуск*:\n  MyProgram.exe <вход. файл>");
                exit(1);
        }
        else if( (f = fopen(Path, "r")) == NULL ){
                ResetError = TRUE;
                Message = "Что-то пошло не так с открытием файла*";
        }
        else {
                ResetError = FALSE; Message = "Ok";
                Pos = 0; Line = 1;
                NextCh();
        }
}


Теперь первый файл по маске программа читает нормально, а вот когда под маску подпадают несколько файлов, то когда дело доходит до второго возникает ошибка:




Ошибка возникает на втором файле, т.к. я пробовал выводить значение переменной Path когда они присваиваются - выдало 1-е имя файла из папки, удовлетворяющее маске и 2-е, потом ошибка.

В чём причина неполадки? Почему программа не хочет обрабатывать последующие файлы? (их имена она определяет правильно)
При компиляции проекта в MS Visual Studio 2008 ошибок не возникало... Помогите.


Время: 20:16.

Время: 20:16.
© OSzone.net 2001-