Помощь - Поиск - Пользователи - Календарь
Полная версия: Ошибка в коде программы(С++)
Форум «Всё о Паскале» > Современный Паскаль и другие языки > Ада и другие языки
V.k.l.chr.by
Будьте добры,уважаемые форумчане можете исправить ошибку в данном коде программе.Уж очень срочно надо.Буду очень признателен.А вот и само условие и код программы:
Исходные данные находятся в текстовом файле. Разделитель – конец строки. Программа запрашивает имя входного и результирующего (отсортиро-ванного) файла. Использовать динамическое распределение памяти.И надо сортировать строки методом пузырька.Тут как бы всё правилььно выполняет,только вот пишет ошибку .. и как мне подсказывали что-то с указателями.Или после кода программы"void main(void)..."


#include<stdio.h>
#include<windows.h>
#include<string.h>
#include<iostream.h>

#define maxline 1000

void sort(char* ptr[],int n_lines)
{
int i,j;
char *tmp;
for(i=0;i<n_lines-1;i++)

for(j=0;j<n_lines-1;j++)

if(strcmp(ptr[j],ptr[j+1])>0)
{
tmp=ptr[j];
ptr[j]=ptr[j+1];
ptr[j+1]=tmp;
}


}
void write_lines(char* ptr[],int n_lines)
{
int i;
char *str = new char[];
for(i=0;i<n_lines;i++)
{
CharToOem(ptr[i],str);
cout<<str<<endl;

}
}

void write_in_file(char* ptr[],int n_lines, FILE* fp)
{
int i;
char *str = new char[];
for(i=0;i<n_lines;i++)
{
fputs(ptr[i],fp);
fputs("\n",fp);


}
}

void main(void)
{

char s[] = "Введите путь к файлу : ";
CharToOem(s,s);
char r[] = "Введите путь к отсортированному файлу : ";
CharToOem(r,r);
char a[] = "Ошибка при открытии файла\n";
CharToOem(a,a);
char ns[] = "До сортировки :";
CharToOem(ns,ns);
char ps[] = "После сортировки :";
CharToOem(ps,ps);


FILE *in;
char *str = new char[];
char *name = new char[];
int counter=0;
char buf[maxline];
char *ptr[maxline];
cout<<s;
cin>>name;

if((in=fopen(name,"rt"))==NULL)
{
cout<<a;
return;
}

FILE *fp;
char *nam = new char[];
cout<<r;
cin>>nam;
fp=fopen(nam,"wt");

for(counter=0;(!feof(in))&&counter<maxline;counter++)
{
fgets(buf,maxline,in);
if(buf[strlen(buf)-1]=='\n')
{
buf[strlen(buf)-1]='\0';
}
ptr[counter] = new char[strlen(buf)+1];
strcpy(ptr[counter],buf);
buf[0]='\0';

}

fclose(in);
cout<<ns<<endl;
write_lines(ptr,counter);
cout<<endl<<endl<<ps<<endl;
sort(ptr,counter);
write_lines(ptr,counter);
write_in_file(ptr,counter,fp);
fclose(fp);

}
P.S.Прошу прощение,за повтор темы.Хотел удалить ,но не нашёл.
volvo
Можно уточнить? зачем понадобилось перемешивать чистый С и C++ (ну, например, работать в одной программе и с FILE* и с потоками) ? Если у тебя С++, то работай с ifstream/ofstream, это ж проще гораздо...

К тому же С++ не позволяет делать void main(), функция main должна возвращать результат типа int...

А вот это:
char *nam = new char[];

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

А вот задание уточни: каков критерий сортировки? Строки можно сортировать по алфавиту, можно - по количеству слов, можно - по числу знаков препинания, да мало ли еще как...
V.k.l.chr.by
Вообщем я просто брал нечто похожую программу с лабораторной работы и вставлял и вышел такой просяк.Да и где-то помогали мне.Так что решил когда заработает программа,тогда и каждую строчку попробую разобраться.А сортироватьться строки будут по буквам как в словари.Тоетсь первая буква перовй строки с второй строки первой буквы.
volvo
Цитата
Так что решил когда заработает программа,тогда и каждую строчку попробую разобраться
Неправильно... Разбираться надо, ЧТОБЫ программа заработала...

Попробуй разобраться:
#include <windows.h>

#include <iostream>
#include <fstream>

using namespace std;

void write_lines(ostream& os, char* ptr[], int n_lines) {
for(int i = 0; i < n_lines; i++) {
char *str = strdup(ptr[i]);
CharToOem(str, str);
os << str << endl;
free(str);
}
}

