Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Чтение текста из файла  (Прочитано 11026 раз)
0 Пользователей и 1 Гость смотрят эту тему.
sd
Постоялец

by
Offline Offline

« : 26-10-2011 16:25 » 

Всем привет. Задание такое: создать программу обработки учебника по программированию с использованием классов: символ, слово, предложение и т.п. Заменить табуляции и последовательности пробелов одним пробелом.
То есть, у меня есть файл с текстом (см. в аттаче). Необходимо прочитать текст из этого файла и обработать его.
Я написал класс слово (Word.h):
Код:
class Word {
char *wrd;
int len;

void SetWord(char *word);
public:
Word(void): wrd(0), len(0) {}
Word(char *word);
Word(const Word &obj);
~Word(void);

int GetLen(void) const {return len;}
int CharCount(char ch) const;
char GetFirstSymbol(void) const {return *wrd;}
void GetWord(char *string, int begindx, int endindx);

friend ostream &operator<<(ostream &stream, Word &obj);
friend istream &operator>>(istream &stream, Word &obj);
Word operator=(const Word &obj);
Word operator=(char *word);
};
и класс предложение, являющееся массивом слов и завершающееся одним из "завершителей" (!, ., ?).
Sentence.h
Код:
class Sentence {
int len;
int size;
Word *wrds;
char *sntnce;

void SetSentence(char *sentence);
bool IsSeparator(char symbol);
void GetWords(void);
void DelRepeatSpaces(char *sentence);
public:
Sentence(void): len(0), size(0), wrds(0) {}
Sentence(char *sentence);
Sentence(const Sentence &obj);
~Sentence(void);

int GetWordsCount(void);
int GetLen(void) const {return strlen(sntnce);}

friend ostream &operator<<(ostream &stream, Sentence &obj);
friend istream &operator>>(istream &stream, Sentence &obj);
Sentence operator=(const Sentence &obj);
Sentence operator=(char *sentence);
};
А вот что делать дальше - я не придумал. Надо создать класс текст, который будет массивом предложений. Проблема в том, что бы считать из файла количество предложений, выделить необходимую память для всех предложений и записать в каждый из элементов массива предложений эти предложения. Плюс ко всему надо сохранить пунктуацию, т.е. обзацы.
Подскажите пожалуйста, как реализовать класс текст.

* c++.txt (1.7 Кб - загружено 178 раз.)
Записан
DneprSMV
Помогающий

kz
Offline Offline
Пол: Мужской

« Ответ #1 : 26-10-2011 17:24 » 

а почему ты не используешь хотябы CString, CStringArray или подобное - там хотябы задача прибивания пробелов или других повторов решается одним вызовом. Массивы, списки ?
Записан

"Не слушайте никаких советов, в том числе и этот" (Сократ ?)
sd
Постоялец

by
Offline Offline

« Ответ #2 : 26-10-2011 17:26 » 

а почему ты не используешь хотябы CString, CStringArray или подобное - там хотябы задача прибивания пробелов или других повторов решается одним вызовом. Массивы, списки ?
Да вопрос не в том, что бы использовать имеющиеся классы типа CString. Меня и мой класс с моими функциями устраивает. Тут вопрос в том, как все это прочитать из файла и загрузить в массив предложений.
Записан
DneprSMV
Помогающий

kz
Offline Offline
Пол: Мужской

« Ответ #3 : 26-10-2011 17:44 » 

так в чем проблема, если разбирать файл "с лету" - fgetc(),
если через буфер (считать весь файл в память, затем разбор) - fgets()  ?
Или ты имеешь ввиду выделение из потока ?

ps  (?)
редактор:абзац   : предложение: слово
        :формат  : разметка  . . . .
        :разметка . . . .

« Последнее редактирование: 26-10-2011 17:51 от DneprSMV » Записан

"Не слушайте никаких советов, в том числе и этот" (Сократ ?)
sd
Постоялец

by
Offline Offline

« Ответ #4 : 26-10-2011 17:49 » 

Проблема в том, что размер файла я заранее не знаю. И не знаю, какую память выделять под буфер. Потом, в буфер считается часть файла до перехода на новую строку, т.е. 1 обзац.
Записан
DneprSMV
Помогающий

kz
Offline Offline
Пол: Мужской

« Ответ #5 : 26-10-2011 17:52 » 

это учебная задача или реально, где неизвестно какой будет файл - 1 к или 1 гиг ?


Добавлено через 23 минуты и 57 секунд:
Проблема в том, что размер файла я заранее не знаю. И не знаю, какую память выделять под буфер. Потом, в буфер считается часть файла до перехода на новую строку, т.е. 1 обзац.
Смотря как будешь считывать и из какого файла. Если обычный текстовый - у тебя считвывается не "абзац" а текстовая строка, и не обязательно это предложение.
Сторка - завершается символами 0x0D 0x0A (или просто 0x0A).
Если такой формат - используй fgets()-fgetc()  
char str1[500];
while( !feof(fd) ) int rc = fgets(str1,500, fd );
Если формат который оперирует абзацами, разметкой итд - напр. RTF - то самому писать будет тяжко и бессмысленно.

