Thread: Creating File Handling Functions

  1. #1
    Registered User
    Join Date
    Jun 2009
    Posts
    11

    Creating File Handling Functions

    Hi All,

    I would like to simplify some of my code to handle the opening and writing of files. Currently, every time I open or write a file, I have the code right there in the program. I'd like to write two functions (readfile, writefile) so that I save space, and can reuse this code.

    First, to open a file, I would like to pass to it a file name to open. How can I do this? Pass a char array? How would I do this. Say I want to open a file named "file.txt"?

    Currently, I use this method to open a file:

    Code:
    unsigned int array[200][200][10];
    
      if (!(file = fopen("text.txt","rb"))){
        exit(1);
      }
      fread(array, sizeof(unsigned char), filesize, file);
      fclose(file);
    
    // with this, my array is filled with the file contents...

    OPENFILE()

    How do I get the file name from a passed character array into the "text.txt" place? Or, can I just say:
    Code:
    openfile("file.txt");
    
    or
    
    openfile(filename[]);

    Then, I want this function to return a 3 dimensional array of the contents from that file, say.. array[200][200][10]. How would I pass this back? In the openfile function go:
    Code:
    return array[][][];
    And what call do I make in my code? Do I do something like:
    Code:
    array[][][] = openfile(filename[]);

    WRITEFILE()

    Then, I will do some processing on this 3 dimensional array, and then I want to pass the array and a file name to a "write file" function. How can I pass the array, and the file name (array?) and have that function write the file.

    Do I do something like:
    Code:
    writefile(array[][][], filename[]);

    I know how to do the file handlng as I already have the code for it.

  2. #2
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    There are a couple of issues you have to be aware of here.

    Here's the first one. If you want to pass a string to a function (say, the filename), then try something like this.
    Code:
    void function(const char *filename) {
        /* ... */
    }
    
    function("file.txt");
    
    const char *file2 = "file2.txt";
    function(file2);
    Notice how if you're passing in a char* (or a char[]) to a function, you just use the name of the variable. Here's an example in three dimensions.
    Code:
    void threeD(int array[][2][2]) {
        /* ... */
    }
    
    int data[2][2][2] = {
        { {1, 2, 3}, {4, 5, 6} },
        { {7, 8, 9}, {10, 11, 12} }
    };
    threeD(data);
    The last line can be written, equivalently, as
    Code:
    threeD(&data[0]);
    if you prefer.

    There's an important point hidden in that little example. When you pass a multi-dimensional array to a function, you have to specify in the function prototype (or definition) the size of every dimension of the array, except for the leftmost dimension. So to pass my 3D array of type int [2][2][2] to the function, I had to say "int array[][2][2]" in the function itself.

    There's a reason for this. In C, even if you say you want a multidimensional array, beneath the hood it's really just a one-dimensional chunk of contiguous memory. Inside a calling function, if you have a three-dimensional array and you ask for array[1][1][1], the code has to calculate the offset into this chunk of memory. The array is laid out logically, with array[0][0][0] being the first element, then array[0][0][1], array[0][0][2], and so on; then comes array[0][1][0], array[0][1][1], etc. In other words, if you want to calculate where array[1][?][?] is, you have to know how many elements are in the rightmost column and how many elements are in the next rightmost column. Because array[1][0][0] is one element after array[0][N][M], where N and M are as big as they are allowed to be.

    That's wasn't a very good explanation, I don't think. Oh well. The upshot is that because all a function is passed is the address of the beginning of the array, it has to know the size of most of the array's dimensions to calculate offsets into it.

    I guess this won't really be a problem for you, since you have hard-coded values here:
    Code:
    unsigned int array[200][200][10];
    But if you're trying to pass an array which can vary in size to a function, it's inconvenient to have to specify the dimensions in the function itself. If the dimensions are different for a different array, well, you can't use that array. There are a few ways to get around this, such as "flattening" the array into a one-dimensional array and calculating the offsets yourself with size parameters passed into the function, but I won't go into it because you probably wouldn't be interested.

    Anyway, that's almost enough information to do what you want to do. The other important point is that you can't return arrays. Sorry, that's just the way it is. I guess the language designers thought it was too inefficient. The easiest way to get around this is to pass a pointer to the array you want to fill into the function. For example:
    Code:
    void zero_everything(int (*data)[2][2]) {
        int x, y;
        for(x = 0; x < 2; x ++) {
            for(y = 0; y < 2; y ++) {
                (*data)[x][y] = 0;
            }
        }
    }
    Notice the syntax I've used; make sure you use the parentheses as I have it. If you leave them out the code does something different, due to operator precedence.

    So . . . yeah. Here's a skeleton. Read the above, adopt this, and have fun.
    Code:
    void read_data(const char *file, unsigned int (*array)[][200][10]) {
        /* ... */
        /* We use *array here because that gives us a pointer to the beginning of a three-dimensional array,
            which is what we want.
        */
        fread(*array, sizeof(unsigned char), filesize, file);
        /* ... */
    }
    
    /* in some other function . . . */
    unsigned int data[200][200][10];
    char filename[BUFSIZ], *p;
    fgets(filename, sizeof filename, stdin);
    if((p = strchr(filename, '\n'))) {
        *p = 0;  /* remove newline */
    }
    open_file(filename, data);
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  3. #3
    Registered User
    Join Date
    Jun 2009
    Posts
    11
    Hey thanks. I have started to build my "savefile" function.

    In my code, I have this:

    Code:
    int inputarray[256][256][1];
    
    //do operations on inputarray
    
    const char *file2 = "file2.raw";
    savefile(file2,inputarray);
    // ...or...
    savefile("name.raw",inputarray);
    And my savefile function is:
    Code:
    savefile(const char *filename, int array[][256][1]){
      FILE *file;
      int filesize = 65536;
    
      if (!(file=fopen(filename,"wb")))
      {
        printf("\n Cannot open file: ");
        exit(1);
      }
      fwrite(array, sizeof(unsigned char), filesize, file);
      fclose(file);
    }
    This does write my files properly, however I get this warning when compiling with gcc:
    "warning: passing argument 2 of 'savefile' from incompatible pointer type"

    So, there seems to be a problem with the way I am passing the inputarray to the savefile() function. What is causing the warning? I can't seem to figure it out.

  4. #4
    Registered User
    Join Date
    Jun 2009
    Posts
    11
    Also, I am trying to make a function that modifies an array in the function that calls it. Since I cannot return arrays, I guess I need to do it this way.

    In your example below, I understand how to set up the void function to operate on the array that is in the calling function (is that the right term?):

    Code:
    void zero_everything(int (*data)[2][2]) {
        int x, y;
        for(x = 0; x < 2; x ++) {
            for(y = 0; y < 2; y ++) {
                (*data)[x][y] = 0;
            }
        }
    }
    But, how do I call this function? Like this?

    Code:
    int data[2][2];
    
    zero_everything(data[][2]);
    
    or
    
    zero_everything(*data[][2]);
    
    or
    
    ???
    Thanks dwks!

  5. #5
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    The parentheses contain the object you want to send to the function. In this case, that object is "data".

  6. #6
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    In fact you can just do this:
    Code:
    #include <stdio.h>
    
    void testfunc(int ray[2][2]){
    	ray[1][1] = 666;
    }
    
    int main() {
    	int ray[2][2] = {0};
    	testfunc(ray);
    	printf("%d\n",ray[1][1]);
            return 0;
    }
    Working with int arrays seems somewhat simpler that char arrays for one reason or another...
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  7. #7
    Registered User
    Join Date
    Sep 2008
    Location
    Toronto, Canada
    Posts
    1,834
    If you're calling the function savefile() before defining it (before, as in, when you read the source file top to bottom), then you really need to put in a prototype declaration at the top someplace:
    Code:
    void savefile(const char *filename, int array[][256][1]);
    That way the compiler can properly check the way you're passing the arguments. I think you may be getting incompatible pointer type because in the absence of prototype, the compiler assumes the second argument would be a simple int... not having seen your function's definition below.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Need Help Fixing My C Program. Deals with File I/O
    By Matus in forum C Programming
    Replies: 7
    Last Post: 04-29-2008, 07:51 PM
  2. Basic text file encoder
    By Abda92 in forum C Programming
    Replies: 15
    Last Post: 05-22-2007, 01:19 PM
  3. Problem while dealing with file handling functions
    By RoshanGautam in forum C Programming
    Replies: 3
    Last Post: 02-22-2006, 01:42 AM
  4. Simple File encryption
    By caroundw5h in forum C Programming
    Replies: 2
    Last Post: 10-13-2004, 10:51 PM