void sort(char* ptr[], int n_lines) {
for(int i = 0; i < n_lines; i++) {
for(int j = n_lines - 1; j > i; j--) {
if(strcmp(ptr[j - 1], ptr[j]) > 0) {
char *tmp = ptr[j-1];
ptr[j-1] = ptr[j];
ptr[j] = tmp;
}
}
}
}


int main()
{

char s_in_name[] = "Введите путь к файлу : ";
// CharToOem(s_in_name, s_in_name);

char s_out_name[] = "Введите путь к отсортированному файлу : ";
// CharToOem(s_out_name, s_out_name);

char s_err_open[] = "Ошибка при открытии файла\n";
// CharToOem(s_err_open, s_err_open);

char s_before[] = "До сортировки :";
// CharToOem(s_before, s_before);

char s_after[] = "После сортировки :";
// CharToOem(s_after, s_after);

char in_name[64], out_name[64];

const int maxLines = 1000;
char *ptr[maxLines];


cout << s_in_name;
cin >> in_name;

ifstream in(in_name, ios::in);
if(!in) {
cout << s_err_open << endl;
return -1;
}

cout << s_out_name;
cin >> out_name;

ofstream out(out_name, ios::out);
if(!out) {
cout << s_err_open << endl;
return -1;
}


const int buf_size = 1024;

char buffer[buf_size];
int current = -1;
while(in.getline(buffer, buf_size)) {
ptr[++current] = new char[strlen(buffer) + 1];
strcpy(ptr[current], buffer);
}

cout << s_before << endl;
write_lines(cout, ptr, current + 1);
sort(ptr, current + 1);
cout << s_after << endl;
write_lines(cout, ptr, current + 1);

write_lines(out, ptr, current + 1);
for(int i = 0; i <= current; i++) {
delete ptr[i];
}

return 0;

}
В принципе, тут можно было обойтись вообще десятком строк для сего, что делается в программе, но ты сам хотел char* и динамическое выделение памяти...

P.S. CharToOem может не сработать, поэтому я ее закомментировал. Попробуй...
V.k.l.chr.by
Просто в условии дано ,чтобы было динамичесткое распределение памяти.Что ж кажись всё правильно.Только вот ,если русские символы в текстовом файле находяться,то в новом файле после сортирвоке появляются непонятные символы.Не мог ли бы исправить.А так ,если английские символы всё отлично.Большое ,спасибо!
V.k.l.chr.by
Проверил ещё раз,всё ровно русские символы не сортируютсяв текстовом файле(непонятные ироглифы),а сама сортирвока в чёрнмом окошке всё нормально.Не помогли ли Вы помочь разобраться?Я так понимаю,чт окажись загвоздка в строке
Код

  CharToOem(str, str);

