Faltfromoss написав:file->attrib = _A_NORMAL
змінює тільки стан змінної file->attrib, а не файлу на диску.
Ваш К.О.
Так, це я вже допер трохи пізніше після того як відкрив тему
Ви не увійшли. Будь ласка, увійдіть або зареєструйтесь.
Ласкаво просимо вас на україномовний форум з програмування, веб-дизайну, SEO та всього пов'язаного з інтернетом та комп'ютерами.
Будемо вдячні, якщо ви поділитись посиланням на Replace.org.ua на інших ресурсах.
Для того щоб створювати теми та надсилати повідомлення вам потрібно Зареєструватись.
Український форум програмістів → Повідомлення користувача Faltfromoss
Faltfromoss написав:file->attrib = _A_NORMAL
змінює тільки стан змінної file->attrib, а не файлу на диску.
Ваш К.О.
Так, це я вже допер трохи пізніше після того як відкрив тему
Можливо треба права адміністратора для видалення файлів з цими атрибутами
chmod() то в лінухах, на вікнах атрибути можна змінити за допомогою SetFileAttributes()
Ну або запустити attrib
Не знаю. Якщо підключити sys/stat.h, то chmod () в принципі працює - атрибути змінюються, файли видаляються. Застосував саме цей метод, бо думаю, довго щось інше шукати знадобиться
Доброго дня. Виникло деяке питаннячко. По завданню треба написати 3 програми для копіювання, переміщення та видалення файлів. Та за умови якщо файл, що видаляється має атрибут "Тільки для читання", то запитати користувача видаляти його, чи ні або видалити всі, або відмінити операцію. Почав писати "копіювання". І все вже написав, програма чітко праціє із звичайними файлами. Але файли Read Only видалятись не хочуть. Єдине, що спало на думку, враховуючи той матеріал, що нам надали, це вручну змінити атрибут файлу на normal Тобто ідея була така:
if (file->attrib & _A_RDONLY)
{
file->attrib = _A_NORMAL;
remove (path_dest);
_CopyFile (path_source, path_dest);
}
Та як з'ясувалося не все так просто. Слідкуючи за відладчиком, дійсно видно, що file->attrib змінюється на 0, але фактично якщо подивитись у властивостях файлу, то як стояла галка "Тільки для читання", так і стоїть. І файл, звісно, не видаляється, remove (path_dest); повертає -1.
На просторах інтернету, знайшов повідомлення, що є функція chmod(), котра зкидує атрибути файлу, але нам про неї навіть не заїкалися, тому хотілося б щоб хтось підказав як простіше це можна зробити.
PS у тестових файлів, котрі я хотів скопіювати і у котрих я поставив атрибут "Тільки для читання" при перевірці атрибутів
if (file->attrib & _A_RDONLY)
виявлялося, що у них атрибут зовсім не _A_RDONLY, а _A_ARCH (архівний) і вони також не видаляються інструкцією
remove (path_dest);
Отакі справи...
Підказка: а якщо flag буде дорівнювати 0, що поверне функція?
Це ви, мабуть, на відсутність default натякали. Справді, його не вистачало, тепер компілятор не свариться.
І взагалі - пишіть структурні програми. Якщо треба вийти в певному сенсі надзвичайній ситуації - знайдено складношуканий результат чи сталася помилка - тоді так, ставте return. А у вас все заплутано.
Крім того, перед (майже?) усіма return у вас Insert(New). Це - яскравий приклад порушення принципу DRY.
Ну ідея така - за першим кейсом змінюються одразу всі дані, за іншими - по кожному полю окремо.
А Insert я використовую при кожній зміні для того, щоб змінений елемент став на своє нове місце в дереві, а не залишився на старому. Бо так порушиться структура бінарного дерева.
І що таке принцип DRY я, нажаль, не знаю
Ще одне невеличке питання. В класі Tree є метод для редагування даних абонента:
При побудові вирішення, компілятор видає попередження
warning C4715: Tree::ChangeData: значение возвращается не при всех путях выполнения
Я вже все передивився, ретурни стоять де тільки можна, що йому ще треба не можу зрозуміти.
Звісно, це можна структурувати і скоротити, але так ніби найочевидніше, що воно робить.
Дійсно, працює. Все так просто... Я щось перемудрив із сортуванням... В черговий раз дякую
І знову бінарне дерево "Телефонний довідник" . Завдання - пошук записів за телефонним номером та фамілією.
Ще раз нагадаю. Є структура:
struct Subscriber
{
char FIO [50];
int YearOfBirth;
char Town [20];
char Number [15];
Subscriber * left, * right, * parent;
};
В класі Tree є функція, яка додає елемент до списку у визначеному порядку:
void Tree::Insert(Subscriber * z)
{
z->left = NULL;
z->right = NULL;
Subscriber * y = NULL;
Subscriber * Node = root;
while(Node != 0)
{
y = Node;
if(strcmp(z->FIO, Node->FIO) < 0)
Node = Node->left;
else
Node = Node->right;
}
z->parent = y;
if(y == 0)
root = z;
else if(strcmp(z->FIO, y->FIO) < 0)
y->left = z;
else
y->right = z;
}
А саме у алфавітному порядку за ФІО абонентів.
А також є функція пошуку:
Subscriber * Tree::Search(Subscriber * Node, char * k)
{
int n = strlen (k);
while(Node != 0 && strnicmp(k, Node->FIO, n) != 0)
{
if(strnicmp(k, Node->FIO, n) < 0)
Node = Node->left;
else
Node = Node->right;
}
return Node;
}
Пошук також реалізований за полем FIO, як і будувалося дерево. Тут питань немає, все знаходить, все працює.
Але в мене виникає питання, як в цьому дереві тепер організувати пошук за іншими полями? Як, неприклад, в завданні - за номером телефону? Адже дерево відсортовано за полем FIO і тепер якшо просто в функції Search () змінити параметри пошуку, то працювати належним чином вона не буде:
Subscriber * Tree::Search(Subscriber * Node, char * k)
{
while(Node != 0 && strcmp(k, Node->Number) != 0)
{
if(strcmp(k, Node->Number) < 0)
Node = Node->left;
else
Node = Node->right;
}
return Node;
}
В мене з'явилося дві ідеї
1) Відсортувати дерево спочатку за тим полем, за яким треба виконати пошук, і потім знайти запис
2) За допомогою рекурсії обійти все дерево із пошуком портібного значення.
По першому варіанту спробував зробити функцію сортування накшталт такої:
void Tree::Sort (Subscriber * Node, Tree & t)
{
if(Node != 0)
{
Sort(Node->left, t);
t.InsertNumber (Node);
Sort(Node->right, t);
}
}
Де аргумент, переданий у функцію Tree & t це тимчасово створене дерево, куди будуть записуватись відсортовані дані за допомогою функції InsertNumber () (за номером). Але якось так виходить, що перший запис в нове дерево записується, а інші - ні...
По другому варіанту в загалі не розумію як зупинити рекурсію та повернути потрібний знайдений елемент
Ось таке питаннячко. І ще, взагалі, я правильно розмірковую? Чи може вирішення цього питання лежить зовсім в другій площині, про яку я поки що не здогадуюсь?
Заздалегідь дякую
У вас тут є 2 варіанти:
- використовувати зовнішню відносно функції змінну (глобальну, елемент класу, статичну, як радить Arete);
- передавати номер в функцію і повертати новий, десь так:int Tree::Print(Subscriber * Node, int lineCount) { if(Node != 0) { lineCount = Print(Node->left, lineCount); printf("%d. %-35s %-20s %9d %15s\n", ++lineCount, Node->FIO, Node->Town, Node->YearOfBirth, Node->Number); lineCount = Print(Node->right, lineCount); } return lineCount; }
Недолік першого варіанту - функція буде нумерувати рядки без обнулення, тобто якщо вперше вивелось 50 рядків, то другий виклик почнеться з номеру 51. Занулення доведеться або робити в іншій функції (забув викликати - маєш баг), або передавати в функцію окремий параметр "занулити". Втім, це можна (як і в другому варіанті) зробити типовим значенням параметру:
int Tree::Print(Subscriber * Node, int lineCount = 0)//тепер без другого параметру в функцію піде 0
Дуже дякую, використав другий варіант із параметром за умовченням, все запрацювало
Суть така:
Є клас бінарного дерева Tree, який сберігає в упорядкованому стані інформацію с телефонними абонентами. В класі Tree є метод, який виводить весь список на екран:
void Tree::Print(Subscriber * Node)
{
if(Node != 0)
{
Print(Node->left);
printf ("%-35s %-20s %9d %15s\n", Node->FIO, Node->Town, Node->YearOfBirth, Node->Number);
Print(Node->right);
}
}
Не можу збагнути як вивести цей список із нумерацією стрічок? Як в циклі це зробити - зрозуміло:
for (int i = 1; i<...; i++)
cout<<i<<". "<</*якісь дані*/<<endl;
Але рекурсія працює зовсім по-іншому, і в мене нічого не виходить. То виводить всі стрічки з однаковим номером, то вони постійно змінюються із кожним визовом Print (). Підкажіть, будь ласка, я це можна зробити, а то у нас такий гарний викладач, що сам не зміг змістовно відповісти на це питання
Ви неправильно зрозуміли причину. Ваші рядки записуться коректно (так, там "зайві" байти, але їх рівно стільки, скільки в структурі), але ще є один некоректний рядок. Виникає він під час читання - тому що feof повертає true тільки після помилки читання, отака фіча в C. А першим він стає через сортування в Insert. Краще контролювати закінчення читання не feof, а кількістю байтів, і робити це окремою функцією:
Subscriber *readFromFile( FILE *F ) { Subscriber *result = new Subscriber;//ще питання, що швидше - створювати/знищувати чи копіювати... int bytesRead = fread ( result, sizeof (Subscriber), 1, F ); if( bytesRead != sizeof( Subscriber ) ) { delete result; result = 0; } return result; }
ну а цикл тоді буде
Subscriber *Node; while( ( Node = readFromFile( F ) ) != 0 ) { Insert( Node ); }
А ще краще, оскільки це плюси, використовувати файлові потоки і перевизначити << та >>.
Нарешті в мене все вдалося. Дуже дякую за допомогу. Спираючись на ваш варіант я таки дійшов висновку як правильно поставити умову при зчитуванні файлу. Однак у вашому коді теж є помилка, що мене і спантеличило.
Я спочатку хотів зробити згідно з вашим варіантом, але не став створювати нову функцію, а піредагував існуючу:
void Tree::ReadOutFile (FILE * F)
{
while (1)
{
Subscriber *Node = new Subscriber;
if (fread (Node, sizeof (Subscriber), 1, F) != sizeof (Subscriber)) return;
Insert (Node);
}
}
Однак при спробі зчитати файл і вивести результат на екран - програма нічого не виводила, хоча в файлі записи були. Прослідкувавши роботу функції за домогою відладчика побачив, що стрічка
if (fread (Node, sizeof (Subscriber), 1, F) != sizeof (Subscriber)) return;
просто не виконуються жодного разу і функція завершується, так і не зробивши свого діла з однієї простої причини - функція fread повертає не 104 байти, як це робить sizeof (Subscriber), а лише 1. Тобто, я дійшов висновку, що fread повертає не фактичну кількість зчитанних байтів, а кількість зчитаних елементів, які ми задаємо в 3-му аргументі функції fread! Тобто, якщо написати, наприклад так:
fread (Node, sizeof (Subscriber), 3, F)
то функція поверне 3 (звісно якщо всі 3 елементи будуть зчитані безпомилково). Ось такий вінегрет виходить. Ну виходячі із вищеприведених висновків, було нетяжко написати правильну умову для виходу із циклу:
void Tree::ReadOutFile (FILE * F)
{
while (1)
{
Subscriber *Node = new Subscriber;
if (fread (Node, sizeof (Subscriber), 1, F) != 1) return;
Insert (Node);
}
}
І все запрацювало, тепер всі записи правильно зчитуються і правильно відображуються.
Взагалі дякую за приклад вирішення, дуже довго не міг зрозуміти як правильно зробити, на російських форумах відповіді так і не побачив.
А з приводу файлових потоків, то ця тема в нашому матеріалі йде наступною, тому це ДЗ треба було зробити за допомогую С-шних функцій роботи з файлами
Зіткнувся за такою проблемкою. Є декілька ділянків коду.
Структура "Абонент":
struct Subscriber
{
char FIO [50];
int YearOfBirth;
char Town [20];
char Number [15];
Subscriber * left, * right, * parent;
};
А також клас Tree - бінарне дерево, яке сортує ці структури.
Клас Tree має метод SaveInFile () - збереження усіх записів в файл:
void Tree::SaveInFile (Subscriber * Node, FILE * F)
{
if(Node != 0)
{
SaveInFile (Node->left, F);
fwrite (Node, sizeof (Subscriber), 1, F);
SaveInFile (Node->right, F);
}
}
А також відповідний метод зчитування із файлу ReadOutFile ():
void Tree::ReadOutFile (FILE * F)
{
while (!feof (F))
{
Subscriber *Node = new Subscriber;
fread (Node, sizeof (Subscriber), 1, F);
Insert (Node); // вставляет указатель на считанную структуру в дерево
}
}
Метод SaveInFile в свою чергу викликається в функції SaveBase ():
Та віповідна функція ReadBase ():
Проблема в тому, що коли я зчитую послідовно записи із файлу, требя якимось чином закінчити зчитування, що зазвичай робиться за допомогою команди feof (), як тут:
while (!feof (F))
{
Subscriber *Node = new Subscriber;
fread (Node, sizeof (Subscriber), 1, F);
Insert (Node);
}
Але справа в тому, що дані до файлу записуються абсолютно всі, включно із невикористаними байтами із символьних масивів FIO, Town, Number. І в підсумку, після зчитування всієї необхідної інформації, він вкінці дописує решту сміття з файлу. Ну ось приклад.
1) Ввожу інфу:
2) Виводжу на екран:
3) Зберігаю в файл:
4) Зчитую із файлу:
5) Виводжу зчитану інформацію на екран:
Загалом я розумію причину, але не можу збагнути, як зробити, щоб зчитувалося все, без сміття. Записувати і зчитувати построково шляхом fputs () і fgets () якось нерозумно і накладно як на мене. Може я якось невірно описав функції запису і зчитування?