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

  1. #1
    Registered User
    Join Date
    Mar 2018
    Posts
    2

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

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

    Code:
    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;
    
    // This is the BST struct
    
    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;
    
    // Holds the current line in the file/user-provided string.
    
    char currentLine[STRING_SIZE];
    
    // This will store the input/output file
        // directories
    
        //char fileDirectory[STRING_SIZE];
    
    char inFileDirectory[STRING_SIZE];
    char outFileDirectory[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);
    strcpy(outFileDirectory, optarg);
    }
    
                    break;
    case '?':
                    err = 1;
    break;
    default:
                    err = 1;
    break;
    }
    
        if (err)
        {
            fprintf(stderr, usage, argv[0]);
    exit(1);
    }
    
        // --- BST SORT CODE STARTS HERE ---
    
        // This is the BST
    
    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");
    
    printf("Wrote w to mode pointer");
    
    // Pointer to the output file
    
    FILE *outFile;
    
    // Attempt to open output file
    
        //outFile = fopen(fileDirectory, mode);
    outFile = fopen(outFileDirectory, "w");
    
    printf("Opened outfile \n");
    
    // Now update mode and fileDirectory so
        // we can open the INPUT file
    
    strcpy(mode, "r");
    
    printf("Wrote r to mode\n");
    
    // 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]);
    strcpy(inFileDirectory, argv[optind]);
    }
    
        printf("Wrote input file name to fileDirectory.\n");
    
    // Pointer to the input file
    
    FILE *inFile;
    
    // Attempt to open the input file
    
        //inFile = fopen(fileDirectory, mode);
        //inFile = fopen(inFileDirectory, "r");
    
    printf("Opened input file\n");
    
    // If the input file was opened successfully, process it
    
    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);
    
    // Call the insert function on the line
                // provided
    
                // 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 to be inserted into
        // the tree
    
    BST* newBranch = (BST*)malloc(sizeof(BST));
    
    // Allocate memory for newBranch's C-string
    
    newBranch->key = (char*)malloc(STRING_SIZE * sizeof(char));
    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 to
                // decide where to insert the key
    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 the root pointer
    
    return root;
    }
    
    At this point I'll skip some methods (tested) that I know are working just so that the code block won't be even larger than it already is

    Code:
    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 the left child of the current branch
            // isn't null, recursively call inOrderPrint
            // on it
    
    if (root->left != NULL)
            {
                inOrderPrint(root->left, oflag, outFile);
    }
    
            // Otherwise, we've reached the bottom
            // left of the tree, so we can print
            // this branch's value
    
            // 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 and accept the next
                    // string
    
    fflush(outFile);
    }
            }
    
                // Otherwise, print to stdout
    
    else
    {
                // Same idea as above: print
                // as many times as there are
                // instances of the string in the tree
    
    for (count = 0; count < root->counter; count++)
                {
                    printf("%s\n", root->key);
    }
            }
    
            // If the right child of the current branch
            // isn't null, recursively call inOrderPrint
            // on it
    
    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);
    }
    }


    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):

    Code:
    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.

  2. #2
    Registered User
    Join Date
    Dec 2017
    Posts
    1,633
    I haven't had time to look over the code yet, but a quick peek and I noticed this:
    Code:
    char *mode = (char*)malloc(sizeof(char*));
    strcpy(mode, "w");
    printf("Wrote w to mode pointer");
    You malloc enough room for a pointer-to-char, then write two chars to that pointer's location, 'w' and '\0'.
    Perhaps:
    Code:
    char *mode = malloc(2);  // sizeof(char) is 1 by definition
                             // and you shouldn't need to cast malloc
    strcpy(mode, "w");
    printf("Wrote w to mode pointer");
    If you've made similar mistakes anywhere else, fix them, too. It turns out that it doesn't matter in this case, but it might matter in other cases.
    A little inaccuracy saves tons of explanation. - H.H. Munro

  3. #3
    Registered User
    Join Date
    Dec 2017
    Posts
    1,633
    You should change the loop control from this:
    Code:
            while (!feof(inFile)) {
                fgets(currentLine, STRING_SIZE, inFile);
    to this
    Code:
            while (fgets(currentLine, STRING_SIZE, inFile) != NULL) {
    otherwise it will process the last line twice.
    A little inaccuracy saves tons of explanation. - H.H. Munro

  4. #4
    Registered User
    Join Date
    Dec 2017
    Posts
    1,633
    In insert, you're not incrementing the counter when you find a duplicate.

    Here's a more readable version of your code for others to peruse.
    Code:
    int main(int argc, char **argv) {
        extern char *optarg;
        extern int optind;
        int c, err = 0;
        char currentLine[STRING_SIZE];
        char inFileDirectory[STRING_SIZE];
        char outFileDirectory[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 (optarg != NULL)
                        strcpy(outFileDirectory, optarg);
                    break;
                case '?':
                    err = 1;
                    break;
                default:
                    err = 1;
                    break;
        }
    
        if (err) {
            fprintf(stderr, usage, argv[0]);
            exit(1);
        }
    
        BST *root = NULL;
    
        FILE *outFile = fopen(outFileDirectory, "w");
    
        if (argv[optind] != NULL)
            strcpy(inFileDirectory, argv[optind]);
    
        FILE *inFile = fopen(inFileDirectory, "r");
    
        if (inFile != NULL) {
            while (!feof(inFile)) {
                fgets(currentLine, STRING_SIZE, inFile);
                if (*currentLine != '\n')
                    root = insert(root, currentLine);
            }
            fclose(inFile);
        }
        else {
            do {
                printf("Please enter a string (or blank line to exit): ");
                fgets(currentLine, STRING_SIZE, stdin);
                if (root == NULL)
                    root = insert(root, currentLine);
                else
                    insert(root, currentLine);
            } while (caseSenStrCmp(currentLine, "\n") != 0);
        }
    
        inOrderPrint(root, oflag, outFile);
    
        deallocateTree(root);
    
        if (outFile)
            fclose(outFile);
        
        return 0;
    }
    
    
    void inOrderPrint(BST *root, int oflag, FILE *outFile) {
        if (root == NULL)
            return;
        else {
            int count;
    
            if (root->left != NULL)
                inOrderPrint(root->left, oflag, outFile);
    
            if (oflag && (outFile != NULL)) {
                for (count = 0; count < root->counter; count++) {
                    fprintf(outFile, "%s\n", root->key);
                    fflush(outFile);
                }
            }
            else
                for (count = 0; count < root->counter; count++)
                    printf("%s\n", root->key);
    
            if (root->right != NULL)
                inOrderPrint(root->right, oflag, outFile);
        }
    }
    
    
    BST* createBranch(char *keyVal) {
        BST* newBranch = (BST*)malloc(sizeof(BST));
        newBranch->key = (char*)malloc(STRING_SIZE * sizeof(char));
        strcpy(newBranch->key, keyVal);
        newBranch->counter = 1;
        newBranch->left = NULL;
        newBranch->right = NULL;
        return newBranch;
    }
    
    
    BST* insert(BST* root, char *key) {
        if (root == NULL)
            return createBranch(key);
    
        if (!existsInTree(root, key, cflag))
        {
            if (cflag)
            {
                if (caseSenStrCmp(root->key, key))
                    root->right = insert(root->right, key);
                else
                    root->left = insert(root->left, key);
            }
            else
            {
                if (caseInsenStrCmp(root->key, key))
                    root->right = insert(root->right, key);
                else
                    root->left = insert(root ->left, key);
            }
        }
    
        return root;
    }
    A little inaccuracy saves tons of explanation. - H.H. Munro

  5. #5
    Registered User
    Join Date
    Mar 2018
    Posts
    2
    Quote Originally Posted by john.c View Post
    I haven't had time to look over the code yet, but a quick peek and I noticed this:
    Code:
    char *mode = (char*)malloc(sizeof(char*));
    strcpy(mode, "w");
    printf("Wrote w to mode pointer");
    You malloc enough room for a pointer-to-char, then write two chars to that pointer's location, 'w' and '\0'.
    Perhaps:
    Code:
    char *mode = malloc(2);  // sizeof(char) is 1 by definition
                             // and you shouldn't need to cast malloc
    strcpy(mode, "w");
    printf("Wrote w to mode pointer");
    If you've made similar mistakes anywhere else, fix them, too. It turns out that it doesn't matter in this case, but it might matter in other cases.
    I noticed that myself a while ago. I meant to delete the lines involving the *mode pointer, since I opted to just pass the mode directly to the fopen() calls ("r" and "w" for in and outfile, respectively), but I guess I must've forgotten.

    Quote Originally Posted by john.c View Post
    You should change the loop control from this:
    Code:
            while (!feof(inFile)) {
                fgets(currentLine, STRING_SIZE, inFile);
    to this
    Code:
            while (fgets(currentLine, STRING_SIZE, inFile) != NULL) {
    otherwise it will process the last line twice.
    Interesting. I didn't know the last line would be processed twice with !feof(inFile) as the loop condition. Actually, I had been notified of this in the thread I posted on StackOverflow - one of the posters linked me to a thread named "Why is while(!eof) always wrong":

    c - Why is “while ( !feof (file) )” always wrong? - Stack Overflow

    I'm ashamed to say I ignored it at first, because it wasn't directly relevant to solving my problem, but now that I've heard it from you as well I decided it would be worth it to take a look. So, from what I understand, using !feof(inFile) as a loop condition is wrong because feof() won't return true (and therefore !feof() won't return false, thereby terminating the loop) until AFTER it's already arrived at the end of the file (so, it won't return true at the last character in the file, but rather at the point AFTER the last character in the file), which makes the loop iterate one extra time?

  6. #6
    Registered User
    Join Date
    Dec 2017
    Posts
    1,633
    Right.

    It's also important to remember that fgets leaves the newline character in the string, so if you don't explicitly remove it you need to remember it's there for printing or comparing.
    Code:
    while (fgets(line, sizeof line, file) != NULL) {
        int len = strlen(line);
        if (line[len - 1] == '\n')
            line[--len] = '\0';  // overwrite newline with null char
    There's a few ways to do it, but that one's pretty direct and foolproof, and has the advantage of giving you the length of the string, which is nice.

    I'm still not sure what could be wrong with your program. Maybe you could provide a zip file somewhere of the whole thing.

    So are you saying that if you say
    progname input_file
    it prints the correct result to the console but if you say
    progname -o output_file input_file
    the output file is garbage, just one character long? (Is that its file size?)
    A little inaccuracy saves tons of explanation. - H.H. Munro

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Printing combination results to 'container'
    By sean_cantab in forum C++ Programming
    Replies: 5
    Last Post: 06-27-2016, 04:03 PM
  2. c program printing out garbage (arrays and for loops)
    By starbucks10v3r in forum C Programming
    Replies: 27
    Last Post: 11-20-2013, 01:23 AM
  3. Structs - garbage printing
    By MethodMan in forum C Programming
    Replies: 11
    Last Post: 03-14-2004, 10:48 AM
  4. printing function results???
    By bluenoser in forum C Programming
    Replies: 1
    Last Post: 11-01-2002, 12:41 AM

Tags for this Thread