0

I've a task to copy elements from .txt file[direct access file] to .bin file[fixed length record file] (homework). .txt file holds strings. Every line has one word. I came up with code below, but I'm not sure if that's what is needed and even slighly correct. Any help will be useful! (I'm new to C++)

#include <iostream>
#include <fstream>
#include <string>
using namespace std;
const int buffer_size = 30;

class Word{
    char name[buffer_size];
public:
    void setName () // Trying to get every word from a line
    {
        string STRING;
        ifstream infile;
        infile.open ("text.txt");
        while(!infile.eof()) // To get you all the lines.
        {
            getline(infile,STRING); // Saves the line in STRING.
        }
        infile.close();
    }
};


void write_record()
{
    ofstream outFile;
    outFile.open("binFILE.bin", ios::binary | ios::app);
    Word obj;
    obj.setName();
    outFile.write((char*)&obj, sizeof(obj));
    outFile.close();
}


int main()
{
    write_record();
    return 0;
}

NEW APPROACH:

class Word
{
char name[buffer_size];
public:
    Word(string = "");
    void setName ( string );
    string getName() const;
};

    void readWriteToFile(){
        // Read .txt file content and write into .bin file
        string temp;
        Word object;
        ofstream outFile("out.dat", ios::binary);
        fstream fin ("text.txt", ios::in);
        getline(fin, temp);
        while(fin)
        {
            object.setName(temp);
            outFile.write( reinterpret_cast< const char* >( &object ),sizeof(Word) );
            getline(fin, temp);
        }
        fin.close();
        outFile.close();
}

int main()
{
readWriteToFile();


return 0;
}
Word::Word(string nameValue)
{
setName(nameValue);
}
void Word::setName( string nameString )
{
// Max 30 char copy
const char *nameValue = nameString.data();
int len = strlen(nameValue);
len = ( len < 31 ? len : 30);
strncpy(name, nameValue, len);
name[len] = '\0';
}
string Word::getName() const
{

return name; }

  • 2
    You might be interested in reading ["Why is iostream::eof inside a loop condition considered wrong?"](http://stackoverflow.com/questions/5605125/why-is-iostreameof-inside-a-loop-condition-considered-wrong). – Some programmer dude May 06 '16 at 23:23
  • Suggestion: If you are allowed to use `std::string` on on this assignment in place of this: `char name[buffer_size];` do it. Save you much pain it will. Yes. Much pain. – user4581301 May 06 '16 at 23:23
  • @JoachimPileborg Thanks for advice. I'm still looking for the answer on my question! Let me know, if you have any idea :) – Maartin1996 May 06 '16 at 23:42
  • BTW, a good coding standard is the have variable names and type names differ by more than case. For example, `string` is a type and `STRING` is a bad variable name (only differs by case). – Thomas Matthews May 06 '16 at 23:54
  • Your `setName` function is useless. It reads lines from the text file into the same variable, overwriting the data from one line to the next. The string is a local variable, so it will disappear when the execution leaves the function. Thus there is a lot of execution for naught. – Thomas Matthews May 06 '16 at 23:56
  • I recommend reading the file outside of the `Word` class. For example, the `Word` class should have a method that reads *one* word from a file; or extracts a word from a string. You could have a *container* of `Word` objects. Maybe this is a root of confusion. – Thomas Matthews May 06 '16 at 23:59
  • @ThomasMatthews Could you show me your thought in a code, please (with explanations - if possible)? :) – Maartin1996 May 07 '16 at 00:11

1 Answers1

0

Quick commentary and walk through

#include <iostream>
#include <fstream>
#include <string>
using namespace std;

Avoid using namespace std; while you are learning. It can lead to some really nasty, hard to pin-down bugs as your functions may be silently replaced by functions with the same name in the standard library.

const int buffer_size = 30;

class Word
{
    char name[buffer_size];

Since it looks like you are allowed to use std::string why not use it here?

public:
    void setName() // Trying to get every word from a line

Really bad name for a function that apparently is supposed to // Trying to get every word from a line

    {
        string STRING;
        ifstream infile;
        infile.open("text.txt");
        while (!infile.eof()) // To get you all the lines.
        {
            getline(infile, STRING); // Saves the line in STRING.
        }

Few things wrong here. One is the epic Why is iostream::eof inside a loop condition considered wrong?

Next is while the code reads each line, it doesn't do anything with the line. STRING is never stored anywhere.

Finally in a class that sounds as though it should contain and manage a single word, it reads all the words in the file. There may be a case for turning this function into a static factory that churns out a std::vector of Words.

        infile.close();
    }
};

void write_record()
{
    ofstream outFile;
    outFile.open("binFILE.bin", ios::binary | ios::app);

ios::app will add onto an existing file. This doesn't sound like what was described in the assignment description.

    Word obj;
    obj.setName();

We've already coverred the failings of the Word class.

    outFile.write((char*) &obj, sizeof(obj));

Squirting an object into a stream without defining a data protocol or using any serialization is dangerous. It makes the file non-portable. You will find that some classes, vector and string prominent among these, do not contain their data. Writing a string to a file may get you nothing more than a count and an address that is almost certainly not valid when the file is loaded.

In this case all the object contains is an array of characters and that should write to file cleanly, but it will always write exactly 30 bytes and that may not be what you want.

    outFile.close();
}

int main()
{
    write_record();
    return 0;
}

Since this is homework I'm not writing this sucker for you, but here are a few suggestions:

Read file line by line will get you started on the file reader. Your case is simpler because there is only one word on each line. Your teacher may throw a curveball and add more stuff onto a line, so you may want to test for that.

Read the words from the file into a std::vector. vector will make your job so easy that you might have time for other homework.

A very simplistic implementation is:

std::vector<std::string> words;
while (getline(infile, STRING)) // To get you all the lines.
{
    words.push_back(STRING);
}

For writing the file back out in binary, I suggest going Pascal style. First write the length of the string in binary. Use a known, fixed width unsigned integer (no such thing as a negative string) and watch out for endian. Once the length is written, write only the number of characters you need to write.

Ignoring endian, you should have something like this:

uint32_t length = word.length(); // length will always be 32 bits
out.write((char*)&length, sizeof(length));
out.write(word.c_str(), length);

When you are done writing the writer, write a reader function so that you can test that the writer works correctly. Always test your code, and I recommend not writing anything until you know how you'll test it. Very often coming at a program from the test side first will find problems before they even have a chance to start.

Community
  • 1
  • 1
user4581301
  • 29,019
  • 5
  • 26
  • 45
  • I took under advice your suggestions. Also, I tried different approach. As I'm on linux, I don't know how to open my .bin file so I can actually see the content of the file (not sure if my program now works, but maybe you can take a look) – Maartin1996 May 08 '16 at 10:56
  • Do a search with whatever package manager your Linux distribution uses for "hex editor" or check to see if your favorite text editor contains a hex mode. A hex editor will allow you to open a file and see it represented as bytes. – user4581301 May 08 '16 at 15:59