------------
в твоем файле в строке (в смысле ограничения 0D-0A) записан абзац.
За одно fgets считывание в буфере - соответственно абзац.
Длину строки-абзаца принимай из более-менее разумной - например до нескольких кб.
Хотя можно узнать хар-ки файла - длину, выделить память по длине, и считать файл в двоичном режиме.
Далее все операции - с памятью.
Можно считать-обработать и в режиме фильтра, но это сложнее.
« Последнее редактирование: 26-10-2011 18:16 от DneprSMV » Записан

"Не слушайте никаких советов, в том числе и этот" (Сократ ?)
sd
Постоялец

by
Offline Offline

« Ответ #6 : 26-10-2011 18:31 » 

Задача учебная.
Файл я прикрепил в первом сообщении, он обычный текстовый.
Т.е., после
char str1[500];
while( !feof(fd) )  {
int rc = fgets(str1,500, fd );
GetSentencesFrom(str1);
}
в переменой str1 я получу абзац? и потом смогу разбить его на предложения. И таким образом считаю все абзацы и разделю их на предложения?
Записан
RuNTiME
Помогающий

ru
Offline Offline
Пол: Мужской

« Ответ #7 : 26-10-2011 18:48 » 

sd, если задача учебная и нет особых претензий к производительности и эффективности программы, то можно использовать функцию realloc(), которая динамически изменяет размер заранее выделенного блока памяти функцией malloc() и возвращает указатель на новый блок.

Т.е. общая стратегия получается таковой (цифры могут быть и другими):
1. Если есть еще строки, выделяем блок памяти под новую строку размером 512 байт
2. Считываем из файла строку в этот буфер, если конец строки не найден, а буфер закончился увеличиваем блок памяти функцией realloc() еще на 512 байт и продолжаем считывать строку до тех пор, пока не найдем конец строки.
3. Нашли конец строки, идем в п.1

Таким образом можно считать весь файл, что каждая строка будет храниться в отдельном блоке памяти.

Ну либо использовать готовый класс, как писал DneprSMV. В них уже реализован подобный алгоритм. Да и чтобы не привязывать свою программу к MFC и другим библиотекам реализующим CString посоветую использовать классы из STL к примеру std::string.

P.S: Информация по функциям выделения и освобождения памяти: http://www.cplusplus.com/reference/clibrary/cstdlib/malloc/
« Последнее редактирование: 26-10-2011 18:54 от RuNTiME » Записан

Любимая игрушка - debugger ...
DneprSMV
Помогающий

kz
Offline Offline
Пол: Мужской

« Ответ #8 : 26-10-2011 19:23 » 

sd,
Задача учебная.
Файл я прикрепил в первом сообщении, он обычный текстовый.
Т.е., после
char str1[500];
while( !feof(fd) )  {
int rc = fgets(str1,500, fd );
GetSentencesFrom(str1);
}
в переменой str1 я получу абзац? и потом смогу разбить его на предложения. И таким образом считаю все абзацы и разделю их на предложения?
Да, помоему этого достаточно (не буквально - надо пару операторов дописать - работа с файлом).
Возьми хелповский пример по fgets. осле fgets() в буфере получаешь считанную строку (в содержательном смысле - это абзац из документа). он заканчивается символом 0x00.

RuNTiME,  Во входном файле могут быть строки произвольной длины - как раз бы прошло
CString - чтобы не отвлекаться на обработку-укладку строк наразвес.
Достаточно один раз выделить буфер достаточного размера, и использовать его повторно в цикле, а уже разобранную из него информацию упаковывать в классовую модель со списками-массивами, выделением памяти.
За STL - да.
Записан

"Не слушайте никаких советов, в том числе и этот" (Сократ ?)
RuNTiME
Помогающий

ru
Offline Offline
Пол: Мужской

« Ответ #9 : 26-10-2011 19:33 » 

Цитата
Достаточно один раз выделить буфер достаточного размера, и использовать его повторно в цикле
DneprSMV, Безусловно можно, я всего лишь предложил самый наипростейший вариант. Повторно использовать память можно и нужно.

Если говорить о парсинге чего - либо, то можно вообще обойтись буфером фиксированного размера для считывания и разбора файла и динамическими строками для хранения информации в классах.
Записан

Любимая игрушка - debugger ...
DneprSMV
Помогающий

kz
Offline Offline
Пол: Мужской

« Ответ #10 : 26-10-2011 20:24 » 

class MyTextProcesor { . . .  } 
MyTextProcesor mtp;
int err;
mtp.Clear();
err = mtp.LoadFile("C++.txt");
err = mtp.Parse();
if(err) .....
 else
  mtp.PrintResult();

 



Записан

"Не слушайте никаких советов, в том числе и этот" (Сократ ?)
sd
Постоялец

by
Offline Offline

« Ответ #11 : 26-10-2011 21:43 » 

А можно чуть чуть подробнее про функции LoadFile и Parse?
Записан
DneprSMV
Помогающий

