My task was to index a file in C.
I'm getting one argument: file name. The index should contain all the words and a line list which appears in.
A few notes:
- Big and small letters are the same.
- A word is max sequence of chars which are only from english alphabet and or digits every other char is splitting between the words.
- I should print an error message to
stderr
if I got too much args or if the input isn't matching (file doesn't exist).
The output will be written to a file with the same name and the ending ".index": if the file reading from is "input.txt" the output will be at "input.txt.index".
The word in the file should appear in lexicographic order.
For example:
input.txt:
Hello world
My, name is
WoooW././.L
Bye
So the file "input.txt.index" is created and should contain:
The word: "bye" appears in line: 4
The word: "hello" appears in line: 1
The word: "is" appears in line: 2
The word: "l" appears in line: 3
The word: "my" appears in line: 2
The word: "name" appears in line: 2
The word: "wooow" appears in line: 3
The word: "world" appears in line: 1
My code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <ctype.h>
#include "index.h"
/* Text functions area */
FILE *getFile (int argc, char const *argv[]) { /* A function to get the file inclouding a input check */
FILE *theinput;
if (argc < 2) { /* no argument */
fprintf(stderr, "Must pass an argument!\n");
exit(1);
}
theinput = fopen(argv[1], "r");
if (!theinput) { /* Argument is non-existing file */
fprintf(stderr, "Can't read %s\n", argv[1]);
exit(1);
}
return theinput;
}
char *getText (FILE *file) { /* Gets the text from file, Return a string with the file content */
char *text, *q;
char token;
int i = 0;
text = malloc(sizeof(char));
while ((token = getc(file)) != EOF) {
text[i] = token;
i++;
q = realloc(text, (i + 2) * sizeof(char));
if (!q) {
fprintf(stderr, "Out of memory\n");
exit(1);
}
text = q;
}
text[i] = '\0';
return text;
}
bool isLegalChar (char a) { /* Checks if a char is A - Z or a - z or 0 - 9 using ascii */
if (a <= 90 && a >= 65) { /* A = 65, Z = 90 */
return true;
}
else if (a <= 122 && a >= 97) { /* a = 97, z = 122 */
return true;
}
else if (isdigit(a)) {
return true;
}
else {
return false;
}
}
char *getWord (char *text, int start, bool *isEnterFound) { /* returns a single word from text starts searching from start */
char *word,*q; /* q = a helper string to make sure mallocing well */
char token; /* token is the right now input char */
int i = 0;
int j = start;
word = malloc(sizeof(char));
while (isLegalChar(token = text[j++]) || token == '\n') {
if (token == '\n')
{
(*isEnterFound) = true;
break;
}
word[i++] = token;
q = realloc(word, (strlen(word) + 2) * sizeof(char));
if (!q) {
fprintf(stderr, "Out of memory\n");
exit(1);
}
word = q;
}
word[i] = '\0';
return word;
}
void tolowerString (char *input) { /* Changes a string so all the letters in it will by lower */
int i;
for (i = 0; i < strlen(input); ++i)
{
input[i] = tolower(input[i]);
}
}
char* stradd(const char* a, const char* b) { /* Return a string which is the two given strings combined */
size_t len = strlen(a) + strlen(b);
char *ret = (char*)malloc(len * sizeof(char) + 1);
*ret = '\0';
return strcat(strcat(ret, a) ,b);
}
/* Tree functions area */
Node* newNode(char* word, int line) { /* Return a new node with the vars given to the funcion */
Node *p = (Node*) malloc (sizeof(Node));
p->word = word;
p->lines = malloc(sizeof(int));
p->lines[0] = line;
p->linesLength = 1;
p->ls = p->rs = NULL;
return p;
}
void insert(Node **root, char *word, int line) { /* Insert node to tree using recursion */
int *q;
int i;
if(!*root) {
*root = newNode(word, line);
return;
}
if(strcmp(word, (*root)->word) < 0) /* In case word is smaller than the nodes word */
{
insert(&((*root)->ls),word,line);
}
else if (strcmp(word, (*root)->word) == 0) /* In case word already exist */
{
for (i = 0; i < (*root)->linesLength; ++i)
{
if ((*root)->lines[i] == line)
{
return;
}
}
q = realloc((*root)->lines, (*root)->linesLength + 2);
if (!q)
{
fprintf(stderr, "Out of memory!\n");
exit(1);
}
(*root)->lines = q;
(*root)->lines[(*root)->linesLength] = line;
(*root)->linesLength++;
return;
}
else /* In case word is bigger than the nodes word */
{
insert(&((*root)->rs),word,line);
}
}
void printTreeRec(Node *root, FILE *outputFile) { /* A recursive function which prints the tree by given root */
int i;
if(!root) {
return;
}
printTreeRec(root->ls, outputFile);
fprintf(outputFile, "The word: \"%s\" ",root->word);
if (strlen(root->word) < 15) {
for (i = 0; i < 15 - strlen(root->word); ++i) {
fprintf(outputFile, " ");
}
}
fprintf(outputFile, "appears in ");
if (root->linesLength == 1) {
fprintf(outputFile, "line: ");
}
else {
fprintf(outputFile, "lines: ");
}
for (i = 0;i < root->linesLength - 1; i++) {
fprintf(outputFile, "%d, ", root->lines[i]);
}
fprintf(outputFile, "%d", root->lines[i]);
fprintf(outputFile, "\n");
printTreeRec(root->rs,outputFile);
}
void freeTree(Node *root) { /* A function to free the tree recursively */
if(!root)
return;
freeTree(root->ls);
freeTree(root->rs);
free(root->lines);
free(root);
}
/* Main */
int main(int argc, char const *argv[]) {
FILE *inputFile = getFile(argc,argv); /* Opening input file */
char *ouputAddress = stradd(argv[1],".index"); /* Creating output file address */
FILE *outputFile = fopen(ouputAddress,"w"); /* Creating output file */
char *text = getText(inputFile);
char *word;
int line = 1; /* Line counter */
bool foundEnter = false;
bool *isEnterFound = &foundEnter;
Node *root = NULL; /* First node of the tree */
int start = 0; /* Pointer to what was the last thing readed in text string */
while (start < strlen(text)) {
word = getWord(text, start, isEnterFound); /* If getWord detected enter foundEnter will become true */
tolowerString(word);
start += strlen(word) + 1;
if (strcmp(word, "") != 0) /* If word is not NULL */
{
insert(&root,word,line);
}
if (foundEnter)
{
foundEnter = false;
line++;
}
}
/* Ptinting the tree */
printTreeRec(root, outputFile);
/* Freeing the tree */
freeTree(root);
fclose(inputFile);
fclose(outputFile);
free(ouputAddress);
free(text);
free(word);
return 0;
}
It's working great, but I'm afraid of memory problems because of all the struct vars.
By the way, the code is running at time complexity of \$O(log(n))\$, which is for adding a word to a binary search tree.