I've been working on this project for about two weeks now. Essentially it checks the date, finds events for the day, tells you about them and even lets you plan new ones.

Thinking this would be a great deal easier than it became, I thought I would have gotten further on this project by now.

Call me irrational but I attempted to read the events from a text file using only C-strings (in C++, yes really) and some library functions. I wanted to learn more about C and C++ and how C-strings are/were used.

Which leads me to the question: was it this difficult in the 1960's and 1970's for Computer Scientists to create programs? I've had so much trouble with simply extracting the fields from my text file!

While thinking about the fact that C didn't have much in the way of OOP when it was created (though my knowledge is not that great about C) I cannot help that I, someone who has programmed as a hobby for several years and considers himself very competent cannot get a simple program to operate as desired! Also noted by myself is because they didn't have OOP before C++, so it must have been very difficult to program computers in that time period!

What do you think?C,ccccccc

Recommended Answers

All 25 Replies

Early incarnation of what we think of as objects can be traced to the fifties, and in the sixties there were certainly many languages making use of them. OOP predates C++ by a great many years.

What do you think?

I think you have an overinflated opinion on the power of OOP. C still doesn't have OOP[1], and it remains a very popular language. Further, nothing about your requirements suggests that OOP would solve any problems, though it might create some. ;)

cannot get a simple program to operate as desired!

What's the format of your file? That will be the deciding factor in how difficult it is to parse. Personally, I'd prefer a relational database for this, but having a file as your database isn't irrational for a relatively smaller number of records.

[1] It can be simulated in C, but the language doesn't have direct support like C++.

Moschops I suspected that was the case; but I was unsure.

My file (Deceptikon) is simply the time and date separated by colons ':', terminated by vertical line chararacters (unsure about what to call them) '|'. It spans multiple lines, and the newline character is skipped accordingly. The thing I have the most trouble with is after putting the entire file in to a memory allocation via the new operator I found it impossible to extract the data in to fields. I tried passing my pointer to the data to the string library functions (such as strcpy) but received complaints from the compiler concerning being unable to convert from 'char *' to 'const char'. or something like that.

Looking back (I may just give up, this is too boring a project to be so frustrated about) I think I could have done this more easily by reading from the file using my file stream and its corresponding .read() function in to a temporary string (or c-string :P) and then putting those in to the correct field variables.

It was working on one of my first iterations but then I realized I had only left enough elements in the arrays which would hold the event data for 16 events. If the file exceeded 16 or I wrote too much data to the file some of that data would be lost and unaccounted for upon next run. So I had to jump through some hoops to do some dynamic memory allocation.

I probably did not need to allocate memory for the entire file; but I'm a stubborn son of a gun and sometimes I think my way is the best way.

Maybe I will try it again with these ideas, or if someone could give me better ideas.

*Which leads me to the question: was it this difficult in the 1960's and 1970's for Computer Scientists to create programs? *

Only if they were foolish enough to use C or C++ for application programming. Applications like yours would be straight forward in business-oriented languages like Cobol, PL/1, RPG etc. Yes, you need C or C++ if you need to deal with memory allocations and addresses etc, but for business applications these just add a layer of difficulty and error-proneness that isn't appropriate. <waits for flame>

That leads me another question: what exactly do most people use C/C++ for anyway? I always assumed they were just for creating software, any software.

It's really only justified where you need the absolute maximum performance, or you need to get very close to the hardware (eg device drivers). For anything else there are far more productive and easy languages (C#, Visual Basic, Objective C, swift, java, python, php, javascript, ruby etc etc)

Productive huh? Lol I hear ya! This has been a very boring exercise in frustration for me.

It is possible to create some 'tools' (functions) in C that emulate things available in C++ and Python ...

Isn't Python coded in C?

You might like to try out some things in this mini library of C utilities ...

... function that are in the following files:

readLine.h
readWord.h
split.h // returns a Clist of parsed dynamic C string 'words' //
Cvec.h
Clist.h
etc...

http://developers-heaven.net/forum/index.php/topic,2608.0.html

The other posters hit on all this,but I'd like to emphasize you need decent libraries to build business or technical applications reasonably easy in C++. This was a problem I faced early on in my C++ work (like 15 years ago), and my solution was to completely ditch the C++ Standard Library and write my own.

Arrays in C++ are pathetic compared to the capabilities offered by other languages. The same can be said of string management. And a lot of other things too.

If you would care to provide further details of the file structure you've come up with and a few more details of the app you are trying to build, I'd take a look at it and see if any of my library code (which I'd be happy to share) would help.

Basically, I do line of business and technical apps in C++ (for Windows using its Api) and put up with some of the downfalls of the language because the benifits outweigh the downfalls. For example, its not a language that will go away if any company fails. This has been an issue with some of the work I do. Also, it is fast and can be efficient if coded properly.

@Frederick2

Do you have any Windows graphic library function calls ...
anything like this ...

move_to(int x, int y);
move_to(double x, double y);

draw_to(int x, int y);
draw_to(double x, double y);

etc... ?

[QUOTE]
Do you have any Windows graphic library function calls ...
anything like this ...

move_to(int x, int y);
move_to(double x, double y);

draw_to(int x, int y);
draw_to(double x, double y);

etc... ?
[\QUOTE]

No, I infrequently need graphic calls in my apps, and while GDI is deprecated, I still use it directly on those rare occasions I do need to do any drawing.

Basically, I need relatively easy database access in my apps, and had always used ODBC directly going back to my C days in the 90s. At that time I had built my own wrappers around the ODBC function calls. Sometime after I taught myself C++ I converted that wrapper code to an ODBC class, and that works well for me.

In terms of dynamic multi-dimensional arrays, which are critical to my work (I need up to four dimensions), I created a templated class for that which operates after the basic model, where I’ve overloaded the ‘(‘ and ‘)’ operators so as to give me access like so …

Ar(2,4,6)

Instead of this C/C++ ism…

Ar[2][4][6].

I also like the way the basic family of languages handle strings, so I wrote my own string class which I use in place of the one in the C++ Standard Library. Its mosly wrappers around the C Standard Library string primitives, i.e., wcscpy(), wcscat(), wcslen(), etc. A lot of useful functions for dealing with apps such as the OP described I put in my string class, such as parsing strings, inserting string segments within an original string which causes it to grow, etc.

One of the reasons I did all this was to avoid the bloat of typical C++ development. Its very idyiosyncratic I realize, but it works for me.

Hmmm... no I just gave up on the project. Using C/C++ is just such a pain. I understand where you're coming from Frederick about what happens when a company which implements and maintains a language goes out of business (it hasn't happened yet but I think we both mean Sun Microsystem's Java Language).

That is an awesome offer though!

As for my "app" its really just command-line interface. The file structure was intended to be delimited by colons ':' like I said before. Each record was to be ended with a vertical line char '|'. That's really all there is to it.

Sounds like you've just become too frustrated. If I had to guess, you simply haven't been exposed to enough of the standard library, or seen (and created) enough C++ code, to be able to work with the grain of the language; by which I mean, some tasks have very easy and elegant implementations in C++, and also very ugly obtuse implementations in C++, and being able to come up with the former is a skill and craft that takes both knowledge and experience.

The above sounds about right. lol

If you are still interested in this topic CuriousGeorge I’ll present some code that I believe might solve some of the specific issues you mentioned.

Here only about a month ago I removed a String::Parse method, as well as a couple others, out of my String Class, so that code could be used in a C like manner in cases where I didn’t want to compile the entirety of my String Class into my executable. Making my executables and dlls really small is something I glory in, and I’ll wear myself to a frazzle at times to accomplish it.

Anyway, taking your specific case, assume we have a text string comprised of three semicolon delimited fields such as this…

wchar_t szString[]=L"9:00 AM; 6/1/2015; Dentist Appointment";

Here is an example program showing how to separate those three fields out using only C isms and no high powered C++ stuff…

#include <cstdlib>
#include <cstdio>
#include <string.h>


size_t iParseCount(const wchar_t* pString, wchar_t cDelimiter)
{
 int iCtr=0;                           //reflects # of strings delimited
 const wchar_t* p;                     //by delimiter.

 p=pString;
 while(*p)
 {
  if(*p==cDelimiter)
     iCtr++;
  p++;
 }

 return ++iCtr;
}


void Parse(wchar_t** pStrings, const wchar_t* pDelimitedString, wchar_t cDelimiter)
{
 wchar_t* pBuffer=NULL;
 const wchar_t* c;
 wchar_t* p;
 size_t i=0;

 pBuffer=(wchar_t*)malloc(wcslen(pDelimitedString)*sizeof(wchar_t)+sizeof(wchar_t));
 if(pBuffer)
 {
    pBuffer[0]=0;
    p=pBuffer;
    c=pDelimitedString;
    while(*c)
    {
       if(*c==cDelimiter)
       {
          pStrings[i]=(wchar_t*)malloc(wcslen(pBuffer)*sizeof(wchar_t)+sizeof(wchar_t));
          wcscpy(pStrings[i],pBuffer);
          p=pBuffer;
          i++;
          pBuffer[0]=0;
       }
       else
       {
          *p=*c, p++;
          *p=0;
       }
       c++;
    }
    pStrings[i]=(wchar_t*)malloc(wcslen(pBuffer)*sizeof(wchar_t)+sizeof(wchar_t));
    wcscpy(pStrings[i],pBuffer);
    free(pBuffer);
 }
}


void LTrim(wchar_t* pStr)
{
 size_t iCt=0, iLen=0;

 iLen=wcslen(pStr);
 for(size_t i=0; i<iLen; i++)
 {
     if(pStr[i]==32 || pStr[i]==9)
        iCt++;
     else
        break;
 }
 if(iCt)
 {
    for(size_t i=iCt; i<=iLen; i++)
        pStr[i-iCt]=pStr[i];
 }
}


void RTrim(wchar_t* pStr)
{
 size_t iCt=0, iLen=0;

 iLen=wcslen(pStr);
 for(size_t i=iLen-1; i>0; i--)
 {
     if(pStr[i]==9||pStr[i]==10||pStr[i]==13||pStr[i]==32)
        iCt++;
     else
        break;
 }
 pStr[iLen-iCt]=0;
}


void Trim(wchar_t* pStr)
{
 LTrim(pStr);
 RTrim(pStr);
}


int main()
{
 wchar_t szString[]=L"9:00 AM; 6/1/2015; Dentist Appointment";
 wchar_t** pStrs=NULL;
 size_t iStrings;

 wprintf(L"szString = %s\n",szString);
 iStrings=iParseCount(szString, L';');
 printf("iStrings = %u\n\n",iStrings);
 pStrs=(wchar_t**)malloc(iStrings*sizeof(wchar_t*));
 if(pStrs)
 {
    Parse(pStrs,szString,L';');
    for(size_t i=0;i<iStrings; i++)
    {
        Trim(pStrs[i]);
        wprintf(L"pStrs[%u] = %s\n",i,pStrs[i]);
    }
    for(size_t i=0;i<iStrings; i++)
        free(pStrs[i]);
    free(pStrs);
 }
 getchar();

 return 0;
}

Output:

szString = 9:00 AM; 6/1/2015; Dentist Appointment
iStrings = 3

pStrs[0] = 9:00 AM
pStrs[1] = 6/1/2015
pStrs[2] = Dentist Appointment

Above you can see the three separate Strings are accessable as pStrs[0], pStrs[1], and pStrs[2]. By the way, using my old Code::Blocks 10.05 version which shipped with GCC v4.4, and compiling for small code (Os, O1), my executable is coming in only 7 K with that. I did compile with C++ however, even though there are no C++ isms in it.

The next topic to handle concerns arrays. If you respond to this I’ll provide ways of dealing with those. Also, I can comment that and explain it if need be. I just didn’t take the time. Also, if you are using one of the newer C++ GNU versions (4.7, 4.8, 4.9), those %s format specifiers in my printf statements need to be %S (capital S).

Wow! Some of the tings you did in that code were really simple and things I didn't know or think were possible. For example to check the ascii code of an array element I thought you had to use a library function to return the code. Instead you just tested for an integer value, like on line 67!

May I ask why you use wchar_t and size_t in this program? I didn't know what those were. I did some research and now I know their data width and wikipedia said something about locale.

I think I will give it another shot!

"I think I will give it another shot!"

That's what I was hoping. Its a program you should be able to do. But wait 'till I give you some array code. That's the final missing ingredient.

In terms of wchar_t, that's a typedef of an unsigned short int, which is something of an ideal construct for storing a wide character, i.e., what we usually think of as a UNICODE character. I mostly work in wide characters nowadays, and while I don't know I suspect a lot of others do to.

In terms of size_t, that's an operating system defined type that is 32 bits for x86 systems and 64 bits for x64 systems. Things being what they are right now, i.e., transitioning from x86 to x64 code, I test compile on x86 and x64 systems. I suspect there may be reasons not to use the size_t type as I do, but its something of a struggle for me to learn all the subtle nuances of the various variable types.

Yes, to a computer, the characters that we read are just numbers.

Haven't posted here for about 4 years, so I'm trying to catch up! :)

