1 Replies - 154 Views - Last Post: 04 March 2018 - 04:19 PM Rate Topic: -----

#1 enharmonics   User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 5
  • Joined: 04-March 18

getopt() I/O: Printing to a file results in garbage characters

Posted 04 March 2018 - 12:32 PM

I'll provide my code first, then explain my problem:

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

#define STRING_SIZE 100

// The flags for case sensitivity
// and an output file
int cflag = 0, oflag = 0;

// These are intended to represent the boolean
// values true and false
const int TRUE = 1;
const int FALSE = 0;

// Type alias for the bool type
typedef int bool;

typedef struct BST
{
    struct BST *left;
    struct BST *right;
    char *key;
    int counter;
} BST;

// ----- FUNCTION PROTOTYPES -----
BST* insert(BST *root, char *key);

int caseSenStrCmp(char *str1, char *str2);

int caseInsenStrCmp(char *str1, char *str2);

bool existsInTree(BST *root, char *key, int cflag);

BST* createBranch(char *key);

void inOrderPrint(BST *root, int oflag, FILE *outFile);

void deallocateTree(BST *root);

int main(int argc, char **argv) {
extern char *optarg;
extern int optind;
int c, err = 0;
char currentLine[STRING_SIZE];
char fileDirectory[STRING_SIZE];

static char usage[] = "Usage: %s [-c] [-o output_file_name] [input_file_name]\n";

while ((c = getopt(argc, argv, "co:")) != -1)
    switch (c)
    {
        case 'c':
            cflag = 1;
            break;
        case 'o':
            oflag = 1;

            // If an output file name
            // was entered, copy it
            // to fileDirectory

            if (optarg != NULL)
            {
                strcpy(fileDirectory, optarg);
            }

            break;
        default:
            err = 1;
            break;
    }

if (err)
{
    fprintf(stderr, usage, argv[0]);
    exit(1);
}

// --- BST SORT CODE STARTS HERE ---

// This is the BST. As the assignment instructions
// specify, it is initially set to NULL

BST *root = NULL;

// Pointer to the mode the files
// will be opened in. Starts as
// "w" since we're opening the output file
// first

char *mode = (char*)malloc(sizeof(char*));
strcpy(mode, "w");

FILE *outFile;
outFile = fopen(fileDirectory, mode);

// Now update mode and fileDirectory so
// we can open the INPUT file
strcpy(mode, "r");

// Check if we have an input file name
// If argv[optind] isn't NULL, that means we have
// an input file name, so copy it into fileDirectory

if (argv[optind] != NULL)
{
    strcpy(fileDirectory, argv[optind]);
}
FILE *inFile;

// Attempt to open the input file
//inFile = fopen(fileDirectory, mode);
fopen(fileDirectory, mode);

    if (inFile != NULL)
{
    // Process the file while EOF isn't
    // returned
    while (!feof(inFile))
    {
        // Get a single line (one string)
        fgets(currentLine, STRING_SIZE, inFile);

        // Check whether the line is an empty line
        if (*currentLine != '\n')
        {
            // If the string isn't an empty line, call
            // the insert function
            root = insert(root, currentLine);
        }
    }
    // At this point, we're done processing
    // the input file, so close it
    fclose(inFile);
}

// Otherwise, process user input from standard input
else
{
    do
    {
        printf("Please enter a string (or blank line to exit): ");

        // Get a single line of input from the user
        fgets(currentLine, STRING_SIZE, stdin);

        // If this is our first call to the
        // insert function, set root to point
        // to the branch created (which is
        // the actual "root" of the tree)
        if (root == NULL)
        {
            root = insert(root, currentLine);
        }
        // Otherwise, simply call the insert
        // function on the root of the tree
        else
        {
            insert(root, currentLine);
        }
    } while (caseSenStrCmp(currentLine, "\n") != 0);

}

// At this point, we've read all the input, so
// perform in-order traversal and print all the
// strings as per assignment specification

inOrderPrint(root, oflag, outFile);

// We're done, so reclaim the tree
deallocateTree(root);

// Finally, if we have an outFile, close it
if (outFile)
{
    fclose(outFile);
}

}

