KKKKK
KKKKK

Reputation: 284

Create a tree from a file .txt in C

my problem is the following:

I have to create a tree from a file .txt, this is an example of what is inside the file:

inside the file

the first element a is the root, after that is always this way:

the first element (x for example is the left tree) the second element (b for example is the right tree).

if an element is preceded by a point, that element for sure dont have a left or right child below.

so in this case the outgoing tree will be the following:

enter image description here

this is the structure in c language of my tree:

typedef char element;

typedef struct tree_element {
    element value;
    struct tree_element* left, * right;
} node;

typedef node* tree;

Any ideas on how to solve this?

my incorrect solution:

bool IsEmpty(tree t) {
    return (t == NULL);
}

tree EmptyTree(void) {
    return NULL;
}
tree Left(tree t) {
    if (IsEmpty(t)) {
        return NULL;
    }
    else {
        return t->left;
    }
}

tree Right(tree t) {
    if (IsEmpty(t)) {
        return NULL;
    }
    else {
        return t->right;
    }
}

tree ConsTree(const element* e, tree l, tree r) {
    tree t = malloc(sizeof(node));
    t->value = *e;
    t->left = l;
    t->right = r;
    return t;
}


tree InsertValue(tree t, element e, int position) {
    if (IsEmpty(t)) {
        return ConsTree(&e, EmptyTree(), EmptyTree());
    }
    tree root = t;
    while (true) {
        if (position == 1) {
            if (IsEmpty(Left(t))) {
                t->left = ConsTree(&e, EmptyTree(), EmptyTree());
                break;
            }
            else {
                t = Left(t);
            }
        }
        else {
            if (IsEmpty(Right(t))) {
                t->right = ConsTree(&e, EmptyTree(), EmptyTree());
                break;
            }
            else {
                t = Right(t);
            }
        }
    }
    return root;
}


extern tree LoadTree(const char* filename) {
    FILE* f = fopen(filename, "r");
    if (f == NULL) {
        return EmptyTree();
    }
    else {
        fseek(f, 0, SEEK_END);
        if (ftell(f) == 0) {
            fclose(f);
            return EmptyTree();
        }
        else {
        
            fseek(f, 0, SEEK_SET);
            tree t = EmptyTree();
            char c;
            c = fgetc(f);
            int i = 0;
            element e;
            e = c;
            l++;
            t = InsertValue(t,e,0);
            while (c != EOF) {
                c = fgetc(f);
                if (c != ' ' && c != '\t' && c != '\r' && c != '\n' && c != '\v' && c != '\f') {
                    i++;
                }
            }
        
            fclose(f);
            return t;
        }
    }
}

thanks in advance

Upvotes: 0

Views: 1190

Answers (1)

bruno
bruno

Reputation: 32596

The input file gives for each sub tree the value then if not prefixed by a dot the left tree then the right tree and that recursively, so the way to construct the tree can follow the same recursion :

node * read_tree(FILE * fp)
{
  node * r = malloc(sizeof(node));
  
  fscanf(fp, " %c", &r->value);
  
  if (r->value != '.') {
    r->left = read_tree(fp);
    r->right = read_tree(fp);
  }
  else {
    fscanf(fp, " %c", &r->value);
    r->left = r->right = NULL;
  }
  
  return r;
}

The space before %c in the format bypass the possible space/newline/tab/... allowing to read a file with the indents or without them like a.xbd.s.u.c as you said in a remark.

The proposal above supposes/hopes the file is valid, else it is possible to do :

node * read_tree(FILE * fp)
{
  node * r = malloc(sizeof(node));
  const char * err;
  
  if (r == NULL)
    err = "not enough memory";
  else if (fscanf(fp, " %c", &r->value) != 1)
    err = "invalid file, unexpected EOF";
  else if (r->value != '.') {
    r->left = read_tree(fp);
    r->right = read_tree(fp);
    return r;
  }
  else if (fscanf(fp, " %c", &r->value) != 1)
    err = "invalid file, unexpected EOF after '.'";
  else if (r->value == '.')
    err = "invalid file, two consecutive '.'";
  else {
    r->left = r->right = NULL;
    return r;
  }
  
  fprintf(stderr, "%s\nposition in the file : %ld\n", err, ftell(fp));
  exit(-1);
}

A full program can be :

#include <stdio.h>
#include <stdlib.h>

typedef char element;

typedef struct tree_element {
  element value;
  struct tree_element* left, * right;
} node;

node * read_tree(FILE * fp)
{
  node * r = malloc(sizeof(node));
  const char * err;
  
  if (r == NULL)
    err = "not enough memory";
  else if (fscanf(fp, " %c", &r->value) != 1)
    err = "invalid file, unexpected EOF";
  else if (r->value != '.') {
    r->left = read_tree(fp);
    r->right = read_tree(fp);
    return r;
  }
  else if (fscanf(fp, " %c", &r->value) != 1)
    err = "invalid file, unexpected EOF after '.'";
  else if (r->value == '.')
    err = "invalid file, two consecutive '.'";
  else {
    r->left = r->right = NULL;
    return r;
  }
  
  fprintf(stderr, "%s\nposition in the file : %ld\n", err, ftell(fp));
  exit(-1);
}

void print_free_tree(node * tree, int lvl)
{
  if (tree == NULL)
    putchar('.');
  else {
    putchar('(');
    print_free_tree(tree->left, lvl+1);
    printf("-%c %d-", tree->value, lvl);
    print_free_tree(tree->right, lvl+1);
    putchar(')');
    free(tree);
  }
}

int main(int argc, char ** argv)
{
  FILE * fp;
  
  if (argc != 2)
    fprintf(stderr, "Usage: %s <file>\n", *argv);
  else if ((fp = fopen(argv[1], "r")) == NULL)
    perror("cannot read file");
  else {
    node * tree = read_tree(fp);
    
    /* must be at end of file except may be some space/newline */
    char c;
    
    if (fscanf(fp, " %c", &c) == 1)
      fprintf(stderr, "invalid file, extra element(s) from the position %ld\n", ftell(fp));
    
    fclose(fp);
    
    /* show & free tree */
    print_free_tree(tree, 1);
    putchar('\n');
  }
  
  return 0;
}

Compilation and execution :

pi@raspberrypi:/tmp $ gcc -Wall c.c
pi@raspberrypi:/tmp $ cat f
a
  .x
  bd.s.u.c
pi@raspberrypi:/tmp $ ./a.out f
((.-x 2-.)-a 1-(((.-s 4-.)-d 3-(.-u 4-.))-b 2-(.-c 3-.)))
pi@raspberrypi:/tmp $ 

to help to check the validity of the tree each value is written with the level of its node, the empty branches are indicated by a '.'.

Execution under valgrind :

pi@raspberrypi:/tmp $ valgrind ./a.out f
==13298== Memcheck, a memory error detector
==13298== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==13298== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==13298== Command: ./a.out f
==13298== 
((.-x 2-.)-a 1-(((.-s 4-.)-d 3-(.-u 4-.))-b 2-(.-c 3-.)))
==13298== 
==13298== HEAP SUMMARY:
==13298==     in use at exit: 0 bytes in 0 blocks
==13298==   total heap usage: 10 allocs, 10 frees, 5,556 bytes allocated
==13298== 
==13298== All heap blocks were freed -- no leaks are possible
==13298== 
==13298== For lists of detected and suppressed errors, rerun with: -s
==13298== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
pi@raspberrypi:/tmp $ 

Upvotes: 1

Related Questions