In terms of the way you set out to solve your appointment app CuriousGeorge, i.e., parsing a delimited text file, you'll almost inevitably need to read the entirety of that text file into memory and operate upon it there during the time your app is running. In such an app, if the user has the option of editing records, adding new ones, etc., then upon closing the app the possibly edited data in memory would be written back to storage.

Now, since there likely will be more than one line in the underlying data file, and each line may be comprised of several colon, comma, etc., delimited fields, this data model suggests a two dimensional array. If there are 5 records, and each record contains three fields, then a 5 X 3 array would be efficient. But the thing is, the bare C and C++ languages without the Standard Libraries are pretty miserable at arrays. You would like your program to be able to handle an underlying data file with a variable number of lines in it. But you can't know the number of lines until the program is run. In Microsoft's C/C++ compiler multi-dimensional arrays have to have constant sizes, i.e., you can't specify variables in the array declaration. The GCC C/C++ compiler allows this, but there are other significant issues involved.

So lets see about solving this in an efficient C like manner. Once you understand the basics in C, we can encapsulate the technique within a C++ Class, and if I must say so myself, it can make for a pretty slick implementation.

So here are the two issues with dynamic multi-dimensional arrays...

1) We don't know how big the arrays need to be until the program is run and the underlying data file is read, and...

2) How are dual subscripts such as [2][3] or (2, 3) translated into the desired memory location within the underlying linear memory allocation for the array?

To make things simple lets just work with ints for now. What is easy to know is that if we need a 5 X 3 array that is going to require 15 ints. And in x86 or x64 each int is four bytes. So we are going to need 15 X 4 = 60 bytes.

We can easily get that with C's malloc or C++'s new, or even operating system specific allocators such as HeapAlloc() in Windows...

int* pInt = new int[15];

or...

int* pInt = (int)malloc(15sizeof(int));

But in the above case we can only access the individual array elements as a single dimension array in a linear address space. But its not too terribly hard to change that. Assume i represents our zero based row and j our zero based column, and d2 is our 2nd dimension - in our case 3, then this macro will translate a two dimensional memory model into the base offset linear notation the computer will understand...

#define  ar2(i,j)     pInt[(i)*(d2) + (j)]  

For example, and using zero based indexing, lets see how it works out to locate the int in row 2 and col 1...

ar2(2, 1) = pInt[2 * 3 + 1]

or...

pInt[7]

So all we're doing is algebraically translating between two indexing systems. Here is a full C example where I'm using 3 rows and 4 columns, and it shows a C centric way of getting dynamic multi-dimensional arrays, and note within main we can use this syntax...

ar2(i, j)

instead of...

ar2[i][j]...

#include <cstdlib>
#include <cstdio>     // d1=1st dim; d2=2nd dim
#define  ar2(i,j)     pObj[(i)*(d2) + (j)]

int main(void)
{
 int* pObj=NULL;
 int d1=3, d2=4, iCtr=0;  // 3 rows; 4 cols

 pObj=(int*)malloc(d1*d2*sizeof(int)); // allocate 12 int objects
 if(pObj)
 {
    for(int i=0; i<d1; i++)        // if allocation successful
        for(int j=0; j<d2; j++)    // store nums 0 - 11 in
            ar2(i,j)=iCtr++;       // successive elements

    for(int i=0; i<d1; i++)
    {
        for(int j=0; j<d2; j++)
        {
            printf("ar2(%d,%d)=%d\t",i,j,ar2(i,j));//output array
        }
        printf("\n");
    }
    free(pObj);
 }
 getchar();

 return 0;
}

/*
ar2(0,0)=0      ar2(0,1)=1      ar2(0,2)=2      ar2(0,3)=3
ar2(1,0)=4      ar2(1,1)=5      ar2(1,2)=6      ar2(1,3)=7
ar2(2,0)=8      ar2(2,1)=9      ar2(2,2)=10     ar2(2,3)=11
*/

But we can do a better job of this in C++. We have Classes in C++ to encapsulate this, and on top of that we have templates where the encapsulation can work with any variable/object type (you need Strings for your app). Here is my CArray Class...

// CArray.h
// This entity is a templated class for creating dynamic
// multi-dimensional arrays of from one to four dimensions.
// It allows for a basic language type syntax for doing
// the above.  GNU C++ compilers implement dynamic array
// allocation but MS VC++ compilers don't.  Since I wanted
// to test compile with both I developed this class.

template <class datatype> class CArray  
{                                       
 public:                                
 CArray() : pObjs(0)                    
 {                                      
  this->iNumObjects=0;                  
  d1=d2=d3=d4=0;
 }


 CArray(int i) : pObjs(0) // One Dimensional Array Constructor
 {
  this->iNumObjects=i;
  this->pObjs=new(std::nothrow) datatype[this->iNumObjects]();
  d1=i, d2=0, d3=0, d4=0;
 }


 CArray(int i, int j) : pObjs(0) //Two Dim Array Constructor
 {
  this->iNumObjects=i*j;
  this->pObjs=new(std::nothrow) datatype[this->iNumObjects]();
  d1=i, d2=j, d3=0, d4=0;
 }


 CArray(int i, int j, int k) : pObjs(0) // 3 Dim Arr Constructor
 {
  this->iNumObjects=i*j*k;
  this->pObjs=new(std::nothrow) datatype[this->iNumObjects]();
  d1=i, d2=j, d3=k, d4=0;
 }


 CArray(int i, int j, int k, int l) : pObjs(0) //4 Dim Ar Constr
 {
  this->iNumObjects=i*j*k*l;
  this->pObjs=new(std::nothrow) datatype[this->iNumObjects]();
  d1=i, d2=j, d3=k, d4=l;
 }


 datatype& operator()(int i)       // One Dimensional Accessor
 {
  return pObjs[i];
 }


 datatype& operator()(int i, int j)   // Two Dimensional Accessor
 {
  return pObjs[i*d2 + j];
 }


 datatype& operator()(int i, int j, int k) //3 Dim Accessor
 {
  return pObjs[i*d2 + j + k*d1*d2];
 }


 datatype& operator()(int i, int j, int k, int l) // 4 dim Accsor
 {
  return pObjs[i*d2 + j + k*d1*d2 + l*d1*d2*d3];
 }


 bool blnMemoryIsGood()
 {
  return !!pObjs;
 }


 int UBound(unsigned int iDim)
 {
  if(iDim==1)
     return d1-1;
  if(iDim==2)
     return d2-1;
  if(iDim==3)
     return d3-1;
  if(iDim==4)
     return d4-1;
  else
     return 0;
 }


 void ClearMemory()   
 {                    
  memset(this->pObjs, 0, sizeof(datatype)*this->iNumObjects);
 }


 ~CArray()
 {
  if(this->pObjs)
     delete [] this->pObjs;
 }


 private:
 int       iNumObjects; 
 datatype* pObjs;       
 int       d1;          // Dimension #1
 int       d2;          // Dimension #2
 int       d3;          // Dimension #3
 int       d4;          // Dimension #4
};