// ===== AUXILIARY METHODS ======

// Creates a new branch for the BST and returns a
// pointer to it.

BST* createBranch(char *keyVal)
{
// Create the new branch
BST* newBranch = (BST*)malloc(sizeof(BST));

// Allocate memory for newBranch's C-string
newBranch->key = (char*)malloc(STRING_SIZE * sizeof(char));

// Copy the user-provided string into newBranch's
// key field
strcpy(newBranch->key, keyVal);

// Set newBranch's counter value to 1. This
// will be incremented if/when other instances
// of the key are inserted into the tree

newBranch->counter = 1;

newBranch->left = NULL;
newBranch->right = NULL;

return newBranch;
}


// Adds items to the BST. Includes functionality
// to verify whether an item already exists in the tree

BST* insert(BST* root, char *key)
{

// If branch is null, call createBranch
// to make one, then return it
if (root == NULL)
{
    return createBranch(key);
}

// Otherwise, check whether the key
// already exists in the tree
if (!existsInTree(root, key, cflag))
{
    // If it doesn't, check whether
    // the case sensitivity flag is set
    if (cflag)
    {
        // If it is, use the case-sensitive
        // string comparison function

        if (caseSenStrCmp(root->key, key))
        {
            // If the key provided is greater
            // than the string stored at the
            // current branch, insert into
            // right child

            root->right = insert(root->right, key);
        }
        else
        {
            // Otherwise, insert into left child

            root->left = insert(root->left, key);
        }
    }
    // If it isn't, use the case-INsensitive string
    // comparison function to decide where to insert
    else
    {
        // Same logic as before
        if (caseInsenStrCmp(root->key, key))
        {
            root->right = insert(root->right, key);
        }
        else
        {
            root->left = insert(root ->left, key);
        }
    }
}

return root;
}

// Skipped a few functions here...

void inOrderPrint(BST *root, int oflag, FILE *outFile)
{
    // The implementation here is very similar
    // to that of existsInTree

    // Check if the current branch is null

    if (root == NULL)
    {
        // If the tree is empty, there's nothing
        // to print, so terminate the function

        return;
    }

        // Otherwise, perform the in-order traversal

    else
    {

        // This counter will be used in a loop
        // that will print the strings in the branches

        int count;



        if (root->left != NULL)
        {
            inOrderPrint(root->left, oflag, outFile);
        }

        // If the oflag is set and outFile
        // isn't NULL, then we can print to
        // the output file

        if (oflag && (outFile != NULL))
        {
            // Print the string as many times
            // as it occurs in the tree
            for (count = 0; count < root->counter; count++)
            {
                //fprintf(outFile, "%s\n", root->key);
                fputs(root->key, outFile);

                // Clear the buffer
                fflush(outFile);
            }
        }

            // Otherwise, print to stdout

        else
        {
            // Same idea as above
            for (count = 0; count < root->counter; count++)
            {
                printf("%s\n", root->key);
            }
        }

        if (root->right != NULL)
        {
            inOrderPrint(root->right, oflag, outFile);
        }
    }
}

// Performs post-order traversal, reclaiming
// the memory used to store the tree using
// the free() function as per assignment
// specifications. Also frees the memory used
// to store the string value (key) stored in each
// branch

void deallocateTree(BST *root)
{
    // First, check whether the tree is
    // empty. If so, there's nothing to
    // free, so just terminate the function

    if (root == NULL)
    {
        return;
    }

        // Otherwise, perform the post-order
        // traversal

    else
    {
        // If the current branch's left child
        // isn't null, recursively call this
        // function on it

        if (root->left != NULL)
        {
            deallocateTree(root->left);
        }

        // If the current branch's left child
        // IS null but its right child isn't,
        // recursively call this function on
        // its right child

        if (root->right != NULL)
        {
            deallocateTree(root->right);
        }

        // If neither of the current branch's
        // children are null, begin its deallocation
        // with its key value...

        free(root->key);

        // ... Followed by the branch itself

        free(root);
    }
}



Apologies for the long code block. I tried asking this question on StackOverflow hours ago already and I was getting some comments about not including enough code:

https://stackoverflo...-to-stdout-does

Unfortunately, despite my updating my answer and responding to comments, it seems my thread has already been pushed way too far back for anyone to see it anymore, so I don't think I'll be getting any more help there.

So, the program uses getopt to parse command line arguments. My problem essentially has to do with the File I/O parts - the part where the user enters strings into the console works perfectly - strings are successfully read from their branches and printed out to the console with inOrderPrint in the correct order and everything.

However, when I try to print to an output file by passing in an output file option:

./bstsort -o outfile

It DOES create a file named outfile, but the file only contains a single garbage character, namely this one:

This is the only character that ever gets printed, no matter what I name the output file or how many strings the program attempts to write (interestingly, the memory SIZE of the output file does change depending on how many strings were "written" to it, but the output is still just a single )

I have no idea how to fix this or why it's even happening. Why does the console printing work perfectly but the file printing return this odd character?

My original ideas as to this were:

- Maybe I'm using the wrong function, or I'm using the functions wrong (Not the case, I tried both fputs() and fprintf() - you can see the latter commented out in my code) and neither work

- Maybe it has something to do with where I'm putting the fclose(outFile) statement (also doesn't seem to be the case - I've moved it around a lot and nothing changes)

The posters on StackOverflow gave me a few ideas:

- One of them implied that I either wasn't terminating my strings with a NULL character '\0' or was inserting the strings into the nodes in the tree the wrong way. I don't think either of these is the case, because if it were, that immediately brings up the question of why the console printing statement works literally without a single problem and the fprintf()/fputs() only print garbage to the output file.

- Another poster asked if I was using fclose() or fflush() on the outFile. This one was interesting. I've done tons of File I/O for schoolwork before, never used fflush() before in my life, and never had ANY problem like this. At worst I've had some segmentation faults due to accidentally operating on a NULL pointer or something. In any case, I took a look at the documentation for fflush() and based on how I saw it used in the sample code, I placed it in the area where the file printing takes place (in the inOrderPrint() function):

 if (oflag && (outFile != NULL))
        {
            // Print the string as many times
            // as it occurs in the tree

            for (count = 0; count < root->counter; count++)
            {
                //fprintf(outFile, "%s\n", root->key);
                fputs(root->key, outFile);

                // Clear the buffer and accept the next
                // string

                fflush(outFile);
            }
        }


This unfortunately didn't solve the problem, and I'm honestly not even sure if this is the proper usage for the function. It was basically just a desperate shot in the dark to get this fixed.

WHAT I'M TRYING NOW (while waiting for answers in this thread):

Given the fact that the printing ONLY fails during FILE output and not console output, I'm guessing the problem is somewhere in the FILE* pointer(s) for the input/output file, so I'm placing printf() statements outputting the steps in the creation of the FILE pointers.

I'm really in deep with this one. I've spent all my spare time working on this program for 6 days straight and everything was going great until I found this bug. The only reason I didn't post a thread earlier is that I like to figure things out myself (asking for help kind of makes me feel like a leech), but now the deadline is coming up and I haven't gotten any closer to fixing this. Any help at all is appreciated.

Is This A Good Question/Topic? 0
  • +

Replies To: getopt() I/O: Printing to a file results in garbage characters

#2 Radius Nightly   User is offline

  • D.I.C Regular

Reputation: 33
  • View blog
  • Posts: 291
  • Joined: 07-May 15

Re: getopt() I/O: Printing to a file results in garbage characters

Posted 04 March 2018 - 04:19 PM

ø is a NULL sign, so your output to file are NULL.

So question is...
Why is it NULL to file output and not NULL in console output?

ATM on my mind comes, wrong character encoding, wrong use of special characters, not saving string(s), or one return arnt a string, saving line by line without append until nothing are returned, output file are in use, iostream, or so on... take a look in that.

Edit: Maybe this can help you...
http://www.dreaminco...post__p__873308

This post has been edited by Radius Nightly: 04 March 2018 - 05:03 PM

Was This Post Helpful? 0
  • +
  • -

Page 1 of 1