1

as beginner need your help

So I'm trying to make program which can write phrases into the text document, new line below previous, just in original order. It checks If phrase exist in document and shows me the row on output, if not, add it as new.

Now I want two thing here, and can't figure out, question is how to get index for lines, and how to manipulate with it to get lines, for example my text document content:

word1
word2
word3
word4
word5

Now, if text inserted by user for example "word6", which is not exist in document, just must be added below. But if inserted text for example "word1" which is exist in document, in this case I want see on output "word2", which is below the found "word1", but if inserted text is "word2", I want see on output "word1" etc. if "word3" on output show me "word4", if "word4" show me "word3" etc, depending which index has been defined in current exist word, divisible -1 or not divisible +1 like this:

word1 (indivisible) show word2
word2 (divisible) show word1
word3 (indivisible) show word4
word4 (divisible) show word3

using namespace std;

std::ofstream outfile("doc.txt", std::ios_base::app);

int main()
{

    int length = 100;

    std::ifstream infile("doc.txt", std::ifstream::in);
    infile.seekg(0, infile.end);
    size_t len = infile.tellg();
    infile.seekg(0, infile.beg);
    char *buf = new char[len];
    infile.read(buf, length);
    infile.close();
    std::string writtenStr(reinterpret_cast<const char *>(buf), len);

    std::string t;

    for (int i = 0; i < 10; i++)
    {
        std::getline(std::cin, t);

        if (writtenStr.find(t) != std::string::npos)
        {
            cout << "Line [" << t << "] exist." << endl;
        }
        else
        {
            cout << "Line [" << t << "] saved." << endl;
            writtenStr += t;
            outfile << t << std::endl;
        }
    }
    _getch();
    return 0;
}
  • Why do you read into a `char` buffer then create a `std::string` from the buffer? Why don't you read into a `std::string`? And why do you `reinterpret_cast` a `char*` to a `const char*` lol. And why the dynamic allocation? Why do you never free the memory? Why do you use horrid `_getch()`? Why is your `ofstream` global but your `ifstream` local? Oh I could go on.... – Lightness Races in Orbit Aug 04 '16 at 23:18
  • 1
    Oh, almost forgot... what's your question? – Lightness Races in Orbit Aug 04 '16 at 23:19
  • @Lightness Races in Orbit as I have say, I'm beginner, I'm trying to figure out somehow, if you can show me all this corrections, I would be thankful. Ok edited I've described qvestion above better –  Aug 04 '16 at 23:28
  • @LightnessRacesinOrbit http://stackoverflow.com/help/be-nice – qxz Aug 04 '16 at 23:32
  • 2
    @qxz: This _is_ me being nice. – Lightness Races in Orbit Aug 04 '16 at 23:38
  • @JohnK: There is no relationship. That was a different user, named "qxz", talking to me. – Lightness Races in Orbit Aug 04 '16 at 23:42
  • @JohnK Lightness was being asked to be nice, not you. I need to run a sick experiment: Can you do `std::getline` on a file stream with the delimiter set to a value that can't be in the file like NULL and read the whole file? – user4581301 Aug 04 '16 at 23:43
  • Looks like it works. Don't think you can trust it though. – user4581301 Aug 04 '16 at 23:48
  • @LightnessRacesinOrbit Well I'm new here and in programing too, and I have qvestion which is described above. Thank you for all remarks, but for my level, better to see your solution for this case. So maybe you can show me how to make my code more correct –  Aug 04 '16 at 23:50
  • 1
    @user4581301 NUL bytes **can** be in a file. Files aren't null-terminated, and you can't be sure that there won't be a NUL somewhere inside, especially for a binary file. – Taywee Aug 04 '16 at 23:51
  • I'm asking you to clarify your question, @John. – Lightness Races in Orbit Aug 04 '16 at 23:51
  • @Taywee Agreed. Just trying to capitalize on the extreme unlikeiness of a null inside an ASCII file being scanned for words. For this case, probably works. General case, not bloody likely. My main interest was in can you do this? Answer is yes, even if it won't work. – user4581301 Aug 04 '16 at 23:53
  • 1
    @user4581301 It's probably less efficient than ideal, because the file will be scanned for the delimiter at each char. [There are much more efficient ways to slurp an entire file into a string](https://stackoverflow.com/questions/2602013/read-whole-ascii-file-into-c-stdstring). – Taywee Aug 04 '16 at 23:56
  • @LightnessRacesinOrbit if new line is found and already exist in text document, on output I want get not this line, but line below it or above, depending which index has been defined in equal line, if index divisible -1 if not then +1. So if inserted new text is equal to exist word1 with index1, I want get on output word2 with index 2, but if new word equal to word2 with index 2 I want see on output word1 –  Aug 05 '16 at 00:08
  • Getting back on topic... John, it is extremely hard to write into an existing file without overwriting the data after the insertion point. The simplest approach is to open a new file and write out the amended text. You can do this fairly easily with @qxz 's answer by inserting new data into the `vector` and then writing the contents of the `vector` into a new file over top of the original file. – user4581301 Aug 05 '16 at 00:08
  • Back off topic, Good link, @Taywee . Hopefully John will read it. – user4581301 Aug 05 '16 at 00:12
  • @JohnK I'm still a bit confused about what you want to do once you've found a matching line... Can you edit your question to clarify, and provide an actual example of the contents of this text file? – qxz Aug 05 '16 at 00:22
  • Mark my answer as accepted if it worked for you! – qxz Aug 05 '16 at 01:15