So basically I've just provided all the piecies you need to create your app, but I'll further flush out the details by providing an example of use of the above class, and what we'll do is create a two dimensional array of std::string where we'll store the i, j coordinates within each string, i.e., the output run looks like this ...

(0,0) (0,1) (0,2) (0,3)
(1,0) (1,1) (1,2) (1,3)
(2,0) (2,1) (2,2) (2,3)

...for an array of three rows and four columns (for your appointment app each string array element will contain a parsed time, date, appointment description, etc.)...

#include <string>

template <class datatype> class CArray // This entity is a templated class for creating dynamic
{                                      // multi-dimensional arrays of from one to four dimensions.
 public:                               // It allows for a basic language type syntax for doing
 CArray() : pObjs(0)                   // the above.  GNU C++ compilers implement dynamic array
 {                                     // allocation but MS VC++ compilers don't.  Since I wanted
  this->iNumObjects=0;                 // to test compile with both I developed this class.
  d1=d2=d3=d4=0;
 }


 CArray(int i) : pObjs(0)       // One Dimensional Array Constructor
 {
  this->iNumObjects=i;
  this->pObjs=new(std::nothrow) datatype[this->iNumObjects]();
  d1=i, d2=0, d3=0, d4=0;
 }


 CArray(int i, int j) : pObjs(0)// Two Dimensional Array Constructor
 {
  this->iNumObjects=i*j;
  this->pObjs=new(std::nothrow) datatype[this->iNumObjects]();
  d1=i, d2=j, d3=0, d4=0;
 }


 CArray(int i, int j, int k) : pObjs(0)  // Three Dimensional Array Constructor
 {
  this->iNumObjects=i*j*k;
  this->pObjs=new(std::nothrow) datatype[this->iNumObjects]();
  d1=i, d2=j, d3=k, d4=0;
 }


 CArray(int i, int j, int k, int l) : pObjs(0) // Four Dimensional Array Constructor
 {
  this->iNumObjects=i*j*k*l;
  this->pObjs=new(std::nothrow) datatype[this->iNumObjects]();
  d1=i, d2=j, d3=k, d4=l;
 }


 datatype& operator()(int i)     // One Dimensional Accessor
 {
  return pObjs[i];
 }


 datatype& operator()(int i, int j)  // Two Dimensional Accessor
 {
  return pObjs[i*d2 + j];
 }


 datatype& operator()(int i, int j, int k)  // Three Dimensional Accessor
 {
  return pObjs[i*d2 + j + k*d1*d2];
 }


 datatype& operator()(int i, int j, int k, int l) // Four Dimensional Accessor
 {
  return pObjs[i*d2 + j + k*d1*d2 + l*d1*d2*d3];
 }


 bool blnMemoryIsGood()
 {
  return !!pObjs;
 }


 int UBound(unsigned int iDim)
 {
  if(iDim==1)
     return d1-1;
  if(iDim==2)
     return d2-1;
  if(iDim==3)
     return d3-1;
  if(iDim==4)
     return d4-1;
  else
     return 0;
 }


 void ClearMemory()   // Non-class types like ints, doubles, etc., need to be initialized
 {                    // to zero.  This must not be doe for class types.
  memset(this->pObjs, 0, sizeof(datatype)*this->iNumObjects);
 }


 ~CArray()
 {
  if(this->pObjs)
     delete [] this->pObjs;
 }


 private:
 int       iNumObjects; // We'll need this to zero memory for non-class types
 datatype* pObjs;       // pointer to the base memory allocation for array
 int       d1;          // Dimension #1
 int       d2;          // Dimension #2
 int       d3;          // Dimension #3
 int       d4;          // Dimension #4
};


template <typename t1> void PrintData(CArray<t1>& strAr)  // Shows syntax of passing
{                                                         // templated object
 for(int i=0; i<=strAr.UBound(1); i++)      //  Upper Bound = 3-1=2
 {
     for(int j=0; j<=strAr.UBound(2); j++)  //  Upper Bound = 4-1=3
         printf("%s\t",strAr(i,j).c_str());
     printf("\n");
 }
}


int main()
{
 CArray<std::string> ar2(3,4);
 char szBuffer[16];

 for(int i=0; i<=ar2.UBound(1); i++)
 {
     for(int j=0; j<=ar2.UBound(2); j++)
     {
         ar2(i,j)="(";
         sprintf(szBuffer,"%d",i);
         ar2(i,j)=ar2(i,j)+szBuffer+",";
         sprintf(szBuffer,"%d",j);
         ar2(i,j)=ar2(i,j)+szBuffer+")";
     }
 }
 PrintData(ar2);

 return 0;
}

#if 0

(0,0)   (0,1)   (0,2)   (0,3)
(1,0)   (1,1)   (1,2)   (1,3)
(2,0)   (2,1)   (2,2)   (2,3)

Process returned 0 (0x0)   execution time : 0.031 s
Press any key to continue.

#endif

Sorry about the formatting. I know it looks like something the cat drug in. This site doesn't allow long enough lines. Maybe someone who has an inside track with Dani could enlighten her to a couple facts...

1) We not in a DOS world anymore;
2) The tools folks use nowadays to write code allow more than 80 characters per line;
3) The screens on the desktops/laptops that content creators such as coders use keep getting wider in a left - right direction and shorter in an up - down direction. Therefore, optimal use of screen real estate requires longer lines than 80 characters;
4) Tell her to fix it!

Hopefully you can fix the mess when you copy it. Its just that I hate seeing my code turned into a mess.

Note further that the above CArray Class can be used for 1, 2, 3, or 4 dimensions. For the work I do in Forest Biometrics I need 4 dimensions, so I never added capabilities for more. Another language I use a lot is PowerBASIC, and that language allows up to 8 dimensions, and of course in that language the capability is built in - you don't have to add it or code it yourself like in C or C++!

to be continued....

Hello again CuriousGeorge. In this, my last major code post, is a complete example of starting with a data file such as this ...

9:00 AM; 1/15/2015; Dentist Appointment at 9:00 AM|
7:00 PM; 1/21/2015; Date with Cindy|
8:00 PM; 2/12/2015; Concert at Tom T Hall|
3:00 PM; 6/1/2015; Graduate From College|
9:00 AM; 6/2/2015; Start Six Figure coding job|

...named Data.dat, and parsing it into a 5 x 3 dynamic String array giving (iRow, iCol) access to the individual array elements. So to use this example copy that data to a text file and save it to your working directory as "Data.dat".

Now, here's the ringer. I don't use anything whatsoever from the C++ Standard Library. I wrote that off over a decade ago. I have my own classes for everything I do from database access to String Classes and everything in between. So those Parse(), ParseCount(), Trim(), etc., functions I previously posted were simply altered forms of code in C form from my String Class, which I'll attempt to post in its entirety.

What might have been better for me to do would be to just use the string class <string> from the C++ Std. Lib. with that Parse() code and CArray code I posted, but I'd have to work at that a bit and I have this code ready.

Here are some issues to keep in mind about this code. There is a #define in Strings.h at top named #define x64. That needs to be commented out if compiling for x86 (32 bit). Also, I never asked, but are you using Windows? I doubt if the code will run under *nix without modifications. I haven't had a *nix box for a couple years, so I can't provide compilable code for that at this point. This code really needs Win 2000, Win XP, Vista, Win 7. I haven't tested on Win 8. It should work in MS Visual Studio or GNU GCC. I use those all the time.

What the code does is attempt to open Data.dat and see how many lines are there. Then it allocates a CArray object large enough to hold howsoever many lines it finds in the file and three columns. Then it parses each line with String::Parse(). It might be interesting for you to compare the C version I previously posted with the String Class version. Finally, the program dumps the array to the console window, and PrintData() shows how to pass a templated array by reference to an external function.

Oh! In Strings.cpp is a define NEW_GCC. That needs to be active if using a 4.7, 4.8 or 4.9 series GCC compiler. Older GCC compilers need it commented out, as does MS VC. Also, the UNICODE and _UNICODE defines control whether ansi or wide character is being used. It should be set up OK the way I post it. MS VC may report piles or warnings but that's OK. If there are errors let me know. I'm sure I can fix them. Here is Strings.h...

Wow man that is incredible!! Thank you!! If you don't mind I am still working on my version, and what you've given me is a very helpful guide. I think the biggest problem I was having was I attempted to put each field in to its appropriate datatype. That turned out to be a much bigger problem to tackle all at once than I had thought.

One reason I deemed this necessary was I was going to use the time library ctime.h to check the date and stuff after the file had been read and put in to memory. Now I have decided it may be better to simply have this class read the file in to memory. Then it would put the data in to programmer supplied arrays or even data pointers. That would be done outside of the class or in a member function for accessing the data fields.

In my original class there was a private "record number" index int, which was used to reference each record. It was supposed to go right to that records location in memory and then place that data in to a string passed to the foo.

A multidimensional array with a row dedicated to each record never crossed my mind!

//Strings.h
#ifndef Strings_h
#define Strings_h
#define EXPANSION_FACTOR      2
#define MINIMUM_ALLOCATION   16
#define  x64

class String
{
 public:
 String();                                                                         //Uninitialized Constructor
 String(const TCHAR);                                                              //Constructor Initializes With A TCHAR.
 String(const TCHAR*);                                                             //Constructor Initializes String With TCHAR*
 String(const String&);                                                            //Constructor Initializes String With Another String (Copy Constructor)
 String(const int, bool);                                                          //Constructor Creates String With User Specified Capacity and optionally nulls out
 String(const int, const TCHAR);                                                   //Constructor initializes String with int # of TCHARs
 String(int);                                                                      //Constructor initializes String with int converted to String
 #ifdef x64
 String(size_t);                                                                   //Constructor initializes String with size_t object converted to String
 String(SSIZE_T);
 #endif
 String(unsigned int);                                                             //Constructor initializes String with unsigned int converted to String
 String(double);                                                                   //Constructor initializes String with double converted to String
 String& operator=(const TCHAR);                                                   //Assign A TCHAR To A String
 String& operator=(const TCHAR*);                                                  //Assign A Null Terminated TCHARacter Array To A String
 String& operator=(const String&);                                                 //Assigns Another String To this One
 String& operator=(int iNum);                                                      //Assigns an unsigned int to a String
 #ifdef x64
 String& operator=(size_t iNum);                                                   //Assigns a size_t integral object to a String
 String& operator=(SSIZE_T iNum);                                                  //Assigns a SSIZE_T integral object to a String
 #endif
 String& operator=(unsigned int iNum);                                             //Assigns an unsigned int to a String
 String& operator=(double dblNum);                                                 //Assign a double to a String
 String operator+(const TCHAR);                                                    //For adding TCHAR to String
 String operator+(const TCHAR*);                                                   //Adds a TCHAR* to this
 String operator+(String&);                                                        //Adds another String to this
 String& operator+=(const TCHAR ch);                                               //Add TCHAR to this
 String& operator+=(const String&);                                                //Adds a String to this and assigns it to left of equal sign
 String& operator+=(const TCHAR*);                                                 //Adds a TCHAR*to this and assigns it to left of equal sign
 bool operator==(String&);                                                         //Compares Strings For Case Sensitive Equality
 bool operator==(const TCHAR*);                                                    //Compares String Against TCHAR* For Case Sensitive Equality
 bool operator!=(TCHAR*);                                                          //Compares String Against TCHAR* For Case Sensitive Inequality
 TCHAR operator()(size_t iZeroBasedOffset);                                        //Returns TCHAR At Zero Based Offset In this
 String Allocate(size_t);                                                          //Allocates a String with a specified buffer size
 String& Make(const TCHAR, size_t);                                                //Returns reference to this with iCount ch TCHARs in it
 String Left(size_t);                                                              //Returns String of iNum Left Most TTCHARs of this
 String Right(size_t);                                                             //Returns String of iNum Right Most TTCHARs of this
 String Mid(size_t, size_t);                                                       //Returns String consisting of number of TTCHARs from some offset
 String Replace(TCHAR*, TCHAR*);                                                   //Returns String with 1st TCHAR* parameter replaced with 2nd TCHAR* parameter
 String Remove(TCHAR*);                                                            //Returns A String With All The TCHARs In A TCHAR* Removed (Individual TCHAR removal)
 String Remove(const TCHAR*, bool);                                                //Returns a String with 1st parameter removed.  2nd is bool for case sensitivity.
 int InStr(const TCHAR*, bool, bool);                                              //Returns one based case sensitive/insensitive offset of a particular TCHAR pStr in a String, starting from left or right (left=true)
 int InStr(const String&, bool, bool);                                             //Returns one based case sensitive/insensitive offset of where a particular String is in another String, starting from left or right (left=true)
 int ParseCount(const TCHAR);                                                      //Returns count of Strings delimited by a TTCHAR passed as a parameter
 void Parse(String*, TCHAR);                                                       //Returns array of Strings in first parameter as delimited by 2nd TTCHAR delimiter
 void Format(double dblNumber, size_t iFldLen, size_t iDecPlaces);                 //Returns this by converting double to String with commas every three digits, in iFDldWidth and iDecimal places
 void Format(double dblNum, int iFldWth, int iDecimal, bool blnRightJustified);    //Modifies this By Converting double to String With int iFieldWidth, int iDecimal Places, and bool blnRightJustified Formatting
 void Format(size_t iNum, int iFldLen, TCHAR cSeperator, bool blnRightJustified);  //Modifies this By Converting size_t To cSeperator Delimited String R/L Justified Within iFldLen String.  iFldLen Can Be Zero And Whole String Is Output In Field Large Enough To Contain It
 void Format(SSIZE_T iNum, int iFldLen, TCHAR cSeperator, bool blnRightJustified); //Modifies this By Converting SSIZE_T To cSeperator Delimited String R/L Justified Within iFldLen String.  iFldLen Can Be Zero And String Is Output
 void Money(double dblNum, size_t iFldLen, size_t iOffsetDollarSign);              //Inserts commas every 3 digits into iFldLen String and inserts '$' sign at one based iOffsetDollarSign
 void Money(double dblNum, size_t iFldLen, bool blnDollarSign);                    //Inserts commas every 3 digits into iFldLen String and inserts '$' sign right in front of leftmost digit
 void SetChar(size_t, TCHAR);                                                      //Sets TCHAR at zero based offset in this
 void LTrim();                                                                     //Returns String with leading spaces/tabs removed
 void RTrim();                                                                     //Returns String with spaces/tabs removed from end
 void Trim();                                                                      //Returns String with both leading and trailing whitespace removed
 int iVal();                                                                       //Returns integral value of String
 int Len();                                                                        //Returns Length Of String Controlled By this
 int Capacity();                                                                   //Returns Maximum Permissable TCHARacter Count (One Less Than Allocation).
 TCHAR* lpStr();                                                                   //Returns TCHAR* To String
 void Print(FILE*, bool);                                                          //Outputs String To Console With Or Without CrLf.
 void Print(FILE*, TCHAR*, bool);                                                  //Outputs String To Console With Or Without CrLf.
 void Print(TCHAR*, bool);
 void Print(bool);
 friend String operator+(TCHAR*, String&);
 ~String();                                                                        //String Destructor

 private:
 TCHAR* lpBuffer;
 size_t iLen;
 size_t iCapacity;
};

String operator+(TCHAR* lhs, String& rhs);
String Str(double dblNum);
#ifdef x64
String Str(int iNum);
String Str(unsigned int iNum);
String Str(SSIZE_T iNum);
String Str(size_t iNum);
#else
String Str(int iNum);
String Str(unsigned int iNum);
#endif
#endif  //#if !defined(STRINGS_H)

Its the formatting thing again. I hope that can be copied out in compilable readable form.

// Strings.cpp
#ifndef   UNICODE
   #define   UNICODE
#endif
#ifndef   _UNICODE
   #define   _UNICODE
#endif
#define   NEW_GCC
#include  <windows.h>
#include  <stdlib.h>
#include  <cstdio>
#include  <tchar.h>
#include  <math.h>
#include  <string.h>
#include  "Strings.h"
#ifdef    MyDebug
extern    FILE* fp;
#endif


String operator+(TCHAR* lhs, String& rhs)  //global function
{
 String sr=lhs;
 sr=sr+rhs;

 return sr;
}


String Str(double dblNum)
{
 return dblNum;
}


#ifdef x64
   String Str(int iNum)
   {
    return iNum;
   }


   String Str(unsigned int iNum)
   {
    return iNum;
   }

   String Str(SSIZE_T iNum)
   {
    return iNum;
   }


   String Str(size_t iNum)
   {
    return iNum;
   }
#else
   String Str(int iNum)
   {
    return iNum;
   }


   String Str(unsigned int iNum)
   {
    return iNum;
   }
#endif


String::String()
{
 lpBuffer=new TCHAR[MINIMUM_ALLOCATION];
 lpBuffer[0]=_T('\0');
 this->iCapacity=MINIMUM_ALLOCATION-1;
 this->iLen=0;
}


String::String(const TCHAR ch)  //Constructor: Initializes
{                               //with TCHAR
 this->iLen=1;
 int iNewSize=MINIMUM_ALLOCATION;
 this->lpBuffer=new TCHAR[iNewSize];
 this->iCapacity=iNewSize-1;
 this->lpBuffer[0]=ch, this->lpBuffer[1]=_T('\0');
}


String::String(const TCHAR* pStr)  //Constructor: Initializes
{                                  //with TCHAR*
 this->iLen=(int)_tcslen(pStr);
 int iNewSize=(this->iLen/16+1)*16;
 this->lpBuffer=new TCHAR[iNewSize];
 this->iCapacity=iNewSize-1;
 _tcscpy(lpBuffer,pStr);
}


String::String(const String& s)  //Constructor Initializes With
{                                //Another String, i.e., Copy Constructor
 int iNewSize=(s.iLen/16+1)*16;
 this->iLen=s.iLen;
 this->lpBuffer=new TCHAR[iNewSize];
 this->iCapacity=iNewSize-1;
 _tcscpy(this->lpBuffer,s.lpBuffer);
}


String::String(const int iSize, bool blnFillNulls)  //Constructor Creates String With Custom Sized
{                                                   //Buffer (rounded up to paragraph boundary)
 int iNewSize=(iSize/16+1)*16;
 this->lpBuffer=new TCHAR[iNewSize];
 this->iCapacity=iNewSize-1;
 this->iLen=0;
 this->lpBuffer[0]=_T('\0');
 if(blnFillNulls)
 {
    for(size_t i=0; i<this->iCapacity; i++)
        this->lpBuffer[i]=0;
 }
}


String::String(int iCount, const TCHAR ch)
{
 int iNewSize=(iCount/16+1)*16;
 this->lpBuffer=new TCHAR[iNewSize];
 this->iCapacity=iNewSize-1;
 for(int i=0; i<iCount; i++)
     this->lpBuffer[i]=ch;
 this->lpBuffer[iCount]=_T('\0');
 this->iLen=iCount;
}


String::String(int iNum)
{
 this->lpBuffer=new TCHAR[16];
 this->iCapacity=15;
 this->iLen=_stprintf(this->lpBuffer,_T("%d"),iNum);
}


#ifdef x64
String::String(size_t iNum)
{
 this->lpBuffer=new TCHAR[32];
 this->iCapacity=31;
 this->iLen=_stprintf(this->lpBuffer,_T("%llu"),iNum);
}


String::String(SSIZE_T iNum)
{
 this->lpBuffer=new TCHAR[32];
 this->iCapacity=31;
 this->iLen=_stprintf(this->lpBuffer,_T("%lld"),iNum);
}
#endif



String::String(unsigned int iNum)
{
 this->lpBuffer=new TCHAR[16];
 this->iCapacity=15;
 this->iLen=_stprintf(this->lpBuffer,_T("%u"),iNum);
}


String::String(double dblNum)
{
 this->lpBuffer=new TCHAR[32];
 this->iCapacity=31;
 this->iLen=_stprintf(this->lpBuffer,_T("%10.14f"),dblNum);
}


String& String::operator=(double dblNum)
{
 if(this->iCapacity<32)
 {
    delete [] this->lpBuffer;
    lpBuffer=new TCHAR[32];
    this->iCapacity=31;
 }
 this->iLen=_stprintf(this->lpBuffer,_T("%f"),dblNum); // %20.10f

 return *this;
}


TCHAR String::operator()(size_t iZeroBasedOffset)
{
 if(iZeroBasedOffset<=this->iCapacity)
    return this->lpBuffer[iZeroBasedOffset];
 else
    return 0;
}


void String::Format(size_t iNumber, int iFldLen, TCHAR cSeperator, bool blnRightJustified)
{
 TCHAR szBuf1[24];
 TCHAR szBuf2[24];
 size_t iDigit=0;
 size_t iLen=0;
 size_t iCtr=1;
 size_t j=0;

 memset(szBuf1,0,24*sizeof(TCHAR));
 memset(szBuf2,0,24*sizeof(TCHAR));
 #ifdef x64
    _stprintf(szBuf1,_T("%llu"),iNumber);
 #else
    _stprintf(szBuf1,_T("%u"),iNumber);
 #endif
 _tcsrev(szBuf1);
 iLen=_tcslen(szBuf1);
 for(size_t i=0; i<iLen; i++)
 {
     if(iCtr==3)
     {
        iDigit++;
        szBuf2[j]=szBuf1[i];
        if(iDigit<iLen)
        {
           j++;
           szBuf2[j]=cSeperator;
        }
        j++, iCtr=1;
     }
     else
     {
        iDigit++;
        szBuf2[j]=szBuf1[i];
        j++, iCtr++;
     }
 }
 _tcsrev(szBuf2);                    // Done With Creating String With Commas
 memset(szBuf1,0,24*sizeof(TCHAR));  // Reuse szBuf1
 _tcscpy(szBuf1,szBuf2);             // szBuf1 Now Contains Comma Delimited Positive Value, but No Field Paddings Or R/L Justification
 size_t iRequiredBytes;              // Find out which of two is larger - length of string necessary
 iLen=_tcslen(szBuf1);               // or iFldLen
 if(iFldLen>(int)iLen)
    iRequiredBytes=iFldLen;
 else
    iRequiredBytes=iLen;
 if(iRequiredBytes>(size_t)this->iCapacity)
 {
    delete [] this->lpBuffer;
    int iNewSize=(iRequiredBytes*EXPANSION_FACTOR/16+1)*16;
    this->lpBuffer=new TCHAR[iNewSize];
    this->iCapacity=iNewSize-1;
 }
 memset(this->lpBuffer,0,this->iCapacity*sizeof(TCHAR)); // Now, at this point, szBuf1 Is Holding reformatted value, whether plus or minus, But no leading or trailing spaces.  And this->lpBuffer is large enough.
 SSIZE_T iDifference=iFldLen-iLen;                       // space = 32;  $ = 36;  , = 44
 if(blnRightJustified)
 {
    if(iDifference > 0)
    {
       for(size_t i=0; i<(size_t)iDifference; i++)
           this->lpBuffer[i]=_T(' ');  // 32
    }
    _tcscat(this->lpBuffer,szBuf1);
 }
 else
 {
    _tcscpy(this->lpBuffer,szBuf1);
    if(iDifference>0)
    {
       for(size_t i=iLen; i<iDifference+iLen; i++)
           this->lpBuffer[i]=_T(' ');  // 32
    }
 }
 this->iLen=_tcslen(this->lpBuffer);
}


void String::Format(SSIZE_T iNumber, int iFldLen, TCHAR cSeperator, bool blnRightJustified)
{
 bool blnPositive;
 TCHAR szBuf1[24];
 TCHAR szBuf2[24];
 size_t iDigit=0;
 size_t iLen=0;
 size_t iCtr=1;
 size_t j=0;

 memset(szBuf1,0,24*sizeof(TCHAR));
 memset(szBuf2,0,24*sizeof(TCHAR));
 if(iNumber<0)
    blnPositive=false;
 else
    blnPositive=true;
 #ifdef x64
    iNumber=_abs64(iNumber);
 #else
    iNumber=abs(iNumber);
 #endif
 #ifdef x64
    _stprintf(szBuf1,_T("%llu"),(size_t)iNumber);
 #else
    _stprintf(szBuf1,_T("%u"),(size_t)iNumber);
 #endif
 _tcsrev(szBuf1);
 iLen=_tcslen(szBuf1);
 for(size_t i=0; i<iLen; i++)
 {
     if(iCtr==3)
     {
        iDigit++;
        szBuf2[j]=szBuf1[i];
        if(iDigit<iLen)
        {
           j++;
           szBuf2[j]=cSeperator;
        }
        j++, iCtr=1;
     }
     else
     {
        iDigit++;
        szBuf2[j]=szBuf1[i];
        j++, iCtr++;
     }
 }
 _tcsrev(szBuf2); // Done With Creating String With Commas

 memset(szBuf1,0,24*sizeof(TCHAR));  // Reuse szBuf1
 if(blnPositive)
    _tcscpy(szBuf1,szBuf2);
 else
 {
    szBuf1[0]=_T('-');
    _tcscat(szBuf1,szBuf2);
 }   // szBuf1 Now Contains Comma Delimited Positive Or Negative Value, but No Field Paddings Or R/L Justification

 size_t iRequiredBytes;         // Find out which of two is larger - length of string necessary
 iLen=_tcslen(szBuf1);          // or iFldLen
 if(iFldLen>(int)iLen)
    iRequiredBytes=iFldLen;
 else
    iRequiredBytes=iLen;

 if(iRequiredBytes>(size_t)this->iCapacity)
 {
    delete [] this->lpBuffer;
    int iNewSize=(iRequiredBytes*EXPANSION_FACTOR/16+1)*16;
    this->lpBuffer=new TCHAR[iNewSize];
    this->iCapacity=iNewSize-1;
 }
 memset(this->lpBuffer,0,this->iCapacity*sizeof(TCHAR));   // Now, at this point, szBuf1 Is Holding reformatted value, whether plus or minus, But no leading or trailing spaces.  And this->lpBuffer is large enough.
 SSIZE_T iDifference=iFldLen-iLen; // space = 32;  $ = 36;  , = 44
 if(blnRightJustified)
 {
    if(iDifference > 0)
    {
       for(size_t i=0; i<(size_t)iDifference; i++)
           this->lpBuffer[i]=_T(' ');  // 32
    }
    _tcscat(this->lpBuffer,szBuf1);
 }
 else
 {
    _tcscpy(this->lpBuffer,szBuf1);
    if(iDifference>0)
    {
       for(size_t i=iLen; i<iDifference+iLen; i++)
           this->lpBuffer[i]=_T(' ');  // 32
    }
 }
 this->iLen=_tcslen(this->lpBuffer);
}


void String::Format(double dblNum, int iFldWth, int iDecimal, bool blnRightJustified)  // This one doesn't insert commas
{                                                                                      // every three digits
 if(iFldWth>0 && iFldWth<32)
 {
    if(iDecimal>=0 && iDecimal<19)
    {
       if(this->iCapacity<32)
       {
          delete [] this->lpBuffer;
          lpBuffer=new TCHAR[32];
          this->iCapacity=31;
       }
       String sFormat;
       if(blnRightJustified)
          sFormat=(TCHAR*)_T("%");
       else
          sFormat=(TCHAR*)_T("%-");
       String strFldWth(iFldWth);
       String strDecimalPoints(iDecimal);
       sFormat=sFormat+strFldWth+(TCHAR*)_T(".")+strDecimalPoints+(TCHAR*)_T("f");
       this->iLen=_stprintf(this->lpBuffer,sFormat.lpStr(),dblNum);  // either of these work
    }
 }
}


void String::Format(double dblNumber, size_t iFldLen, size_t iDecPlaces)
{
 TCHAR szNumber[24], szFormat[8], szDecPlaces[4], szDecimal[16];
 size_t iLen=0,iDot=0;
 SSIZE_T iNumber=0;
 SSIZE_T iDiff=0;
 String strTmp;

 memset(szNumber,0,24*sizeof(TCHAR));
 memset(szFormat,0,8*sizeof(TCHAR));
 memset(szDecPlaces,0,4*sizeof(TCHAR));
 memset(szDecimal,0,16*sizeof(TCHAR));
 #ifdef x64
 _stprintf(szDecPlaces,_T("%llu"),iDecPlaces);
 #else
 _stprintf(szDecPlaces,_T("%u"),iDecPlaces);
 #endif
 _tcscpy(szFormat,(TCHAR*)_T("%0."));
 _tcscat(szFormat,szDecPlaces);
 _tcscat(szFormat,(TCHAR*)_T("f"));
 iLen=_stprintf(szNumber,szFormat,dblNumber);
 if(iDecPlaces)  // At Least 1 Digit To Right Of Decimal Point
 {
    iNumber=(SSIZE_T)dblNumber;
    for(size_t i=0; i<iLen; i++)
    {
        if(szNumber[i]==_T('.'))
        {
           iDot=i;
           break;
        }
    }
    _tcsncpy(szDecimal,szNumber+iDot+1,iDecPlaces);
    if(dblNumber>=0)
       strTmp.Format(iNumber,0,_T(','),true);
    else
    {
       if(iNumber==0)
       {
          strTmp.Format(iNumber,0,_T(','),true);
          strTmp=(TCHAR*)_T("-")+strTmp;
       }
       else
          strTmp.Format(iNumber,0,_T(','),true);
    }
    strTmp=strTmp+(TCHAR*)_T(".");
    strTmp=strTmp+szDecimal;
    iLen=strTmp.Len();
    if(this->iCapacity<iLen || this->iCapacity<iFldLen)  // Code To Reallocate If this->lpBuffer Isn't Big Enough
    {
       size_t iNewSize,iNewLen;
       delete [] this->lpBuffer;
       iNewLen=iLen+iFldLen;
       iNewSize = (iNewLen/16 + 1) * 16;
       this->lpBuffer=new TCHAR[iNewSize];
       this->iCapacity=iNewSize-1;
    }
    iDiff=iFldLen-iLen;
    if(iDiff>0)
    {
       for(size_t i=0; i<(size_t)iDiff; i++)
           this->lpBuffer[i]=_T(' ');
       this->lpBuffer[iDiff]=NULL;
    }
    else
       this->lpBuffer[0]=NULL;
    _tcscat(this->lpBuffer,strTmp.lpStr());
    this->iLen=_tcslen(this->lpBuffer);
 }
 else // No Decimal Places
 {
    #ifdef x64
       strTmp.Format((SSIZE_T)_ttoi64(szNumber),0,_T(','),true);
    #else
       strTmp.Format((SSIZE_T)_ttoi(szNumber),0,_T(','),true);
    #endif
    iLen=strTmp.Len();
    if(this->iCapacity<iLen || this->iCapacity<iFldLen)  // Code To Reallocate If this->lpBuffer Isn't Big Enough
    {
       size_t iNewSize,iNewLen;
       delete [] this->lpBuffer;
       iNewLen=iLen+iFldLen;
       iNewSize = (iNewLen/16 + 1) * 16;
       this->lpBuffer=new TCHAR[iNewSize];
       this->iCapacity=iNewSize-1;
    }
    iDiff=iFldLen-iLen;
    if(iDiff>0)
    {
       for(size_t i=0; i<(size_t)iDiff; i++)
           this->lpBuffer[i]=_T(' ');
       this->lpBuffer[iDiff]=NULL;
    }
    else
       this->lpBuffer[0]=NULL;
    _tcscat(this->lpBuffer,strTmp.lpStr());
 }
 this->iLen=_tcslen(this->lpBuffer);
}


void String::SetChar(size_t iOffset, TCHAR ch)   //zero based!
{
 if(iOffset<this->iCapacity)
 {
    this->lpBuffer[iOffset]=ch;
    if(ch==_T('\0'))
    {
       if(iOffset<this->iLen || this->iLen==0)
          this->iLen=iOffset;
    }
 }
}


String& String::operator=(const TCHAR ch)
{
 this->lpBuffer[0]=ch, this->lpBuffer[1]=_T('\0');
 this->iLen=1;
 return *this;
}


String& String::operator=(const TCHAR* pStr)
{
 size_t iNewLen=_tcslen(pStr);
 if(iNewLen>this->iCapacity)
 {
    delete [] this->lpBuffer;
    int iNewSize=(iNewLen*EXPANSION_FACTOR/16+1)*16;
    this->lpBuffer=new TCHAR[iNewSize];
    this->iCapacity=iNewSize-1;
 }
 _tcscpy(this->lpBuffer,pStr);
 this->iLen=iNewLen;

 return *this;
}


String& String::operator=(const String& strAnother)
{
 if(this==&strAnother)
    return *this;
 if(strAnother.iLen>this->iCapacity)
 {
    delete [] this->lpBuffer;
    int iNewSize=(strAnother.iLen*EXPANSION_FACTOR/16+1)*16;
    this->lpBuffer=new TCHAR[iNewSize];
    this->iCapacity=iNewSize-1;
 }
 _tcscpy(this->lpBuffer,strAnother.lpBuffer);
 this->iLen=strAnother.iLen;

 return *this;
}


String& String::operator=(int iNum)
{
 if(this->iCapacity>=15)
    this->iLen=_stprintf(this->lpBuffer,_T("%d"),iNum);
 else
 {
    delete [] this->lpBuffer;
    this->lpBuffer=new TCHAR[16];
    this->iCapacity=15;
    this->iLen=_stprintf(this->lpBuffer,_T("%d"),iNum);
 }

 return *this;
}


#ifdef x64
String& String::operator=(size_t iNum)
{
 if(this->iCapacity>=32)
    this->iLen=_stprintf(this->lpBuffer,_T("%llu"),iNum);
 else
 {
    delete [] this->lpBuffer;
    this->lpBuffer=new TCHAR[32];
    this->iCapacity=31;
    this->iLen=_stprintf(this->lpBuffer,_T("%llu"),iNum);
 }

 return *this;
}


String& String::operator=(SSIZE_T iNum)
{
 if(this->iCapacity>=32)
    this->iLen=_stprintf(this->lpBuffer,_T("%lld"),iNum);
 else
 {
    delete [] this->lpBuffer;
    this->lpBuffer=new TCHAR[32];
    this->iCapacity=31;
    this->iLen=_stprintf(this->lpBuffer,_T("%lld"),iNum);
 }

 return *this;
}
#endif


String& String::operator=(unsigned int iNum)
{
  if(this->iCapacity>=15)
    this->iLen=_stprintf(this->lpBuffer,_T("%d"),iNum);
 else
 {
    delete [] this->lpBuffer;
    this->lpBuffer=new TCHAR[16];
    this->iCapacity=15;
    this->iLen=_stprintf(this->lpBuffer,_T("%d"),iNum);
 }

 return *this;
}


String String::operator+(const TCHAR ch)
{
 int iNewLen=this->iLen+1;

 String s(iNewLen,false);
 _tcscpy(s.lpBuffer,this->lpBuffer);
 s.lpBuffer[iNewLen-1]=ch;
 s.lpBuffer[iNewLen]=_T('\0');
 s.iLen=iNewLen;

 return s;
}


String String::operator+(const TCHAR* pStr)
{
 int iNewLen=_tcslen(pStr)+this->iLen;
 String s(iNewLen,false);
 _tcscpy(s.lpBuffer,this->lpBuffer);
 _tcscat(s.lpBuffer,pStr);
 s.iLen=iNewLen;

 return s;
}


String String::operator+(String& strRef)
{
 int iNewLen=strRef.iLen+this->iLen;
 String s(iNewLen,false);
 _tcscpy(s.lpBuffer,this->lpBuffer);
 _tcscat(s.lpBuffer,strRef.lpBuffer);
 s.iLen=iNewLen;

 return s;
}


String& String::operator+=(const TCHAR ch)
{
 size_t iTot=this->iLen+1;
 if(iTot>this->iCapacity)
 {
    int iNewSize=(iTot*EXPANSION_FACTOR/16+1)*16;
    TCHAR* pNew=new TCHAR[iNewSize];
    _tcscpy(pNew,this->lpBuffer);
    delete [] this->lpBuffer;
    this->lpBuffer=pNew;
    this->lpBuffer[iTot-1]=ch;
    this->lpBuffer[iTot]=_T('\0');
    this->iCapacity=iNewSize-1;
    this->iLen=iTot;
 }
 else
 {
    this->lpBuffer[iTot-1]=ch;
    this->lpBuffer[iTot]=_T('\0');
    this->iLen=iTot;
 }
 return *this;
}


String& String::operator+=(const TCHAR* pStr)
{
 size_t iStrlen=_tcslen(pStr);
 size_t iTot=iStrlen+this->iLen;
 if(iTot>this->iCapacity)
 {
    int iNewSize=(iTot*EXPANSION_FACTOR/16+1)*16;
    TCHAR* pNew=new TCHAR[iNewSize];
    _tcscpy(pNew,this->lpBuffer);
    delete [] this->lpBuffer;
    this->lpBuffer=pNew;
    _tcscat(pNew,pStr);
    this->iCapacity=iNewSize-1;
    this->iLen=iTot;
 }
 else
 {
    _tcscat(this->lpBuffer,pStr);
    this->iLen=iTot;
 }
 return *this;
}


String& String::operator+=(const String& strRef)
{
 size_t iTot=strRef.iLen+this->iLen;
 if(iTot>this->iCapacity)
 {
    int iNewSize=(iTot*EXPANSION_FACTOR/16+1)*16;
    TCHAR* pNew=new TCHAR[iNewSize];
    _tcscpy(pNew,this->lpBuffer);
    delete [] this->lpBuffer;
    this->lpBuffer=pNew;
    _tcscat(pNew,strRef.lpBuffer);
    this->iCapacity=iNewSize-1;
    this->iLen=iTot;
 }
 else
 {
    _tcscat(this->lpBuffer,strRef.lpBuffer);
    this->iLen=iTot;
 }
 return *this;
}


bool String::operator==(String& strRef)
{
 if(_tcscmp(this->lpStr(),strRef.lpStr())==0)
    return true;
 else
    return false;
}


bool String::operator==(const TCHAR* pStr)
{
 if(_tcscmp(this->lpStr(),pStr)==0)
    return true;
 else
    return false;
}


bool String::operator!=(TCHAR* pStr)
{
 if(_tcscmp(this->lpStr(),(TCHAR*)pStr)==0)
    return false;
 else
    return true;
}


String String::Allocate(size_t iCount)
{
 if(iCount>this->iCapacity)
 {
    delete [] lpBuffer;
    size_t iNewSize=(iCount*EXPANSION_FACTOR/16+1)*16;
    this->lpBuffer=new TCHAR[iNewSize];
    this->iCapacity=iNewSize-1;
 }
 this->lpBuffer[0]=_T('\0');
 this->iLen=0;

 return *this;
}


String& String::Make(const TCHAR ch, size_t iCount)    //Creates (Makes) a String with iCount TCHARs
{
 if(iCount>this->iCapacity)
 {
    delete [] lpBuffer;
    size_t iNewSize=(iCount*EXPANSION_FACTOR/16+1)*16;
    this->lpBuffer=new TCHAR[iNewSize];
    this->iCapacity=iNewSize-1;
 }
 for(size_t i=0; i<iCount; i++)
     this->lpBuffer[i]=ch;
 this->lpBuffer[iCount]=0;
 this->iLen=iCount;
 return *this;
}


String String::Left(size_t iNum)   //  strncpy = _tcsncpy
{
 if(iNum<this->iLen)
 {
    size_t iNewSize=(iNum*EXPANSION_FACTOR/16+1)*16;
    String sr(iNewSize,false);
    _tcsncpy(sr.lpBuffer,this->lpBuffer,iNum);
    sr.lpBuffer[iNum]=0;
    sr.iLen=iNum;
    return sr;
 }
 else
 {
    String sr=*this;
    sr.iLen=this->iLen;
    return sr;
 }
}


String String::Right(size_t iNum)  //Returns Right$(strMain,iNum)
{
 if(iNum<this->iLen)
 {
    size_t iNewSize=(iNum*EXPANSION_FACTOR/16+1)*16;
    String sr(iNewSize,false);
    _tcsncpy(sr.lpBuffer,this->lpBuffer+this->iLen-iNum,iNum);
    sr.lpBuffer[iNum]=_T('\0');
    sr.iLen=iNum;
    return sr;
 }
 else
 {
    String sr=*this;
    sr.iLen=this->iLen;
    return sr;
 }
}


String String::Mid(size_t iStart, size_t iCount)
{
 if(iStart<1)
 {
    String sr;
    return sr;
 }
 if(iCount+iStart>this->iLen)
    iCount=this->iLen-iStart+1;
 String sr(iCount,false);
 _tcsncpy(sr.lpBuffer,this->lpBuffer+iStart-1,iCount);
 sr.lpBuffer[iCount]=_T('\0');
 sr.iLen=iCount;

 return sr;
}


String String::Replace(TCHAR* pMatch, TCHAR* pNew)  //strncmp = _tcsncmp
{
 size_t i,iLenMatch,iLenNew,iCountMatches,iExtra,iExtraLengthNeeded,iAllocation,iCtr;
 iLenMatch=_tcslen(pMatch);
 iCountMatches=0, iAllocation=0, iCtr=0;
 iLenNew=_tcslen(pNew);
 if(iLenNew==0)
 {
    String sr=this->Remove(pMatch,true); //return
    return sr;
 }
 else
 {
    iExtra=iLenNew-iLenMatch;
    for(i=0; i<this->iLen; i++)
    {
        if(_tcsncmp(lpBuffer+i,pMatch,iLenMatch)==0)
           iCountMatches++;  //Count how many match strings
    }
    iExtraLengthNeeded=iCountMatches*iExtra;
    iAllocation=this->iLen+iExtraLengthNeeded;
    String sr(iAllocation,false);
    for(i=0; i<this->iLen; i++)
    {
        if(_tcsncmp(this->lpBuffer+i,pMatch,iLenMatch)==0)
        {
           _tcscpy(sr.lpBuffer+iCtr,pNew);
           iCtr+=iLenNew;
           i+=iLenMatch-1;
        }
        else
        {
           sr.lpBuffer[iCtr]=this->lpBuffer[i];
           iCtr++;
        }
        sr.lpBuffer[iCtr]=_T('\0');
    }
    sr.iLen=iCtr;
    return sr;
 }
}


String String::Remove(TCHAR* pStr)
{
 unsigned int i,j,iStrLen,iParamLen;
 TCHAR *pThis, *pThat, *p;
 bool blnFoundBadTCHAR;

 iStrLen=this->iLen;               //The length of this
 String sr((int)iStrLen,false);    //Create new String big enough to contain original String (this)
 iParamLen=_tcslen(pStr);          //Get length of parameter (pStr) which contains TCHARs to be removed
 pThis=this->lpBuffer;
 p=sr.lpStr();
 for(i=0; i<iStrLen; i++)
 {
     pThat=pStr;
     blnFoundBadTCHAR=false;
     for(j=0; j<iParamLen; j++)
     {
         if(*pThis==*pThat)
         {
            blnFoundBadTCHAR=true;
            break;
         }
         pThat++;
     }
     if(!blnFoundBadTCHAR)
     {
        *p=*pThis;
         p++;
        *p=_T('\0');
     }
     pThis++;
 }
 sr.iLen=_tcslen(sr.lpStr());

 return sr;
}


String String::Remove(const TCHAR* pMatch, bool blnCaseSensitive)
{
 size_t i,iCountMatches=0,iCtr=0;

 size_t iLenMatch=_tcslen(pMatch);
 for(i=0; i<this->iLen; i++)
 {
     if(blnCaseSensitive)
     {
        if(_tcsncmp(lpBuffer+i,pMatch,iLenMatch)==0)  //_tcsncmp
           iCountMatches++;
     }
     else
     {
        if(_tcsnicmp(lpBuffer+i,pMatch,iLenMatch)==0) //__tcsnicmp
           iCountMatches++;
     }
 }
 int iAllocation=this->iLen-(iCountMatches*iLenMatch);
 String sr(iAllocation,false);
 for(i=0; i<this->iLen; i++)
 {
     if(blnCaseSensitive)
     {
        if(_tcsncmp(this->lpBuffer+i,pMatch,iLenMatch)==0)
           i+=iLenMatch-1;
        else
        {
           sr.lpBuffer[iCtr]=this->lpBuffer[i];
           iCtr++;
        }
        sr.lpBuffer[iCtr]=_T('\0');
     }
     else
     {
        if(_tcsnicmp(this->lpBuffer+i,pMatch,iLenMatch)==0)
           i+=iLenMatch-1;
        else
        {
           sr.lpBuffer[iCtr]=this->lpBuffer[i];
           iCtr++;
        }
        sr.lpBuffer[iCtr]=_T('\0');
     }
 }
 sr.iLen=iCtr;
 return sr;
}


int String::ParseCount(const TCHAR c)  //returns one more than # of
{                                      //delimiters so it accurately
 int iCtr=0;                           //reflects # of strings delimited
 TCHAR* p;                             //by delimiter.

 p=this->lpBuffer;
 while(*p)
 {
  if(*p==c)
     iCtr++;
  p++;
 }

 return ++iCtr;
}


void String::Parse(String* pStr, TCHAR delimiter)
{
 size_t i=0;
 TCHAR* pBuffer=0;
 TCHAR* c;
 TCHAR* p;

 pBuffer=new TCHAR[this->iLen+1];
 if(pBuffer)
 {
    pBuffer[0]=0, p=pBuffer;
    c=this->lpBuffer;
    while(*c)
    {
       if(*c==delimiter)
       {
          pStr[i]=pBuffer,  p=pBuffer;
          i++,              pBuffer[0]=0;
       }
       else
       {
          *p=*c,  p++;
          *p=0;
       }
       c++;
    }
    pStr[i]=pBuffer;
    delete [] pBuffer;
 }
}


int iMatch(TCHAR* pThis, const TCHAR* pStr, bool blnCaseSensitive, bool blnStartBeginning, int i, int iParamLen)
{
 if(blnCaseSensitive)
 {
    if(_tcsncmp(pThis+i,pStr,iParamLen)==0)   //_tcsncmp
       return i+1;
    else
       return 0;
 }
 else
 {
    if(_tcsnicmp(pThis+i,pStr,iParamLen)==0)  //__tcsnicmp
       return i+1;
    else
       return 0;
 }
}


int String::InStr(const TCHAR* pStr, bool blnCaseSensitive, bool blnStartBeginning)
{
 int i,iParamLen,iRange,iReturn;

 if(*pStr==0)
    return 0;
 iParamLen=_tcslen(pStr);
 iRange=this->iLen-iParamLen;
 if(blnStartBeginning)
 {
    if(iRange>=0)
    {
       for(i=0; i<=iRange; i++)
       {
           iReturn=iMatch(this->lpBuffer,pStr,blnCaseSensitive,blnStartBeginning,i,iParamLen);
           if(iReturn)
              return iReturn;
       }
    }
 }
 else
 {
    if(iRange>=0)
    {
       for(i=iRange; i>=0; i--)
       {
           iReturn=iMatch(this->lpBuffer,pStr,blnCaseSensitive,blnStartBeginning,i,iParamLen);
           if(iReturn)
              return iReturn;
       }
    }
 }

 return 0;
}


int String::InStr(const String& s, bool blnCaseSensitive, bool blnStartBeginning)
{
 int i,iParamLen,iRange,iReturn;

 if(s.iLen==0)
    return 0;
 iParamLen=s.iLen;
 iRange=this->iLen-iParamLen;
 if(blnStartBeginning)
 {
    if(iRange>=0)
    {
       for(i=0; i<=iRange; i++)
       {
           iReturn=iMatch(this->lpBuffer,s.lpBuffer,blnCaseSensitive,blnStartBeginning,i,iParamLen);
           if(iReturn)
              return iReturn;
       }
    }
 }
 else
 {
    if(iRange>=0)
    {
       for(i=iRange; i>=0; i--)
       {
           iReturn=iMatch(this->lpBuffer,s.lpBuffer,blnCaseSensitive,blnStartBeginning,i,iParamLen);
           if(iReturn)
              return iReturn;
       }
    }
 }

 return 0;
}


void String::LTrim()
{
 size_t iCt=0;

 for(size_t i=0; i<this->iLen; i++)
 {
     if(this->lpBuffer[i]==32 || this->lpBuffer[i]==9)
        iCt++;
     else
        break;
 }
 if(iCt)
 {
    for(size_t i=iCt; i<=this->iLen; i++)
        this->lpBuffer[i-iCt]=this->lpBuffer[i];
 }
 this->iLen=this->iLen-iCt;
}


void String::RTrim()
{
 int iCt=0;

 for(int i=this->iLen-1; i>0; i--)
 {
     if(this->lpBuffer[i]==9||this->lpBuffer[i]==10||this->lpBuffer[i]==13||this->lpBuffer[i]==32)
        iCt++;
     else
        break;
 }
 this->lpBuffer[this->iLen-iCt]=0;
 this->iLen=this->iLen-iCt;
}


void String::Trim()
{
 this->LTrim();
 this->RTrim();
}


void String::Money(double dblAmount, size_t iFldLen, size_t iOneBasedOffsetDollarSign)
{
 size_t iLen,iCharactersNeeded,iDifference;
 String strAmount;

 strAmount.Money(dblAmount,0,false);
 iLen=strAmount.iLen;
 if(iLen>iFldLen)
    iCharactersNeeded=iLen;
 else
    iCharactersNeeded=iFldLen;
 if(this->iCapacity<iCharactersNeeded)
 {
    delete [] this->lpBuffer;
    size_t iNewSize = (iCharactersNeeded/16 + 1) * 16;
    this->lpBuffer=new TCHAR[iNewSize];
    this->iCapacity=iNewSize-1;
 }
 if(iLen>iFldLen)
    iDifference=0;
 else
    iDifference=iFldLen-iLen;
 for(size_t i=0; i<iDifference; i++)
     this->lpBuffer[i]=32;
 this->lpBuffer[iDifference]=L'\0';
 _tcscat(this->lpBuffer,strAmount.lpStr());
 if(iOneBasedOffsetDollarSign && iOneBasedOffsetDollarSign<=iFldLen)
    this->lpBuffer[iOneBasedOffsetDollarSign-1]=_T('$');
 this->iLen=_tcslen(lpBuffer);
}


void String::Money(double dblAmount, size_t iFldLen, bool blnDollarSign)
{
 size_t iLen,iCharactersNeeded,iDifference;
 String strAmount;

 strAmount.Format(dblAmount,0,2);
 if(blnDollarSign)
    strAmount=(TCHAR*)L"$"+strAmount;
 iLen=strAmount.iLen;
 if(iLen>iFldLen)
    iCharactersNeeded=iLen;
 else
    iCharactersNeeded=iFldLen;
 if(this->iCapacity<iCharactersNeeded)
 {
    delete [] this->lpBuffer;
    size_t iNewSize = (iCharactersNeeded/16 + 1) * 16;
    this->lpBuffer=new TCHAR[iNewSize];
    this->iCapacity=iNewSize-1;
 }
 if(iLen>iFldLen)
    iDifference=0;
 else
    iDifference=iFldLen-iLen;
 for(size_t i=0; i<iDifference; i++)
     this->lpBuffer[i]=32;
 this->lpBuffer[iDifference]=L'\0';
 _tcscat(this->lpBuffer,strAmount.lpStr());
 this->iLen=_tcslen(lpBuffer);
}


int String::iVal()
{
 return _ttoi(this->lpBuffer);  //_ttoi
}


int String::Len(void)
{
 return this->iLen;
}


int String::Capacity(void)
{
 return this->iCapacity;
}


TCHAR* String::lpStr()
{
 return lpBuffer;
}


void String::Print(FILE* fp1, bool blnCrLf)
{
 #ifdef NEW_GCC
    #ifdef UNICODE
       _ftprintf(fp1, _T("%S"),lpBuffer);
    #else
       _ftprintf(fp1, _T("%s"),lpBuffer);
    #endif
 #else
    _ftprintf(fp1, _T("%s"),lpBuffer);
 #endif
 if(blnCrLf)
    _ftprintf(fp1,_T("\n"));
}


void String::Print(FILE* fp1, TCHAR* pStr, bool blnCrLf)
{
 #ifdef NEW_GCC
    #ifdef UNICODE
       _ftprintf(fp1, _T("%S%S"),pStr,lpBuffer);
    #else
       _ftprintf(fp1, _T("%s%s"),pStr,lpBuffer);
    #endif
 #else
    _ftprintf(fp1, _T("%s%s"),pStr,lpBuffer);
 #endif
 if(blnCrLf)
    _ftprintf(fp1,_T("\n"));
}


void String::Print(TCHAR* pStr, bool blnCrLf)
{
 #ifdef NEW_GCC
    #ifdef UNICODE
       _tprintf(_T("%S%S"),pStr,lpBuffer);
    #else
       _tprintf(_T("%s%s"),pStr,lpBuffer);
    #endif
 #else
    _tprintf(_T("%s%s"),pStr,lpBuffer);
 #endif
 if(blnCrLf)
    _tprintf(_T("\n"));
}


void String::Print(bool blnCrLf)
{
 #ifdef NEW_GCC
    #ifdef UNICODE
       _tprintf(_T("%S"),lpBuffer);
    #else
       _tprintf(_T("%s"),lpBuffer);
    #endif
 #else
    _tprintf(_T("%s"),lpBuffer);
 #endif
 if(blnCrLf)
    _tprintf(_T("\n"));
}


String::~String()   //String Destructor
{
 delete [] lpBuffer;
 lpBuffer=0;
}
// Main.cpp
#ifndef UNICODE
    #define UNICODE
#endif
#ifndef _UNICODE
    #define _UNICODE
#endif
#include <windows.h>
#include <cstdio>
#include <new>
#include "Strings.h"
#include "Carray.h"


size_t iGetRecordCount(FILE* fp)   // This function simply returns
{                                  // the number of lines found in
 wchar_t szBuffer[128];            // a text file.  Need to pass in
 size_t iCtr=0;                    // valid cstdio FILE* object.

 while(!feof(fp))
 {
    fgetws(szBuffer,128,fp);
    iCtr++;
 }

 return iCtr;
}


template <typename t1> void PrintData(CArray<t1>& strAr)  // This function shows how to pass templated objects
{                                                         // through parameter list.  Note here it is passing
 for(int i=0; i<=strAr.UBound(1); i++)                    // a CArray object By Reference, which is our two
 {                                                        // dimensional array of my String Class objects.  It
     strAr(i,0).Print(false);                             // simply outputs these to console.  Note its using
     strAr(i,1).Print((wchar_t*)L"\t\t",false);             // overloaded String Class Print() methods.
     strAr(i,2).Print((wchar_t*)L"\t",true);
 }
}


int main()
{
 size_t iParseCount=0;       // For obtaining the number of semicolon delimited fields in a text file line
 wchar_t szBuffer[128];      // To hold line reada text file. in from text file through C Runtime getws()
 size_t iRecCt=0;            // For holding the number of lines of data in the data file
 String strLine;             // An object of my String Class type.  szBuffer contents to be reassigned to this
 FILE* fp=NULL;              // C Std. Lib. FILE object (in cstdio.h)

 fp=fopen("Data.dat","r");                     // Open data file containing 5 lines
 if(fp)                                        // If successfully opened, find out how many records/lines are in it
 {                                             // Then output to console that number.  Then rewind() to set file
    iRecCt=iGetRecordCount(fp);                // pointer to beginning of file.  Since we know that Data.dat has
    printf("iRecCt = %u\n\n",iRecCt);          // semicolon delimited fields, and that there should be three fields
    rewind(fp);                                // in each line of the file, we'll create a CArray object of my
    CArray<String> strDates(iRecCt,3);         // String Class which contains five rows and three columns if thought
    String* pStrs=NULL;                        // of in a two dimensional tabular representation.  The next step is
    for(size_t i=0; i<iRecCt; i++)             // to re-read each line (use C Std. Lib. rewind() to return FILE* to
    {                                          // beginning of file), and use the String::ParseCount() and
       fgetws(szBuffer,128,fp);                // String::Parse() members to seperate the colon delimited String
       strLine=szBuffer;                       // Objects.  Assign these to our two dimension String array strDates().
       iParseCount=strLine.ParseCount(L';');   // Only use lines with three fields.  Anything else is bad data.
       if(iParseCount==3)                      // Don't forget to delete [] each new call.  Actually, it would be
       {                                       // possible to reuse ths memory and only make one new call for
          pStrs=new String[iParseCount];       // pStrs.  The PrintData function shows how to pass CArray() objects
          strLine.Parse(pStrs,L';');                            // through functios.  Note strDates is passed By
          pStrs[0].Trim(), pStrs[1].Trim(), pStrs[2].Trim();    // Reference.
          pStrs[2]=pStrs[2].Remove((wchar_t*)L"|");
          strDates(i,0)=pStrs[0], strDates(i,1)=pStrs[1], strDates(i,2)=pStrs[2];
          delete [] pStrs;
       }
    }
    fclose(fp);
    PrintData(strDates);
 }
 getchar();

 return 0;
}

#if 0

iRecCt = 5

9:00 AM         1/15/2015       Dentist Appointment at 9:00 AM
7:00 PM         1/21/2015       Date with Cindy
8:00 PM         2/12/2015       Concert at Tom T Hall
3:00 PM         6/1/2015        Graduate From College
9:00 AM         6/2/2015        Start Six Figure coding job

#endif

One last thing. In the includes you at the top of the main program I just posted is CArray.h. That is simply the CArray Class I posted twice today. You can copy that to a seperate text file and name it CArray.h, or just put that code in the main source code file Main.cpp.

I tried copying my mangled code out of a post into Notepad and it came out OK, so the dog's breakfast this forum software makes of my code can be rectified apparently.

Looks like we cross posted. But yes, do your own thing for sure. I am only posting this code to give you some new ideas. I know its a lot to absorb.

One thing you might not be aware of is that Windows has a date/time entity - SYSTEMTIME I believe, which stores both time and date in one struct.

Also, I'm not sure I'd use a text file for this. But since you first mentioned that was what you were using and the problems you were having with it, that's why I decided to show how it might be done that way.

If binary data was used then it wouldn't be necessary to read the entire file into memory. You could just move through the file looking for what you need. But a whole binary (random access flat file) file could be read into memory like a text file. In that case you would'nt need a multi-dimensional array, but a single dimension array of a class/struct type, i.e.,

struct MyAppointments
{
 double dblDateTime;
 char szDescription[56];
};

Or whatever, and you would make an array of those...

MyAppointments* pMyAppointments = new [] MyAppointments[iNumber];

Or something like that. And others have mentioned relational databases. So there's lots of ways of attacking it.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.