Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Результат_2012_02_09.docx
Скачиваний:
6
Добавлен:
20.04.2015
Размер:
593.36 Кб
Скачать

Задача 5. В файле удалить все слова, которые начинаются и заканчиваются одной буквой (без использования потоков и std::string)

Следует отметить, что данную задачу можно решить другим способом, более близкому к программированию на чистом C. Для решения другим способом будем использовать строки с завершающим нулем. Для работы с файлами воспользуемся типом FILE *. Это очень сильно изменит реализацию решения.

  1. При решении задачи одна из главных задач – получить содержимое файла. Следует отметить, что функция feof может указывать на достижение конца файла (FILE *). Файл можно открыть на чтение (read) или запись (write) или дозапись (append). Файл можно открыть как бинарный (binary) или текстовый (text). Для открытия файла необходимо воспользоваться функцией fopen, которая имеет два аргумента. Первый – имя файла. Второй – тип открытия файла. Тип открытия состоит из двух букв (хотя может состоять и из одной и из трех). Первая буква – первая буква типа открытия (read, write, append), вторая – тип файла (binary, text). Если файл не открылся, то fopen возвращает NULL. Для закрытия файла необходимо воспользоваться функцией fclose. Основа открытия файлов будет выглядить следующим образом:

void processFile(char *infileName, char *outFileName) {

FILE *fin = fopen(infileName, "rt");

if (fin == NULL) {

return;

}

FILE *fout = fopen(outFileName, "wt");

if (fout == NULL) {

fclose(fin);

return;

}

//обработка файла

fclose(fin);

fclose(fout);

}

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

Название

Что делает

strlen

Возвращает длину строки без завершающего нуля

strcmp

Сравнивает строки. Возвращает число, меньшее 0, если первая строка меньше второй; 0, если строки равны; число, больше 0, если первая строка больше второй

strchr

Возвращает указатель на первое вхождение символа в строку. NULL, если символ не входит

strcat

Объединяет строки

atof

Преобразует строку в дробное число

atoi

Преобразует строку в целое число

strstr

Ищет подстроку в строке. Возвращает указатель на первое вхождение подстроки. NULL, если подстрока не найдена

strcpy

Копирует одну строку в другую (включая завершающий ноль)

itoa

Выполняет преобразование целого числа в строку

ftoa

Выполяет преобразование дробного числа в строку

  1. Теперь встает вопрос об обработке файла. Для этого необходимо получить строку из файла. Однако заранее не известен её размер, поэтому будем считывать её посимвольно, пока не встретим enter (‘\n’) или не дойдем до конца файла. Изначально строка пустая. Но с учетом того, что в ней находится завершающий 0, необходимо выделить один байт. Каждый новый считанный символ необходимо добавлять в конец строки. Полученную строку можно записать в выходной файл с помощью функции fputs. Поэтому алгоритм будет выглядить следующим образом:

void addCharToEnd(char *&str, char newC) {

//находим длинну строки без завершающего нуля

int oldLen = strlen(str);

//+1 - на новый символ

//+1 - на завершающий 0

char *newStr = new char[oldLen + 1 + 1];

//копируем данные из str в newStr

strcpy(newStr, str);

//добавляем новый символ

newStr[oldLen] = newC;

//ставим завершающий 0

newStr[oldLen + 1] = 0;

delete[] str;

str = newStr;

}

char *getStrFromFile(FILE *&fin) {

char *res = new char[1];

res[0] = 0;

while (true) {

char curChar = fgetc(fin);

if (feof(fin)) {

break;

}

addCharToEnd(res, curChar);

if (curChar == '\n') {

break;

}

}

return res;

}

void processLine(char *&str) {

//реализация логики по обработке одной строки

}

void processFile(char *infileName, char *outFileName) {

FILE *fin = fopen(infileName, "rt");

if (fin == NULL) {

return;

}

FILE *fout = fopen(outFileName, "wt");

if (fout == NULL) {

fclose(fin);

return;

}

while (true) {

char *loadedStr = getStrFromFile(fin);

processLine(loadedStr);

fputs(loadedStr, fout);

delete[] loadedStr;

if (feof(fin)) {

break;

}

}

fclose(fin);

fclose(fout);

}

  1. Теперь необходимо реализовать алгоритм обработки одной строки. Следует отметить, что в результате обработки строки строка не может увеличиться, поэтому для увеличения скорости работы мы не будем перевыделять память во время её обработки.

  2. Проверка на то, является символ разделителем или нет, остается почти такой же (только в разделители необходимо добавить символы ‘\n’ и ‘\r’). Проверка на то, является ли символ с номером index в строке str по логике остается такой же. Но при нахождении длинны слова необходимо использовать тот факт, что в конце строки находится завершающий ноль, т.е. символ с кодом ноль. С учетом всех этих рассуждений реализация данных функций будет следующая:

