Thread: Problem with File Input & Pointers

  1. #1
    Registered User
    Join Date
    Sep 2004
    Posts
    3

    Problem with File Input & Pointers

    Hi,
    Long time lurker, first time poster.
    I have been working on this assignment for days and keep getting stuck. I am supposed to read the contents of a text file (first and last names, e.g. Smith, John ) into a two dimensional array, sort the array, and write it to a new file. Names longer than 25 characters are supposed to be truncated.

    I am storing the names in a character array of pointers, I am able to input the names into the array but as soon as my while loop exits my array is totally messed up.

    Here's my code:
    Code:
    //Names are found in the file "names.txt"
    
    #include <iostream>
    
    using std::cout;
    using std::cin;
    using std::ios;
    using std::cerr;
    using std::endl;
    
    #include <iomanip>
    
    using std::setw;
    
    #include <fstream>
    
    using std::ifstream;
    using std::ofstream;
    
    void sortFile( char *[][1], int );
    void getFileName( char [] );
    void writeFile ( char *[][1], char [], int );
    
    
    int main ()
    {
    	char *names[25][1]; //array to hold first and last names
    	char filename[25]; //array to hold filename input
    
    	int row = 0;
    	char fname[25]; //array to hold filename
    	char buffer[50] = ""; //array to hold buffer
    	char temp[26] = ""; //array to hold temporary value of buffer
    
    	cout << "Enter filename to read from: ";
    	cin >> setw(24) >> fname; //input filename
    
    	ifstream inFile( fname, ios::in ); //open file for input
    
    	if ( !inFile ) { //if filename is not valid ask for another name 
    		cout << "File could not be opened." << endl;
    
    		while ( !inFile ) {
    			
            	cout << "Enter filename: ";
    	        cin >> fname;
    	        ifstream inFile( fname, ios::in );
    		}
    	} 
    
    	while ( inFile.peek() != EOF )  {
    
    		inFile.getline(buffer, 50);
    
    		if (strlen(buffer) > 26) {
    			strncat(temp, buffer, 26);
    			names[row][0] = temp;
    		
    		}
    			else { names[row][0] = buffer;}
    
    			cout << names[row][0] << endl;
    		
    		row++;
    
    		
    		inFile.clear();
    
    	}	
    
    	sortFile( names, row ); //call function to sort file
    
    	
    	getFileName( filename ); //call function to input filename
    	writeFile( names, filename, row ); //call function to write to file	
    
       return 0;
    }
    
    void sortFile( char *n[][1], int num ){
    
    	char *tmp; //pointer to hold temporary value
    
      for(int i = 0; i < num; i++) {
    
    	  if(n[i][0] > n[i + 1][0]) { //if current array element is larger than next element swap them
    
          //swap last name
          tmp = n[i][0];
          n[i][0] = n[i + 1][0];
          n[i + 1][0] = tmp;
    
          //swap first name
          tmp = n[i][1];
          n[i][1] = n[i + 1][1];
          n[i + 1][1] = tmp;
    
    	  }
      }
    
    
    }
    
    void getFileName( char fname[] ) {
    
    	char response;
    
    	cout << "Enter filename to write to: ";
    	cin >> setw(24) >> fname; //input filename
    
    	ofstream inFile( fname, ios::out ); //open file for output
    
    	if ( inFile ) { //if file already exists ask if it should be overwritten
    		cout << "File already exists. Overwrite (y or n)?";
    	    cin >> response;
    
    		if ( response == 'n' ) { //if no ask for another filename
    			cout << "Enter filename to write to: ";
    	        cin >> setw(24) >> fname;
    		}
    	}
    
    }
    
    void writeFile( char *n[][1], char fname[], int num ) {
    
    	cout << num << endl;
    
    	int row = 0; //initialize counter
    
    	ofstream outFile( fname, ios::out ); //open file for output
    
    	if (!outFile ) { //if file could not be opened display error and exit
    		cerr << "File could not be opened." << endl;
    		exit( 1 );
    	}
    
    	while ( row < num ) { //output names to file while number of names is greater than counter
    
            cout << n[row][0];		
    		outFile << n[row][0] << endl;
    
    		  row++; //increase counter		 
        
    	} 
    
    }
    I will be so grateful if you can give me some advice.

    Thank you! Jenna

  2. #2
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Are you allowed to use std::string? I would highly recommend doing so, it would free you of all memory management troubles.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  3. #3
    Carnivore ('-'v) Hunter2's Avatar
    Join Date
    May 2002
    Posts
    2,879
    I notice that you're using the assignment operator (=) to assign values to 'strings', and comparing them using the > and < operators. The problem is, you can't do this in C++. You can use strcpy() to copy strings and strcmp() to compare them (in <cstring>), but then you have to worry about making sure that the strings are large enough to hold whatever you put in them, etc.

    The easiest solution I can think of, is to use std::string (include <string>) instead of char* or char[], since it actually does work with the =, ==, <, > operators - and std::string automatically resizes itself to fit the contents of the string, and cleans up after itself, i.e. frees its own memory.
    Just Google It. √

    (\ /)
    ( . .)
    c(")(") This is bunny. Copy and paste bunny into your signature to help him gain world domination.

  4. #4
    Registered User
    Join Date
    Mar 2002
    Posts
    1,595
    Your sortFile looks like an attempt to implement a bubble sort. I'd recommend you do a board search or a google search to find a variety of ways to implement this type of sort.


    If you can't use std::string class and you have to use char * then the procedure in rough pseudocode could be something like this:

    void sortFile( char n[10][26], int num )
    {
    //external loop to determine number of times to run through the array
    //internal loop to compare each name in the array with each other name in the array
    //use result of strcmp(n[i], n[i+1]) to determine whether to swap or not.
    //if swapping, use something like
    char tmp[26]; //maximum 25 useable char + null
    strcpy(tmp, n[i + 1]);
    strcpy(n[i+1], n[i]);
    strcpy(n[i], tmp);
    Last edited by elad; 12-04-2004 at 09:08 AM.

  5. #5
    Registered User
    Join Date
    Sep 2004
    Posts
    3
    Thanks for the advice everyone.

    The assignment says that I have to use a two dimensional array to hold the names. Can I still use std::string and maybe use the array to hold pointers to the strings? Any other way to use a two dimensional array for this besides pointers?

    Jenna

  6. #6
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    std::string names[25][2];

    This will work just fine.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  7. #7
    Registered User
    Join Date
    Mar 2002
    Posts
    1,595
    char n[10][26];

    n is a two dimensional array of type char that can hold up to 10 different C style strings, each of which can hold up to 25 useable char and a single null terminating char. Note that there is no * in the declaration. You could substitute a single * for a single [] if you wish, and declare memory for that * using the heap rather than the stack, but I interpret

    char * n[10][26];

    to be a pointer to a two dimensional array of char, rather than a two dimensional array of char.

    std::string n[26];

    is an array of 26 std::strings, and not a two dimensional array as far as I'm concerned.

    std::vector<std::string> n;

    is a vector of strings and, again, not a two dimensional array of strings. This despite the fact that string n[26] and vector<string> n could both make your life easier.

    BTW I think

    n[i][0]

    is a single char rather than a string if n is declared as follows:

    char n[10][26];

    n is the pointer to n[0][0], n[i] is a given string in n, &n[i][0] is the address of the first char of a given string and could be used like n[i] but the syntax is terrible cumbersome. I still find all this somewhat confusing myself, but if I've led you astray I'm sure someone will get us back on the road to (somewhere).

  8. #8
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    but I interpret

    char * n[10][26];

    to be a pointer to a two dimensional array of char, rather than a two dimensional array of char.
    That's a two-dimensional array of pointer to char.

    Basically, try to avoid having more than one dimension, especially when dealing with strings. Since your assignment apparently requires you to have two dimensions, at least use std::string to avoid big memory confusions.

    If the assignment allows it, you could also use std::sort to sort the array. But since it's 2dim, you would have to write your own sorting predicate (comparator).
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  9. #9
    Registered User
    Join Date
    Mar 2002
    Posts
    1,595
    char names1[10][26] would hold 10 strings of up to 25 useable char. Each string would be a single name, like "Ebekeneezer" or "Smythe, Jane".

    strcpy(names1[0], "Smythe, Jane");

    std::string names2[10][2] would hold 10 groups of strings each group having two strings of unlimited (except by available memory) size. I could see this working well if you wanted to recognize the first name and the last separately, like:

    names2[0][0] = "Smythe";
    names2[0][1] = "Jane";

    If you wanted to hold first and last names separately, each name could only be 25 char long and you couldn't use std::string, then you could try:

    char * names3[10][2];
    char first1[26] = "Jane";
    char second1[26] = "Smythe";
    char first2[26] = "Peter";
    char second2[26] = "Potter";
    names3[0][0] = second1;
    names3[0][1] = first1;
    names3[1][0] = &second2[0];
    names3[1][1] = &first2[0];

    Apparently names1, names2, and names3 would all be two dimensional arrays with names1 being a two dimensional array of char, names2 being a two dimensional array of std::string and names3 being a two dimensional array of char pointers.

    However, names1 is what I think of when I think of a two dimensional array used to hold names. That's because in order to get an individual char (say the first char of the initial name in each array) I only need to go two levels deep in names1:

    char ch1 = names1[0][0];

    whereas in names2 I would need to do this:

    char ch2 = names2[0][0][0];

    and in names3 I believe I could also do this:

    char ch3 = names3[0][0][0];

    In other words, to me, names2 and name3 are really doing this under the hood:

    char names4[10][2][26];

    (which is clearly a three dimensional array of char) except names2 doesn't have the size restriction of 26 on the third level of indirection. Since I have no idea what your instructor really wants, I guess it's up to you to make the decision which way you want to go in "using a two dimensional array to hold the names".

    Here's a program using the above topic:

    Code:
    #include <iostream>
    #include <string>
    using namespace std;
    
    int main(int argc, char* argv[])
    {
      char names1[10][26]; 
      strcpy(names1[0], "Smythe, Jane");
      cout << names1[0] << endl;
     
      std::string names2[10][2];
      names2[0][0] = "Smythe";
      names2[0][1] = "Jane";
      cout << names2[0][0] << ", " << names2[0][1] << endl;
     
      char * names3[10][2];
      char first1[26] = "Jane";
      char second1[26] = "Smythe";
      char first2[26] = "Peter";
      char second2[26] = "Potter";
     
      names3[0][0] = second1;
      names3[0][1] = first1;
      names3[1][0] = &second2[0];
      names3[1][1] = &first2[0];
     
      cout << names3[0][0] << ", " << names3[0][1] << endl;
      cout << names3[1][0] << ", " << names3[1][1] << endl;
    
      char ch1 = names1[0][0];
      char ch2 = names2[0][0][0];   
      char ch3 = names3[0][0][0];
     
      cout << ch1 << ch2 << ch3 << endl;
     
      char names4[10][2][26];
      strcpy(names4[0][0], second1);
      strcpy(names4[0][1], first1);
      
      cout << names4[0][0] << ", " << names4[0][1] << endl;
      return 0;
    }

  10. #10
    Registered User
    Join Date
    Sep 2004
    Posts
    3
    Thank you so much for the advice everyone. I think I am on the right track now. This board is such a great resource with so many knowledgeable people.

    Jenna

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Newbie homework help
    By fossage in forum C Programming
    Replies: 3
    Last Post: 04-30-2009, 04:27 PM
  2. Data Structure Eror
    By prominababy in forum C Programming
    Replies: 3
    Last Post: 01-06-2009, 09:35 AM
  3. Can we have vector of vector?
    By ketu1 in forum C++ Programming
    Replies: 24
    Last Post: 01-03-2008, 05:02 AM
  4. pointers
    By InvariantLoop in forum C Programming
    Replies: 13
    Last Post: 02-04-2005, 09:32 AM
  5. a file problem !!
    By girlzone in forum C++ Programming
    Replies: 17
    Last Post: 04-15-2003, 07:55 PM