1 Востаннє редагувалося shabaranskij (02.07.2016 23:42:14)

Тема: Сі. Вивести вміст текстового файлу.

Приніс свій код для критики і порад по поліпшенню. ОС Ubuntu. GCC.

#include <stdio.h>
#include "openfile.h"
#include <string.h>

int main(void)
{
  int work=1;

  while (work!=0)
    {
      longString  a;
      shortString path;
      printf("Enter path:");
      scanf("%s",path);

      openFile(a,path);

      printf("%s\nSize=%i (байт).\n\n",a,strlen(a));

      printf("Open new file? yes=1, no=0: ");
      scanf("%i",&work);
    }

  return 0;
}
#include <stdio.h>
#include <string.h>
#define STRING_BUFFER (3*1024*1024)

typedef  char longString [STRING_BUFFER];

typedef char shortString [1024];

int openFile(char out[],char path[])
{
  char str[STRING_BUFFER];
  FILE *file;
  file=fopen(path,"r");

  while((fgets(str,STRING_BUFFER,file))!=NULL)
    {
      strcat(out,str);
    }

  fclose (file);

  return 0;
}
Подякували: leofun011

2

Re: Сі. Вивести вміст текстового файлу.

shabaranskij написав:
  while (work!=0)
    {
      longString  a;

Немає сенсу робити два рази відступ, тим більше, на різну кількість пробілів. Можна писати

  while (work!=0)
{
    longString  a;

чи

  while (work!=0) {
    longString  a;

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

shabaranskij написав:
scanf("%s",path);

Так безпечніше:

fgets( path, sizeof(path), stdin );

Далі взагалі якийсь сюр. Ви виділили 3МБ пам'яті на рядок (в стеку!!!), а потім зчитуєте туди файл в циклі блоками по... 3МБ. А якщо файл буде 5МБ, то куди будуть зчитані решта 2МБ? Абикуди в стек?
Жодних перевірок помилок немає. А якщо такого файлу не існує?
А якщо у файлі зустрічається знак '\0', то strlen і printf будуть обробляти його тільки до цього знаку, ви в курсі?
А якщо хтось в останній scanf введе літеру 'A', що станеться з програмою?
Ну і блоки по 3МБ - якесь невдале рішення для виводу на екран. Зробіть блоки по 16КБ і виводіть їх одразу, щойно прочитали.

Подякували: ADR, leofun012

3

Re: Сі. Вивести вміст текстового файлу.

А ще у вас тіло функції в .h-файлі і немає include guards. Не робіть так.

4

Re: Сі. Вивести вміст текстового файлу.

Немає сенсу робити два рази відступ

Дивлячись якого стилю відступів дотримується розробник. Приклад: https://en.wikipedia.org/wiki/Indent_style#GNU_style (схоже, shabaranskij використовує цей же стиль).

Як бачимо, поширених варіантів оформлення коду доволі багато — є з чого вибирати. Головне, щоб стиль відступів у межах проекту був один і той же, а не їх мішанина.

Подякували: leofun011

5

Re: Сі. Вивести вміст текстового файлу.

P.Y. написав:

Немає сенсу робити два рази відступ

Дивлячись якого стилю відступів дотримується розробник. Приклад: https://en.wikipedia.org/wiki/Indent_style#GNU_style (схоже, shabaranskij використовує цей же стиль).

Як бачимо, поширених варіантів оформлення коду доволі багато — є з чого вибирати. Головне, щоб стиль відступів у межах проекту був один і той же, а не їх мішанина.

Добре, добре, стилів до біса, і так теж можна, хоча мені й не подобається... але ніхто не робить чотири пробіли до { і два після, це вже точно.

6 Востаннє редагувалося ADR (03.07.2016 14:02:12)

Re: Сі. Вивести вміст текстового файлу.

koala написав:

Немає сенсу робити два рази відступ, тим більше, на різну кількість пробілів. Можна писати

  while (work!=0)
{
    longString  a;

Ви мали на увазі так? (ну чи з двома пробілами)

    while (work!=0)
    {
        longString a;

І у автора було 2 і 2 пробіли, а не 4 і 2. (цикл while ж вкладений)

Подякували: koala1

7 Востаннє редагувалося shabaranskij (03.07.2016 15:57:53)

Re: Сі. Вивести вміст текстового файлу.

Якось так. На стиль не зважайте Я ще буду читати книги як правильно оформляти код.

#include <stdio.h>
#include <string.h>
#include "openfile.h"
#include "conio.h"

int main(void)
{
  int work=1;

  while (work!=0)
    {
      shortString path="";

      clrscr();
      printf("Введіть шлях до файла:");
      scanf("%s",path);
      printf("\n/////Початок файла/////\n\n");
      printFile(path);
      printf("\n/////Кінець файла/////\n\n");
      printf("Відкрити інший файл? так=1, ні=0: ");
      scanf("%i",&work);
    }
  return 0;
}
#ifndef OPENFILE
#define OPENFILE

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

typedef char shortString [1024];

int printFile(char path[])
{
  char str;
  FILE *file;

  file=fopen(path,"r");
  while((str=fgetc(file))!=EOF)
    {
      printf("%c",str);
    }
  fclose (file);

  return 0;
}

#endif

8

Re: Сі. Вивести вміст текстового файлу.

ADR написав:

І у автора було 2 і 2 пробіли, а не 4 і 2. (цикл while ж вкладений)

Так. Щось я дуже неуважний сьогодні. Вибачте.

Подякували: shabaranskij, ADR2

9

Re: Сі. Вивести вміст текстового файлу.

Ви під яку операційну систему пишете? conio.h - це заголовковий файл DOS-а. Якщо хочете, щоб ваша програма працювала тільки в сумісних з DOS ОС - то все гаразд, але я чомусь в цьому не певен.

while((str=fgetc(file))!=EOF)

у вас не спрацює на символі з кодом 255.
Ну і зауваження про work і scanf ви проігнорували. До речі, там цикл з пост-умовою сам проситься.
А ще про тіло функції в .h.

10 Востаннє редагувалося shabaranskij (03.07.2016 20:15:13)

Re: Сі. Вивести вміст текстового файлу.

koala написав:

Ви під яку операційну систему пишете? conio.h - це заголовковий файл DOS-а. Якщо хочете, щоб ваша програма працювала тільки в сумісних з DOS ОС - то все гаразд, але я чомусь в цьому не певен.

За conio.h я знаю.
А остальне я не зрозумів.
Як це

while((str=fgetc(file))!=EOF)

не спрацює на символі з кодом 255.
І як розуміти

koala написав:

А ще про тіло функції в .h.

Можете порадити хороші книги по Сі на українській мові.

А про перевірку  на помилки я пам'ятаю.

11

Re: Сі. Вивести вміст текстового файлу.

Про char(255): http://ideone.com/cxXfj4
Чому так: уважно подивіться сигнатуру функції fgetc.
Література: https://uk.wikipedia.org/wiki/Доповняльний_код

Про тіло функції: додайте до свого проекту ще один .c-файл і додайте туди #include "openfile.h" - отримаєте помилку.
Чому так: в C програма спершу компілюється в об'єктний код, а потім з нього збирається (link) в виконуваний. Програма буде скомпільована, але під час збірки виявиться, що в двох об'єктних файлах є тіло функції openFile, що викличе помилку.

Подякували: shabaranskij, leofun012

12 Востаннє редагувалося shabaranskij (03.07.2016 23:06:36)

Re: Сі. Вивести вміст текстового файлу.

koala написав:

Про тіло функції: додайте до свого проекту ще один .c-файл і додайте туди #include "openfile.h" - отримаєте помилку.
Чому так: в C програма спершу компілюється в об'єктний код, а потім з нього збирається (link) в виконуваний. Програма буде скомпільована, але під час збірки виявиться, що в двох об'єктних файлах є тіло функції openFile, що викличе помилку.

А хіба якщо додати цю частину коду в openfile.h, то  не буде запобігати виникненню  такої помилки?

#ifndef OPENFILE
#define OPENFILE
......
....
#endif

13

Re: Сі. Вивести вміст текстового файлу.

Ні, не буде, адже ця частина коду(include guard) запобігає включенню .h файлу в один інший файл більше одного разу. Але це не забороняє .h файлу включитись по одному разу в різні .c файли, що у вашому випадку призведе до  помилок, описаних паном koala.
Тому краще тіло функції перенести в окремий .c файл, а в хедері залишити тільки її прототип:

// openfile.h
#ifndef OPENFILE
#define OPENFILE
//...
int printFile(char path[]);
//...
#endif
// openfile.c
#include "openfile.h"

int printFile(char path[])
{
    // Реалізація
}
Подякували: shabaranskij, leofun01, koala3

14

Re: Сі. Вивести вміст текстового файлу.

Дякую всім за відповіді. Я пішов шукати інші книги по сі  бо в тих що я маю  немає інфи. 
*WALL*  Чим дальше в ліс тим більше дров.  :(

15 Востаннє редагувалося shabaranskij (06.07.2016 01:06:08)

Re: Сі. Вивести вміст текстового файлу.

//  main.c
#include <stdio.h>
#include "rwfile.h"
#include <stdlib.h>

int main(void)
{

    int work;
    string  path=newString;     //    /home/lazarus/name.txt

    for(;;)
    {

        if (path==NULL)
        {
            system("echo Error!");
            exit(0);
        }

        printf("Введіть шлях до файла:");
        scanf("%s",path);

        openFile(path);
        printf("\nЩоб відкрити інший документ введіть 1. Для виходу 0:");
        scanf("%i",&work);

        if (work==1 )
        {
            system("clear");
            continue;
        } else
        if (work==0 )
        {
            system("clear");
            exit(0);
        }else
        {
            system("clear");
            exit(0);
        }

    }

        free(path);

    return 0;
}
//readfile.c
#include <stdio.h>
#include <stdlib.h>
#include "rwfile.h"

int openFile(char path[])
{
  string str=newString;
  int isNorm=0;
  FILE *file;

  if (str==NULL)
      {
       system("echo Error!");
       exit(0);
      }

  file=fopen(path,"r");

  if (file==NULL)
  {
      isNorm=0;
      system("echo Помилка. Перевірте введену адресу на помилки.");
      exit(0);
  } else
      isNorm=1;

    printf("#Початок\n");

  while ((fgets(str,sizeof(str),file))!=NULL)
    {
      printf("%s",str);
    }

    printf("#Кінець\n");
    fclose(file);
    free(str);

  return isNorm;
}
//rwfile.h
#ifndef RWFILE_H
#define RWFILE_H

#define newString (char*)(malloc(sizeof(int)))
typedef char *string;
extern int openFile(char path[]);

#endif

16

Re: Сі. Вивести вміст текстового файлу.

rwfile.h
1. Макровизначення прийнято робити великими літерами: NEWSTRING
2. Якщо ви пропонуєте власні засоби для виділення пам'яті, давайте і відповідні засоби для її звільнення, тобто

#define FREE free
...
FREE(str);

Просто для уніформності.
3. Ви точно певні, що хочете виділяти під рядок 4 байти?

readfile.c:
1. Прийнято називати відповідні .c і .h файли однаково.
2. Це зазвичай роблять printf-ом

system("echo Error!");


3. Рядок 22 зайвий, як і else в 25-му, і взагалі isNorm в цьому коді, фактично, не використовується.

main.c:
1. Перевірку path==NULL можна (і треба) робити перед циклом.
2. while() читається краще, ніж for(;;). Втім, питання смаку.
3. Якщо у всіх гілках if-else if-else щось однакове робиться спочатку чи в кінці, його треба виносити перед/після if:

        system("clear");
        if (work==1 )
        {
            continue;
        } else
        if (work==0 )
        {
            exit(0);
        }else
        {
            exit(0);
        }

А якщо в if і else робиться однакове, то if-else не потрібен:

        system("clear");
        if (work==1 )
        {
            continue;
        } else
        {
            exit(0);
        }

До речі, continue останньою дією теж не потрібне:

        system("clear");
        if (work!=1 )
        {
            exit(0);
        }

Ну, а оскільки тут після цього фактично немає коду, то exit(0) еквівалентний до break і зайву перевірку краще винести в цикл:

do
{
...
}while(work!=0);

Це краще тим, що буде виконано очищення наприкінці програми; так, поки там тільки звільнення ресурсів, то це не обов'язково, але в принципі можливо, що там буде якийсь лог.
4. І не використовуйте system для очищення екрану. Ось кілька способів.

Подякували: leofun01, shabaranskij2

17 Востаннє редагувалося shabaranskij (07.07.2016 19:27:30)

Re: Сі. Вивести вміст текстового файлу.

Я трохи заплутався з malloc, calloc, i realloc.
Що буде якщо я слово "Привіт всім!" запишу в  name;

char* name=(char*)(malloc(sizeof(char)*2))

або  так

char* name=(char*)(calloc(0,sizeof(char)))

18 Востаннє редагувалося koala (07.07.2016 19:42:47)

Re: Сі. Вивести вміст текстового файлу.

calloc(x,y) еквівалентний memset(malloc(x*y), 0, x*y).
Виділення 0 байт - безглузда операція, за таким вказівником не можна переходити.

shabaranskij написав:

Що буде якщо я слово "Привіт всім!" запишу в  name;
char* name=(char*)(malloc(sizeof(char)*2))

В кращому разі туди влізе 2 символи, далі ви писатимете невідомо куди.

shabaranskij написав:

char* name=(char*)(calloc(0,sizeof(char)))

А тут ви писатимете невідомо куди одразу.
UB в обох випадках.

Подякували: leofun011

19

Re: Сі. Вивести вміст текстового файлу.

char* name=(char*)(malloc(sizeof(char)*512));
char str[]=" me";
name="Help";
strcat(name,str);

Виникає помилка. Як з'єднати два рядки?

20 Востаннє редагувалося shabaranskij (07.07.2016 20:07:17)

Re: Сі. Вивести вміст текстового файлу.

І пішов офтоп. Як створити тип даних string в с з такими можливостями як наприклад в java. щоб можна було додавати два рядки наприклад стрінг += стрінг2.
І чи це не повний маразм.
Чи потрібно весь час обходити такі помилки

char* name=(char*)(malloc(sizeof(char)*512));
char str[]=" me";
name="Help";
strcat(name,str);

Можливо ліпше написати

сhar a[1024];

і не паритися.