bool isSeparator(char z) {

return strchr(" .,\\&!~/\n\r", z) != NULL;

}

bool isBeginOfWord(char *str, int index) {

if (isSeparator(str[index])) {

return false;

}

if (index == 0) {

return true;

}

return isSeparator(str[index - 1]);

}

int getWordLength(char *str, int startIndex) {

int res = 0;

for (int i = startIndex; str[i] != 0; i++) {

if (isSeparator(str[i])) {

break;

}

res++;

}

return res;

}

  1. Для удаления слова необходимо переместить все данные, которые находятся после удаляемого слова на размер удаляемого слова. При этом завершающий ноль так же необходимо перенести. В кодах это будет выглядить следующим образом:

void deleteWord(char *str, int startWord, int lengthWord) {

for (int i = startWord + lengthWord; true; i++) {

str[i - lengthWord] = str[i];

if (str[i] == 0) {

break;

}

}

}

  1. Теперь вернемся к обработке строки. Надо пройти по всей строке, находить начала и длины слов и их удалять по условию. Для увеличения скорости работы вычислим длину строки только в самом начале функции и при удалении слова будем её уменьшать на необходимую величину. В кодах это будет реализовано следующим образом:

void processLine(char *str) {

int strLen = strlen(str);

for (int i = 0; i < strLen; i++) {

if (isBeginOfWord(str, i) == false) {

continue;

}

int len = getWordLength(str, i);

if (str[i] != str[i + len - 1]) {

i += len;

continue;

}

deleteWord(str, i, len);

strLen -= len;

}

}

  1. Приведем весь код программы:

#include <string.h>

#include <stdio.h>

bool isSeparator(char z) {

return strchr(" .,\\&!~/\n\r", z) != NULL;

}

bool isBeginOfWord(char *str, int index) {

if (isSeparator(str[index])) {

return false;

}

if (index == 0) {

return true;

}

return isSeparator(str[index - 1]);

}

int getWordLength(char *str, int startIndex) {

int res = 0;

for (int i = startIndex; str[i] != 0; i++) {

if (isSeparator(str[i])) {

break;

}

res++;

}

return res;

}

void deleteWord(char *str, int startWord, int lengthWord) {

for (int i = startWord + lengthWord; true; i++) {

str[i - lengthWord] = str[i];

if (str[i] == 0) {

break;

}

}

}

void addCharToEnd(char *&str, char newC) {

//находим длинну строки без завершающего нуля

int oldLen = strlen(str);

//+1 - на новый символ

//+1 - на завершающий 0

char *newStr = new char[oldLen + 1 + 1];

//копируем данные из str в newStr

strcpy(newStr, str);

//добавляем новый символ

newStr[oldLen] = newC;

//ставим завершающий 0

newStr[oldLen + 1] = 0;

delete[] str;

str = newStr;

}

char *getStrFromFile(FILE *&fin) {

char *res = new char[1];

res[0] = 0;

while (true) {

char curChar = fgetc(fin);

if (feof(fin)) {

break;

}

addCharToEnd(res, curChar);

if (curChar == '\n') {

break;

}

}

return res;

}

void processLine(char *str) {

int strLen = strlen(str);

for (int i = 0; i < strLen; i++) {

if (isBeginOfWord(str, i) == false) {

continue;

}

int len = getWordLength(str, i);

if (str[i] != str[i + len - 1]) {

i += len;

continue;

}

deleteWord(str, i, len);

strLen -= len;

}

}

void processFile(char *infileName, char *outFileName) {

FILE *fin = fopen(infileName, "rt");

if (fin == NULL) {

return;

}

FILE *fout = fopen(outFileName, "wt");

if (fout == NULL) {

fclose(fin);

return;

}

while (true) {

char *loadedStr = getStrFromFile(fin);

processLine(loadedStr);

fputs(loadedStr, fout);

delete[] loadedStr;

if (feof(fin)) {

break;

}

}

fclose(fin);

fclose(fout);

}

int main(int argc, char* argv[]) {

processFile("labin.txt", "labout.txt");

return 0;

}