kz
Offline Offline
Пол: Мужской

« Ответ #12 : 27-10-2011 08:32 » 

sd, Улыбаюсь
так как успехи ? выкладывай Улыбаюсь
LoаdFile - загрузка текста из файла в память (полностью). Выделение памяти в рамках class MyTextProcesor
Parse - собственно разбор загруженной информации на абзацы-предложения-слова и уапковка в объекты class Word, Sentence + ....
Можно еще добавить .TrimSpace() и .TrimTab() (или могут входить как protected и отрабатывать скрыто при загрузке файла)
Записан

"Не слушайте никаких советов, в том числе и этот" (Сократ ?)
sd
Постоялец

by
Offline Offline

« Ответ #13 : 27-10-2011 18:05 » 

Хотел сделать так:
Код:
Txt::Txt(char *filename) {
ifstream in(filename);
char str[1024];

if(!in) {
cout<<"Can not open file "<<filename;
exit(1);
}

int count=0, len=0, indx=0, size=0;
char ch;
             //Считаем количество предложений
while((ch=in.get())!=EOF) {
size++;

if(isEndOfSentence(ch))
count++;
}

sntnces=new Sentence[count]; //выделяем для них памят

in.close();
in.open(filename);

if(!in) {
cout<<"Can not open file "<<filename;
exit(1);
}
              //выделяем предложения и выводим их на экран
while((ch=in.get())!=EOF) {
len=1;
while(!isEndOfSentence(ch)) {
len++;
ch=in.get();
}
int pos=in.tellg();
in.seekg(pos-len, ios::beg);
in.getline(str, len+1);
cout<<str<<endl;
in.seekg(pos+1, ios::beg);
}
in.close();
}

Проблема в том, что выводится только первое предложение, а дальше нет.
Записан
Вад
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #14 : 27-10-2011 20:27 » 

А зачем так сложно - открывать файл, пересчитывать предложения, второй раз открывать и пытаться читать?

Я бы делал всё сразу на ходу.
И, поскольку задача пока учебная, забьём пока на слишком длинные предложения, переполняющие буфер. Или простой буфер на что-нибудь, контролирующее свою длину - std::deque, например. Или вообще std::string.

Получится что-то типа
Код:
Sometype buf;
std::ifstream input(name);
char c;
while (!input.eof()) {
  input.get(c);
  if (isSeparator(c) && buf.size()) {
    // перекладываем содержимое буфера куда надо - это наше предложение.
    // и очищаем буфер
  }
  else {
    // кладём c в конец буфера
  }
}
Кстати, сам разделитель тоже надо добавлять в предложение, или нет? Я исходил из того, что не надо.

Добавлено через 3 минуты и 8 секунд:
ЗЫ. Кстати, не обязательно повторно открывать файловый поток. Можно просто сделать seekg в начало файла.
« Последнее редактирование: 27-10-2011 20:30 от Вад » Записан
DneprSMV
Помогающий

kz
Offline Offline
Пол: Мужской

« Ответ #15 : 28-10-2011 09:18 » 

sd,
по-моему сложновато. Ты все операции хочешь проделать в конструкторе ?
(по большому счету, он в начале может быть вообще пустой, Txt::Txt() {) ).
Твой текст состоит из абзацев, абзац состоит из предложений, предложения состоят из слов и знаков препинания. Символы веделять в качестве объектов - можно да(а смысл ?) наподобие объекта pixel::pixel(x.y) в графике)  а можно нет . +форматирование.
--------
Как бы я делал помолчу, тк. не хочу испортить "обедню" своей метОдой  Улыбаюсь
Но в любом случае вначале надо выделить классы, их методы-интерфейсы.
У тебя в конструкторе совмещены 2 операции - чтение файла и его обработка.
У меня некоторые представления, что в конструкторе операции по выделению памяти и инициализации объекта. Чтение и обработку я бы вынес в методы-функции. Или "верх" ты стремишься свести к
Txt MyTxt("file"); MyTxt.Yahoo(); ?
Если при работе конструктора произойдет ошибка - кто ее обработает ? (кроме exit(1) )
Обработанную-выделенную инф я бы располагал в виде списков
текст - список абзацев
абзац - список предложений
предложение - список слов и знаков пунктуации
с методами Txt.GetAbzatz(....) Abzatz.GetSentence(....) Sentence.GetWord(...) или нечто подобное.
У тебя есть ф-ии GetWord(). Попробуй черново прописать программу "сверху" - с красивых ворот Улыбаюсь
Отладочную печать удобно делать не через поток, а printf - если Win32 Console
printf("Отладка строка=<%s> n=%d", ptr_string, n); // n - число int
Записан

"Не слушайте никаких советов, в том числе и этот" (Сократ ?)
sd
Постоялец

by
Offline Offline

« Ответ #16 : 28-10-2011 21:18 » 

Сделал так как сделал. Вроде сдал и вроде прокатило. Спасибо вам.
Сейчас начну делать след. задание - написание словаря. Буду обращаться наверное.
Записан
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines