I am trying to get a handle on two things: building strings, and reading a file. I am trying to write a function that will pull a line from a file and put it into a string. I want to use it in a loop, so I am having the function return the EOF status (true or false). I am passing a pointer to the string so it should come out on the other side.

The function works fine internally... if I do a cout inside the function I can see it does build the string. However, it is not passing the string back to the main program.

Furthermore, the program crashes after it ends. I think it is some sort of memory leak, though I am not allocating any memory.

Any help would be appreciated.

-Dan

#include <iostream>
#include <stdio.h>

using namespace std;

bool getline(FILE *f, char* ln)
{
    char ch[1];
    bool runFlag=true;
    int ps=0;
    
    ln[0]=0;
    
    while ((fgets(ch, 2, f) != NULL) && (runFlag))
    {
        if ((ch[0]=='\n') || (ch[0]=='\r')) runFlag=false;
        if (runFlag==true) {ln[ps++]=ch[0];}
    }
   
    ln[ps]=0;
    return !runFlag;
}

int main(int argc, char *argv[])
{
    FILE *st;
    char  buf[256];
    char *ln = buf;
    
    st = fopen("c:\\work\\clock.txt", "r");
    
    while (getline(st,ln))
    {
        cout << ln << '\n';
    }

    fclose(st);
   
    system("PAUSE");
    return 0;
}

Recommended Answers

All 6 Replies

Asking for the sake of curiosity, is there a specific reason you're not using the filestreams and <string>?
for example see this which might (I don't know for sure) have similar insides and can definitely be used in a loop.

I'll look at that... I've been using that site for help as well.

I am trying to learn a few basics on the nitty-gritty level, such as how strings work on a fundamental level, and as I have it looking character-by-character for the end of line characters, I will be making similar parsers that look for other patterns and do things accordingly. It turns out C++ is much more picky than I thought, so I have a lot to learn. ;-)

I am gearing up to build an application that will be a liason between Excel and an as/400 system we use at work, that can run scripts and do automatic data entry and such. I already have the scripting language designed and portions working in Visual Basic, but C++ is much faster and the libraries for the AS/400 system are in C (though there is limited functionality I can do from VBA as well) so I am learning C/C++ now.

Writing a scripting language requires a lot of parsing and communication between functions, so I am starting at the ground and learning up.

I plan to work on some sort of a Pascal-type string library to get around the "not knowing where the end of the line" problems (for efficiency sake), maybe using two bytes for the end of string marker so I can have up to 65536 characters instead of just 256. (I know already how to break into high-byte, low-byte.) To do that, I have to know exactly how C/C++ works with strings.

-Dan


Writing a scripting language requires a lot of parsing and communication between functions, so I am starting at the ground and learning up.

To do that, I have to know exactly how C/C++ works with strings.

I gotcha. Sounds like a cool project (and automation anything is always nice). I've begun to appreciate the power of using streams to do things but getting it all from the ground up is a great way to learn a ton, so best of luck.

I gotcha. Sounds like a cool project (and automation anything is always nice). I've begun to appreciate the power of using streams to do things but getting it all from the ground up is a great way to learn a ton, so best of luck.

thanks... I will look into streams as well, if it will make things easier. Not everything I am doing needs to be so micro-managed, so in some cases streams will be the way to go.

I got my start with 8-bit computers in the '80s where to do anything you had to do all the basics by hand. They would do some things (like strings) but other things like memory management, graphics, sounds, etc. you had to manually break down every note, every pixel, in some cases actually map out the physical sections of memory... but I learned tons about computers that way that I think really helps me now. (I was in fifth grade when I finally started understanding binary mathematics and how computers really break down things. From there, I've had a good foundation in computer science.)

So I want to do the same here. It's a little tricky but when I think about it, in the end it isn't too bad, because in the other languages when you read in a string, the same things happen, it's just that the system does it automatically. So it's not like iterating character by character is going to be slower... strcopy and strtok and such do the same thing. And I can control exactly how I want... I have some things where I want to do three or four things in the same pass so I don't have to iterate through the string a hundred times... I am going to have some of these routines as part of a timer loop (a simple way of writing a windows application that will actively do something) so I want each step to count.

The problem is I have to know exactly what C++ is doing. It's too easy to crash a program or get weird side effects from simple mistakes.

ai yi yi... I figured it out.

I am putting two characters into a one character buffer.

When I allocate two characters instead, it works.

I will have to get used to allocating space for the null terminator.

-Dan

Incidentally, I just learned about fgetc... I was using fgets to pull in a character (which had to be allocated as two characters because it must also pull in the null terminator), when really that was just complicating things. I wouldn't have had a problem at all if I had just used fgetc. So I have fixed the code and made it a bit more readable:

bool getline(FILE *f, char* ln)
//Get a line of text from a file
{
    char ch;
    bool endLine=false;
    int ps=0;
    
    ln[0]=0;
    
    do
    {
        ch=fgetc(f);
        if ((ch=='\n') || (ch=='\r') || (ch==EOF)) endLine=true; //end of line, exit loop
        if (!endLine) ln[ps++]=ch; //if we are still in the file, build the string
    } while (!endLine);

    ln[ps]=0; //put a zero-terminator at the end of the string
    
    return ch==EOF; //send back if we have reached the end of the file
}

Then the instantiating code could be as such:

FILE *f;
    char  buf[256];
    char *ln = buf;
    bool go=true;
    
    //open file, such as f = fopen("c:\\sample.txt", "r");
    
    do
    {
        go=!getline(f,ln);

        //work with the line here...
        //example: cout << ln << '\n';, though there are much better
        //methods to use if we are simply displaying a line.

    } while (go);

    //at some point, close the file
    fclose(f);

Note the string returned will not have the \n or \r at the end, it will strictly be the text. If the line was blank, it will return a null string.

I'm sure there is a better way to build the string than to specifically index each character and explicitly add a zero-terminator at the end... but this is what I know at this point.

The power is that I can modify the getline function to look for other characters along the way and do things such as exclude characters, change characters, use other characters than /n or /r to delineate the string, etc. And the code should be perfectly portable and safe.

The other problem I can forsee is if the line ends in a \n\r combination, it will send the line and clear the \n but the next read will be null because it will see the \r before it gets to the next line. However, it seems the files I am testing on (and I made one in notepad to be sure) do not have this problem.

I could put an if (endLine && !EOF && ps==0) endLine=false; just before the end of the loop if that does prove to be a problem. Note that would also skip any blank lines (such as double spacing after a paragraph in a text document).

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.