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.