Так как ,я взял и данную строку закомментировал и уже файл нормально отсортировался,но возникла проблемав черном окошке(непоняные ироглифы)
volvo
Цитата
я взял и данную строку закомментировал и уже файл нормально отсортировался,но возникла проблемав черном окошке(непоняные ироглифы)
Ну, так возьми и чуть-чуть подкорректируй write_lines, чтобы CharToOem вызывалась только тогда, когда вывод происходит на экран:
void write_lines(ostream& os, char* ptr[], int n_lines) {
for(int i = 0; i < n_lines; i++) {
char *str = strdup(ptr[i]);

if(os == cout) { // <--- Вот эта строка
CharToOem(str, str);
}

os << str << endl;
free(str);
}
}
V.k.l.chr.by
Ух..выручили Вы меня всё тепреь работает!!Ура с победой!!Та кже небольшйо вопрос.. а можно задавать вопросы по задачке.Просто как моей помощи мало было.Поэтмоу ,чтобы защитить эту задачку за зачёте,хотелсоь разобрат ькаждую строчку.Так что если будут вопрсоы можно задавать?
volvo
Цитата
Так что если будут вопрсоы можно задавать?
Задавай, конечно... Для этого форум и существует - чтобы помочь разобраться с тем, что непонятно...
V.k.l.chr.by
И так мне уже в начале не понятно первые строчки.А именно:"void write_lines(ostream& os, char* ptr[], int n_lines) {
for(int i = 0; i < n_lines; i++) {
char *str = strdup(ptr[i]);"
Если можно каждую строчку прокоментировать.Само слово void я так понял -это типо функция которая не будет возращат значение.В даном случае её название"write_lines"А дальше не понял.Но там я та кпонял что-то связано с указателями.Я не очень и так понял про указатели.Если вообщем,то они выделяют память под объект,если так,тогда когда пишем простые программы мы вообще не использовали память.Тогда вопрос,откуда появлялась память,в тех простых программах?


volvo

// функция write_lines описывается как void - не возвращающая результата
// принимает 3 параметра:
// 1) ссылку на поток вывода, куда собственно будет производиться вывод
// 2) массив указателей на строки
// 3) число строк
void write_lines(ostream& os, char* ptr[], int n_lines) {

// для всех номеров строк от 0 до n_lines - 1 (в С++ индексация всегда начинается с 0)
for(int i = 0; i < n_lines; i++) {

// при помощи библиотечной функции strdup выделяется память, необходимая
// для хранения i-той строки массива, и сама строка копируется в динамическую память
// (фактически - создается копия строки)
char *str = strdup(ptr[i]);

// если в функцию был передан поток cout (связанный с консолью), то ...
if(os == cout) {
// конвертируем копию строки в OEM, для правильного отображения
CharToOem(str, str);
}

// и выводим копию строки в поток (это может быть не только консоль,
// но и любой поток вывода, скажем, файл)...
os << str << endl;

// и освобождаем память, удаляя копию строки, она нам больше не понадобится
free(str);
}
}


Цитата
Тогда вопрос,откуда появлялась память,в тех простых программах?
Ну, если программки были совсем простые, значит все переменные создавались статически, то есть вся необходимая память выделялась еще при компиляции...
V.k.l.chr.by
Вообщем всё понятно,но возникли небольшие вопросики.
1-Почему,в коде программы нельзя было просто написать до N,а не n_lines?
2-И у Вас в комментарии написано,что "до n_lines - 1"Причём тут единица.
3-И если не трудно,можно ещё пару слов об "os"ЧТо это такое?Я как понял,это назваине потока,благодаря чему будет копироваться троки в файл.Я так понял?

-
// для всех номеров строк от 0 до n_lines - 1 (в С++ индексация всегда начинается с 0)
for(int i = 0; i < n_lines; i++) {


Я так подумал вышлю остальной код программы(если не трудно прокментировать его),чтобы после сего,по частям разбарал бы и если что вопрсоызадавал бы.Думаю так быстрее будет,чем по частям высылать и так же уже нужно скоро уже относить программу.
p/S/Я ещё раз хочу выразить благодарность за помощь студенту.
-
void sort(char* ptr[], int n_lines) {
for(int i = 0; i < n_lines; i++) {
for(int j = n_lines - 1; j > i; j--) {
if(strcmp(ptr[j - 1], ptr[j]) > 0) {
char *tmp = ptr[j-1];
ptr[j-1] = ptr[j];
ptr[j] = tmp;
}
}
}
}


int main()
{

char s_in_name[] = "Введите путь к файлу : ";
CharToOem(s_in_name, s_in_name);

char s_out_name[] = "Введите путь к отсортированному файлу : ";
CharToOem(s_out_name, s_out_name);

char s_err_open[] = "Ошибка при открытии файла\n";
CharToOem(s_err_open, s_err_open);

char s_before[] = "До сортировки :";
CharToOem(s_before, s_before);

char s_after[] = "После сортировки :";
CharToOem(s_after, s_after);

char in_name[64], out_name[64];

const int maxLines = 1000;
char *ptr[maxLines];


cout << s_in_name;
cin >> in_name;

ifstream in(in_name, ios::in);
if(!in) {
cout << s_err_open << endl;
return -1;
}

cout << s_out_name;
cin >> out_name;

ofstream out(out_name, ios::out);
if(!out) {
cout << s_err_open << endl;
return -1;
}


const int buf_size = 1024;

char buffer[buf_size];
int current = -1;
while(in.getline(buffer, buf_size)) {
ptr[++current] = new char[strlen(buffer) + 1];
strcpy(ptr[current], buffer);
}

cout << s_before << endl;
write_lines(cout, ptr, current + 1);
sort(ptr, current + 1);
cout << s_after << endl;
write_lines(cout, ptr, current + 1);

write_lines(out, ptr, current + 1);
for(int i = 0; i <= current; i++) {
delete ptr[i];
}

return 0;

}
volvo
Цитата(V.k.l.chr.by @ 15.05.2008 22:03) *
1-Почему,в коде программы нельзя было просто написать до N,а не n_lines?
Ну, если тебе удобнее до N, то пиши до N, я привык давать "говорящие" имена переменным...
Цитата(V.k.l.chr.by @ 15.05.2008 22:03) *
в комментарии написано,что "до n_lines - 1"Причём тут единица.
При том, что если число строк это n_lines, то значит, их индексы - от 0 до (n_lines-1)
Цитата(V.k.l.chr.by @ 15.05.2008 22:03) *
можно ещё пару слов об "os"ЧТо это такое?
Это поток вывода, os (сокращение от output stream)... Понимаешь в чем дело... Когда запускается программа на С++, создаются несколько потоков. Один (cout, стандартный вывод на экран) - для вывода данных, другой (cin, стандартный ввод с клавиатуры) - для ввода данных в программу. Ну, есть еще cerr/clog, но это пока не важно... Так вот, cout имеет тип ostream. От этого же типа унаследован и файловый поток (ofstream), то есть, в свою процедуру я могу передать (по правилу совместимости типов) не только сам cout, но и любого его наследника, и если я передам файловый поток, то все то, что должно было бы напечататься на экране, будет выведено в файл... Чем я и воспользовался... Когда мне нужно - вывожу данные на монитор, а когда нужно - в файл...

Теперь что касается остальной программы:

Это - самая обычная сортировка "пузырьком" массива ptr, содержащего n_lines строк. Алгоритм этот найдешь на любом сайте, да хоть у нас в FAQ-е по Паскалю: Методы сортировок

void sort(char* ptr[], int n_lines) {
for(int i = 0; i < n_lines; i++) {
for(int j = n_lines - 1; j > i; j--) {
if(strcmp(ptr[j - 1], ptr[j]) > 0) {
char *tmp = ptr[j-1];
ptr[j-1] = ptr[j];
ptr[j] = tmp;
}
}
}
}




// а это - основная программа
int main()
{

// Сначала задаем все строковые константы и приводим их к виду,
// в котором они нормально отображаются на экране...

char s_in_name[] = "Введите путь к файлу : ";
CharToOem(s_in_name, s_in_name);

char s_out_name[] = "Введите путь к отсортированному файлу : ";
CharToOem(s_out_name, s_out_name);

char s_err_open[] = "Ошибка при открытии файла\n";
CharToOem(s_err_open, s_err_open);

char s_before[] = "До сортировки :";
CharToOem(s_before, s_before);

char s_after[] = "После сортировки :";
CharToOem(s_after, s_after);

// место для имен входного/выходного файлов
char in_name[64], out_name[64];

// максимальное число строк, которые будут обрабатываться.
// Если в файле будет больше строк, чем указано здесь - то массив,
// описанный чуть ниже, переполнится и получишь Access Violation - ошибку доступа
const int maxLines = 1000;

// Вот, собственно, массив для хранения указателей на строки
char *ptr[maxLines];

// запрашиваем у пользователя и вводим имя входного файла
cout << s_in_name;
cin >> in_name;

// открываем файловый поток in для чтения
ifstream in(in_name, ios::in);
if(!in) { // поток не открылся, выходим с ошибкой
cout << s_err_open << endl;
return -1;
}

// запрашиваем и принимаем от пользователя имя выходного файла
cout << s_out_name;
cin >> out_name;

// опять же открываем файловый поток, но теперь уже на запись
ofstream out(out_name, ios::out);
if(!out) { // не открылся - ошибка
cout << s_err_open << endl;
return -1;
}
// максимальное количество символов, читаемое из файла за один раз
const int buf_size = 1024;
// ну, и буфер для временного хранения прочитанной из файла строки
char buffer[buf_size];

// здесь будет номер строки, прочитанной из файла на каждой итерации цикла
// для начала (-1), потом поймешь почему...
int current = -1;

// попытка прочесть до buf_size символов (на самом деле может быть прочитано
// и меньше, поскольку чтение завершится как только встретится символ "\n")
// из файла in в buffer...
// пока эти попытки успешны (т.е., строка читается)
while(in.getline(buffer, buf_size)) {
// увеличиваем номер текущей строки (теперь понятно, почему изначально был (-1)?
// Потому что начинается индексация с 0), и для текущего номера выделяется память,
// достаточная для хранения прочитанной из файла строки...
ptr[++current] = new char[strlen(buffer) + 1];

// как только память выделена - копируем строку из временного буфера в дин. память
strcpy(ptr[current], buffer);
}

// Ну, а дальше - все просто: выводим все прочитанные строки в cout
// (т.е., на монитор), как именно, я уже объяснял
cout << s_before << endl;
write_lines(cout, ptr, current + 1);

// сортируем массив строк (поскольку current содержит индекс последней
// прочитанной строки начиная с 0, а в процедуру сортировки нужно передать
// КОЛИЧЕСТВО строк, то передаем current + 1)
sort(ptr, current + 1);

// и уже отсортированные строки выводим сначала на монитор (cout)...
cout << s_after << endl;
write_lines(cout, ptr, current + 1);

// ... а потом - в файловый поток, открытый нами для записи
write_lines(out, ptr, current + 1);

// Все, строки сохранены, можно дин. память освобождать
for(int i = 0; i <= current; i++) {
delete ptr[i];
}

return 0;

}
По-моему несложно...
V.k.l.chr.by
Спасибо.Но и в правду как бы всё понятно.
Так же хочу вернуться к ...
Цитата

Ну, если программки были совсем простые, значит все переменные создавались статически, то есть вся необходимая память выделялась еще при компиляции...

Я как читил,что каждый тип имеет определённый диапозон.Получатеся,если в простых программах этот диапозон нам вполне хватал,то в случае больших программах как в моей,ей болльше потребуеться диапозон и поэтому и нужны указатели.Правильно ли я расcуждаю?
Так же я хотел вашего совета,как всё таки начитсья правильно составлять алгоритмы для решение той или иной программы.Я к чему,к тому что как бы теория понятно,а вот напрактики чтобы это применить не очень получаеться.То ли у меня слабо мышление,или как бы первый курс и надо побольше решать задач,чтобы набратсья опыта.И меня так же заинтересовало,так где же этот тот же метод пузырбка применяеться,ну можетв програамах каких либо.Может частично он применяется в программе тот же "телефонный справочник"где он сортирует фамилии.И так если не трудно прокоментировать характеристики на данной ссылке
http://forum.pascal.net.ru/index.php?act=Attach&type=post&id=986
Читал,что метод пузырька считается самый легкий способ,но и не очень часто он используется он.Может я благодаря этой характеристе и получу на свой ответ,почему он на столько плох.Думаю Вас не загрузил своим дурацкими вопросами.
volvo
Дело не в диапазоне... Дело в том, что память в программе может выделяться при компиляции и во время работы. Вот, скажем пример программы, которой тоже вроде бы не нужно многое, но без указателей в ней обойтись нельзя (здесь я бы попросил более опытных программистов не напоминать про продвинутые библиотеки языка, я о них не забыл, помню, что можно и без указателей, но на _таком_ уровне лучше об этом не упоминать, иначе в голове останется только каша...): написать программу, которая получит от пользователя число, организует массив из этого количества элементов, и обрабатывает его... Все просто, правда? Но ты не можешь написать так:

// тут подключение заголовочных файлов
int main() {
int n;
cin >> n;

int arr[n];
for(int i = 0; i < n; i++) {
arr[i] := i; // для примера
}
... // работаем с массивом
return 0;
}

, потому что память под массив выделяется при компиляции, а ты еще не знаешь, что введет пользователь...
(вернее, это возможно, но только на определенном компиляторе. Сменишь компилятор - придется переписывать программу, поэтому данный способ лучше не использовать). Придется делать так:

// тут подключение заголовочных файлов
int main() {
int n;
cin >> n;

int *arr = new int[n]; // выделяем в "куче" память под n элементов типа int
for(int i = 0; i < n; i++) {
arr[i] := i; // работаем точно так же, как и со статическим массивом
}
... // работаем с массивом
delete arr; // не забываем освободить память
return 0;
}

Вот тебе и необходимость (вернее одна из необходимостей) использования указателей... Я не буду углубляться в дебри, и не стану говорить ничего про массивы и указатели, про строки - тебе надо бы прочесть какую-нибудь книжку по С++, чтобы понять основы, потом можно будет разговаривать дальше...

Цитата
меня так же заинтересовало,так где же этот тот же метод пузырбка применяеться,ну можетв програамах каких либо
В принципе, он может применяться где угодно - это полноценный метод сортировки, только медленный очень, что и видно из сравнения времени выполнения сортировок разными алгоритмами: сортировка методом "пузырька" выполняется медленнее, чем любым другим методом при любых размерах массивов; чем размер больше, тем отрыв заметнее... Но у него есть и преимущество: он очень простой, и иногда (если надо быстро написать программку, сортирующую небольшой массив) очень даже может использоваться (потому как набирается просто автоматически)...
Это текстовая версия — только основной контент. Для просмотра полной версии этой страницы, пожалуйста, нажмите сюда.