1 Answers1

2

First of all, an easy way to read an entire file into an std::string (ref):

std::ifstream infile("file.txt");
std::string str(std::istreambuf_iterator<char>(t),
                std::istreambuf_iterator<char>());

But, if you want to get a list (std::vector) of lines, try this instead:

std::ifstream infile("file.txt");
std::vector<std::string> lines;
for (std::string line; std::getline(infile, line); ) {
   lines.push_back(line);
}

lines will then contain a list of each line in the file. If you want to find the index of a particular line in this list (ref):

std::string t = ...; // the line to search for
auto it = std::find(lines.begin(), lines.end(), t);
if (it == lines.end()) {
  // line wasn't found
} else {
  int index = it - lines.begin();
  //...
}

Once you have the index of a line, you can get the adjacent line described in your question like this:

int index2;
if (index%2 == 0) {
  // even index
  index2 = index + 1;
} else {
  // odd index
  index2 = index - 1;
}
std::string str2 = lines[index2]; // the text of the adjacent line

Other tips:

Your std::ofstream should probably be a local variable. Declare it inside of your int main().

ifstreams get the std::ios::in flag by default, so you can leave out that second parameter to its constructor.

If you dynamically allocate memory (with new type or new type[len]), always make sure it gets freed (with delete or delete[]). Example from your code:

char *buf = new char[len]; // dynamically allocated with new
// use buf somehow...
// when done with buf:
delete[] buf; // frees the memory

Complete working example:

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


const string FILE_NAME = "doc.txt";

vector<string> getFileLines(string file) {
    ifstream in(FILE_NAME);
    vector<string> lines;
    for (string line; getline(in, line); ) {
        lines.push_back(line);
    }
    return lines;
}

string getUserInput() {
    string str;
    getline(cin, str);
    return str;
}

int main() {
    // read the contents of the text file into an std::vector of lines
    vector<string> lines = getFileLines(FILE_NAME);

    // open an output stream to append new lines
    ofstream fileOut(FILE_NAME, ios::app);

    // 10 times...
    for (int n = 0; n < 10; n++) {
        // get a line from the user
        cout << "> ";
        std::string t = getUserInput();

        // find it in the vector of lines
        auto it = std::find(lines.begin(), lines.end(), t); // "auto" deduces the type automatically
        if (it == lines.end()) {
            // the line wasn't found; append it...
            fileOut << t << endl; // to the file
            lines.push_back(t);   // to our cached list of lines
            cout << "Line \"" << t << "\" saved.\n";
        } else {
            // the line was found, and this is its index:
            int index = it - lines.begin();
            cout << "Line \"" << t << "\" found, at index " << index << ".\n";

            // get the adjacent line
            int index2;
            if (index%2 == 0) {
                // even index, get the next line
                index2 = index + 1;
            } else {
                // odd index, get the previous line
                index2 = index - 1;
            }
            if (index2 < lines.size()) {
                string line2 = lines[index2]; // the text of the adjacent line
                cout << "Adjacent line: \"" << line2 << "\" (index " << index2 << ")\n";
            } else {
                cout << "No adjacent line yet!\n";
            }
        } // end if (line was found)
    } // end for (10 times)

    cout << endl;
    getUserInput();
    return 0;
}
Community
  • 1
  • 1
qxz
  • 3,697
  • 1
  • 11
  • 29
  • yes I guess it must work, but I'm still trying to combine everything you give me below and still have errors, probably because I do something wrong, maybe you can show me some assembled working sample with your corrections and solutions for my code? it would be very useful –  Aug 05 '16 at 02:17
  • thank you for your support, you helped me understand things that interested me, respect. now the only thing I wonder is how fast is a search for example if I have 10 000 lines. Should I use database in this case instead of a text document or this is not necessary? –  Aug 05 '16 at 12:42
  • An indexed database or HashMap/HashSet type thing would be faster. `std::find` searches linearly through up to the entire list. – qxz Aug 05 '16 at 18:32
  • However, I just tested with a list of 100,000 words (on my crummy computer), and the `find` operation was still instantaneous. So unless you're dealing with many millions of lines, you should be okay. – qxz Aug 05 '16 at 18:42