|
||||
|
« 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:
The programC/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 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 ParametersThis 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: 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:
will yield:
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
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: 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: 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 BytesThere are times you may need to figure out programatically how large a file is. One technique, using standard calls uses:
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 SeparatorsC/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:
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 ValueThis is simply a variation of the comma-separated values.
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.
|
GIDNetwork Sites
Archives
Recent GIDBlog Posts
Recent GIDForums Posts
Contact Us
|
« Formatting C / C++ code | Beginning Python Tutorial (Part 3) » |