GIDNetwork > Programming Techniques
Register
« Formatting C / C++ code Beginning Python Tutorial (Part 3) »

Programming Techniques

by: WaltP - Aug 13, 2005

Last week I wanted a program that would give me the length of time a .wav file would play without actually playing the file. So I wrote a program to calculate the length of the track based upon the file size. I used various techniques within the program that I thought would be useful as a guide on how to do some standard procedures.

I'll go through the program and point out how to:

  • process command line parameters

  • find the byte size of a file

  • output a large number with comma separators

  • output an integer as a time value

The program

C/CPP/C++ Code Example:

/* Program: WaveTime   by WildeWare
   This program will compute:
   1) the time length of given a WAV file name
   2) the time length of given a file size
   3) the file size of a given time length
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define TIMESIZE 176555L
#define FILESIZE  '#'
#define FILETIME  '@'

//==========================================
// Display the integer parameter fsize as a
// comma separated value
//
void DisplayCommaDelim(long fsize)
{
    long parts[6];                    // up to 6 3-digit segments
    int  p;                           // index into parts[]

    p = 0;                            // start index at 0
    while (fsize > 0)                 // loop while there is size left
    {
        parts[p] = fsize % 1000;      // get the lower 3 digits
        fsize /= 1000;                // remove these digits
        p++;                          // next index
    }
    printf(" %3d", parts[--p]);       // output the high order segment
    while (p > 0)                     // loop for the rest of the segs
    {
        printf(",%03d", parts[--p]);  // output each seg zero filled
    }
}


//=================================================
// Convert and display the char* parameter timeval 
// from "mm:ss" to the file size in bytes
//
void DisplaySize(char *timeval)
{
    long  fsize;                    // file size
    int   ftime;                    // time in seconds
    int   fsec;                     // seconds from time string
    char *p;                        // pointer to ':'
    
    ftime = atoi(timeval);          // convert minutes to integer
    p = strchr(timeval, ':');       // find the : in the time string
    if (p)                          // If there was a : ...
    {
        p++;                            // skip the :
        fsec = atoi(p);                 // convert seconds to integer
        ftime = (ftime * 60) + fsec;    // finish converting time to seconds
    }
    fsize = ftime * TIMESIZE;       // calculate size of file based on time
    DisplayCommaDelim(fsize);       // display the file size
    printf("%6s  ", timeval);       // display the time value
    printf("\n");   
}


//=================================================
// Convert and display the integer parameter fs (file size) 
// into the length of time 
// fl is the file name
//
void DisplayTime(long fs, char *fl)
{
    int   ftime;                    // time length of the file
    int   fmin;                     // file minutes
    int   fsec;                     // file seconds

    ftime = fs / TIMESIZE;          // compute the time length from size
    fmin = ftime / 60;              // compute the minutes
    fsec = ftime % 60;              // compute the seconds
    DisplayCommaDelim(fs);          // display the file size
    printf("  %d:%02d  %s \n", fmin, fsec, fl); // file time & name
}

//====================================
// The Program:
//
int main(int argc, char *argv[])
{
    int   i;
    long  fsize;                    // length of the file in bytes
    int   parm = 1;                 // index to command line parameters
    char  fname[256];               // file name to compute time 
    char *tparam;                   // pointer to the time parameter
    FILE *st;                       // file pointer
    
    // The following while loop will check each command line parameter    
    while (parm < argc)             // argv is the current command line parameter
    {
        // The first two ifs will test the first character in the parameter
        // for the special value signifying a time or file size entry
        
        if (argv[parm][0] == FILESIZE)      // if the parameter starts with FILESIZE, then
        {                                   //   the parameter is a file size
            fname[0] = 0;                   // no file name
            fsize = atol(&argv[parm][1]);   // convert to long
        }
        else
        if (argv[parm][0] == FILETIME)      // if the parameter starts with FILETIME, the
        {                                   //   parameter is a time length
            fsize = -1;                     // no file size
            tparam = &argv[parm][1];        // point to the time parameter
        }
        else
        {                                   // file name was entered
            strcpy(fname, argv[parm]);      // copy the file name for later
            st = fopen(fname, "rb");        // open the file in binary mode
            if (st == NULL)
            {                               // test for good file open
                printf("File %s was not opened \n", fname);
                fsize = -1;
            }
            else
            {
                // This segment will get the size of the file in bytes
                fseek(st, 0, SEEK_END);     // move to the end of the file
                fsize = ftell(st);          // get the byte count
                fclose(st);                 // close the file
            }
        }
        if (fsize >= 0)                     // if the above received a file size...
                DisplayTime(fsize, fname);  // call the time display with the size
          else  DisplaySize(tparam);        // else the info based on the time specified
        
        parm++;     // next command line parameter
    }
    return 0;
}

The command line parameters are:

filename -- the name of a file to display the file size and track length
#size -- a specific file size, calculates the time length
@time -- a time value in minutes or mm:ss format, calculates an approximate file size for the specified length of the track.

These parameters are independant of each other.

I elected to use the form #size instead of the more standard /S:size or /S size just to be ornery. Gives you another potential option for parameters for personal programs, as this form is a tad easier. But the other styles should be used for any program that others will use, these styles will also be discussed.

There is no help information within the program. I will leave the implementation of a help routine to you should you wish to experiment. Check out the command lineparameters below for techniques to add a help parameter.

Process Command Line Parameters

This is the most complex of the procedures -- and the most useful. The code in question is:

C/CPP/C++ Code Example:

int main(int argc, char *argv[])
{
    int   i;
    long  fsize;                    // length of the file in bytes
    int   parm = 1;                 // index to command line parameters
    char  fname[256];               // file name to compute time 
    char *tparam;                   // pointer to the time parameter
    FILE *st;                       // file pointer
    
    // The following while loop will check each command line parameter    
    while (parm < argc)             // argv is the current command line parameter
    {
        // The first two ifs will test the first character in the parameter
        // for the special value signifying a time or file size entry
        
        if (argv[parm][0] == FILESIZE)      // if the parameter starts with FILESIZE, then
        {                                   //   the parameter is a file size
            fname[0] = 0;                   // no file name
            fsize = atol(&argv[parm][1]);   // convert to long
        }
        else
        if (argv[parm][0] == FILETIME)      // if the parameter starts with FILETIME, the
        {                                   //   parameter is a time length
            fsize = -1;                     // no file size
            tparam = &argv[parm][1];        // point to the time parameter
        }
        else
        {                                   // file name was entered
            strcpy(fname, argv[parm]);      // copy the file name for later
            ...
        }
        parm++;     // next command line parameter
    }

In detail:

The main() function starts with the line:

C/CPP/C++ Code Example:

int main(int argc, char *argv[])

The parameters are:
argc -- the count of arguments.
argv -- an array of character array pointers

The first command line argument is in argv[0]. This argument is always the program name. On some systems it will contain the full path to the program, on others it will be just the file name itself.

Each successive value after argv[0] will be the additional parameters added to the command line. For example:

WaveTime TheThing.wav #14323432 @12:34

will yield:

  • argc = 4

  • argv[0] = WaveTime (program name)

  • argv[1] = "TheThing.wav"

  • argv[2] = "#14323432"

  • argv[3] = "@12:34"

In the code you will notice the variable parm. That value will be the parameter index into the argument list. Other useful names would be param, paridx, anything that tells the 'next guy' what the variable is for. This value will start at 1 because we don't care about the program name parameter.

We then start looping through all the parameters until the parameter count parm is equal to the number of parameters argc:

C/CPP/C++ Code Example:

while (parm < argc)
{
    // == loop contents == //
    parm++;     // increment parameter counter at end of the loop
}

I prefer using a while loop for parameters, although a for loop in most cases would also work. If a parameter is a "two parter", such as /F filename where the /F designates a filename will follow, the while loop is a much better choice.

In this program, we must check the special parameter types first, the filesize and filetime parameters, checking for the '#' and '@' respectively. This can be done in a few ways, most common are of course the nested if and the switch statements. Here I opted for the if. I will also show you the switch version. After that will be the standard / and - option. Also, remember, these arguments are character buffers.

1) Nested if:

First we check for the special characters, '#' and '@', both of which will have a value immediately afterwards. The final else is the catch-all that will assume a filename:

C/CPP/C++ Code Example:

if (argv[parm][0] == FILESIZE)      // if the parameter starts with FILESIZE, then
{                                   //   the parameter is a file size
    fname[0] = 0;                   // no file name
    fsize = atol(&argv[parm][1]);   // convert to long
}
else
if (argv[parm][0] == FILETIME)      // if the parameter starts with FILETIME, the
{                                   //   parameter is a time length
    fsize = -1;                     // no file size
    tparam = &argv[parm][1];        // point to the time parameter
}
else
{                                   // file name was entered
    strcpy(fname, argv[parm]);      // copy the file name for later
    ...
}

If the first character argv[parm][0] is equal to '#' (FILESIZE), a numeric value follows the character. Simply pass the address of the next character (argv[parm][1]) to atol() or strtol() to convert it to a long integer. The 1 designates the second character in the current command line parameter, the & passes the address of this character. So if the parameter is #14323432, then

  • argv[parm] = "#14323432" (string)

  • argv[parm][0] = '#' (character)

  • argv[parm][1] = '1' (character)

  • &argv[parm][1] = "14323432" (string)

If the first character argv[parm][0] is equal to '@' (FILETIME), a time value follows the character. The fsize value is set to -1 to flag for later that a size value was not entered.

If neither of the above situations is true, fall into the default else and process the parameter as a file name.

2) Switch:

Not much different than above. The code is simply:

C/CPP/C++ Code Example:

switch (argv[parm][0])
{
  case FILESIZE:
    fname[0] = 0;
    fsize = atol(&argv[parm][1]);
    break:
  case FILETIME:
    fsize = -1;                     // no file size
    tparam = &argv[parm][1];        // point to the time parameter
    break:
  default:
    strcpy(fname, argv[parm]);      // copy the file name for later
    ...
    break;
}

3) Standard Option #1:

This uses the normal switch designation of '/' and '-' to start a parameter. For example, we define:
/Ssize as the File Size switch
/Tmm[:ss] as the Time switch (minutes with optional seconds). No space between the switch and the value.

The code to process this technique can be written as:

C/CPP/C++ Code Example:

if ((argv[parm][0] == '-') ||
    (argv[parm][0] == '/'))         // check for the switch designation
{
    switch(toupper(argv[parm][1]))  // check the switch value (upper case convert)
    {
      case 'S':
        fname[0] = 0;
        fsize = atol(&argv[parm][2]);
        break;
      case 'T':
        fsize = -1;                     // no file size
        tparam = &argv[parm][2];        // point to the time parameter
        break;
      default:
        printf("Illegal switch '%c' \n", argv[parm][1]);
        break;
    }
}
else
{   // If not a switch designator, it's a file name
    strcpy(fname, argv[parm]);      // copy the file name for later
    ...
}

4) Standard Option #2:

Similar to the above, but a space is placed between the switch and the value:
/S size
/T mm[:ss]

The code to process this technique can be written as:

C/CPP/C++ Code Example:

if ((argv[parm][0] == '-') ||
    (argv[parm][0] == '/'))             // check for the switch designation
{
    switch(toupper(argv[parm][1]))      // check the switch value (upper case convert)
    {
      case 'S':
        fname[0] = 0;
        fsize = atol(argv[++parm]);     // Next parameter (++parm)
        break;
      case 'T':
        fsize = -1;                     // no file size
        tparam = argv[++parm];          // point to the time parameter
        break;
      default:
        printf("Illegal switch '%c' \n", argv[parm][1]);
        break;
    }
}

So now, go forth and read the command line!

Find the Size of a File in Bytes

There are times you may need to figure out programatically how large a file is. One technique, using standard calls uses:

  • fopen() -- open the file

  • fseek() -- move to the end of the file

  • ftell() -- return the current byte position in the file

  • fclose() -- close the file

This technique uses all standard function calls -- no system dependant functions so it should work with all compilers. The code is:

C/CPP/C++ Code Example:

st = fopen(fname, "rb");        // open the file in binary mode
if (st == NULL)
{                               // test for good file open
    printf("File %s was not opened \n", fname);
    fsize = -1;                 // flag the size as invalid
}
else
{
    // This segment will get the size of the file in bytes
    fseek(st, 0, SEEK_END);     // move to the end of the file
    fsize = ftell(st);          // get the byte count
    fclose(st);                 // close the file
}

Open the file for read in binary mode. Use binary so the end-of-line characters are not translated which may change the number of bytes.

See the Standard I/O tutorial for specifics on the calls.

Output a Large Number with Comma Separators

C/C++ does not have a standard output format that allows large numbers to be displayed with imbedded commas. We get the likes of 25364542. So, if you want to output an integer with commas, here's one technique:

  1. create an array large enough to hold each 3-digit segment.

  2. store the number modulo 1000 (the last 3 digits) into this array.

  3. divide the value by 1000 to remove the last 3 digits.

  4. repeat the steps 2 & 3 until the value is <= 0

  5. ouput the last value stored in the array as an integer

  6. loop thru the remaining values, outputting first a comma then the value with 3 zero filled digits

The above technique as a function:

C/CPP/C++ Code Example:

void DisplayCommaDelim(long fsize)
{
    long parts[6];                    // up to 6 3-digit segments
    int  p;                           // index into parts[]

    p = 0;                            // start index at 0
    while (fsize > 0)                 // loop while there is size left
    {
        parts[p] = fsize % 1000;      // get the lower 3 digits
        fsize /= 1000;                // remove these digits
        p++;                          // next index
    }
    printf(" %3d", parts[--p]);       // output the high order segment
    while (p > 0)                     // loop for the rest of the segs
    {
        printf(",%03d", parts[--p]);  // output each seg zero filled
    }
}

In the last printf(), the %03d outputs the value with 3 digits, zero filled. For example, the value 2 will be displayed at 002.

Thie function outputs up to 999,999,999,999,999,999 (1 quintillion - 1):American or (1 trillion - 1):British. Unfortunately, a long can't handle a number that big but doesn't it feel good to know you're ready for 64 bit processors?

Output an Integer as a Time Value

This is simply a variation of the comma-separated values.

  1. Get the seconds by calculating the value modulo 60

  2. Divide by 60

  3. Value is now number of minutes.

  4. If value is > 60, repeat steps 1 & 2 to get minutes, value is now hours.

  5. If value is > 24, repeat steps 1 & 2 with 24 instead of 60 to get hours, value is now days.

  6. etc.

  7. Display the values separated by the approriate characters ':'

The above technique written as a function (hh:mm:ss only):

C/CPP/C++ Code Example:

void DisplayTime(long ftime)
{
    int   fhour;
    int   fmin;
    int   fsec;

    fsec = ftime % 60;               // compute the seconds
    fmin = ftime / 60;               // compute the minutes left
    fmin = ftime % 60;               // compute the minutes
    fhour= ftime / 60;               // compute the hours left
    printf("%2d:%02d:%02d\n", fhour, fmin, fsec);
}

Again, the %02d is used to force 2-digit values, 01, 02, 10, etc.

Would you like to comment? This story has been viewed 18,825 times.
« Formatting C / C++ code Beginning Python Tutorial (Part 3) »

__top__

Copyright © GIDNetwork™ 2001 - 2024

Another website by J de Silva

Page generated in : 0.01095 sec.