Reading Text File and Appending

Pages: 123
So I am starting to create this program to read a text file and copy the text to another file until it reaches the line that is </settings>. I thought I was doing well but everything copies. I am not sure what I am missing and was hoping someone could help. I also tried messing with my if statement but when I changed the != to == only the stuff after </settings> copied. I know the program is not complete as I plan to do this a couple times then save the new file so it's not finished yet.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
  //

#include <fstream>
#include <iostream>
#include <string>
#include <stdio.h>
#include <Windows.h>

int main()
{
    int results;
    char oldname[] = "c:\\synergyii\\config\\clientconfig.xml";
    char newname[] = "c:\\synergyii\\config\\clientconfig.txt";
    results = rename(oldname, newname);
    if (results == 0)
        puts("File successfully renamed");
    else
        perror("Error renaming file");

    
    std::ifstream inf{ "c:\\synergyii\\config\\clientconfig.txt" };

    std::ofstream outf{ "c:\\synergyii\\config\\clientconfig.xml" };

    if (!inf)
    {
        std::cerr << "Cannot open client config for reading" << std::endl;
        return 1;
    }

    if (!outf)
    {
        std::cerr << "Cannot open client config for writing" << std::endl;
        return 1;
    }
   
    while (inf)
    {

        std::string line;

        line.assign("</settings>");

        std::string strInput;
        std::getline(inf , strInput);

        if (strInput != line)
        {
            outf << inf.rdbuf();
        }
   
    }
    return 0;

}
Last edited on
before you debug this (it looks ok), look at your input file.

1
2
3
4
5
6
7
</settings>
^^ that would match, no spaces or tabs etc to left and right
 </settings>
^^ nope, leading space
</settings> 
^^ nope, trailing space
.. etc
Last edited on
Hey jonnin,

I thought that too, but there are no spaces before or after. It is the only thing in that line.
Last edited on
Hello Vendetto,

I thought I was doing well but everything copies.

My question would be in line 49 what does "rdbuf" contain?

Next question is what is the exact input file that you are using. Without seeing what you have to work with it makes it hard to understand your code and to test the program.

Andy
Hello Vendetto,

Working on the program I noticed:

Renaming a ".xml" file to a ".txt" file is unnecessary and a ".xml" file is a text tile. What would make more sense would be to choose a different output file name and then, maybe, at the end of the program rename the file.

Good job on defining the file stream variables and opening the files then checking if they opened.

After that I am thinking that this might work as a start:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
std::string line{ "</settings>" };
std::string strInput;

while (std::getline(inf, strInput))
{
    if (strInput == line)
    {
        ;// <--- Do Something
    }
    else
    {
        outf << strInput << '\n';
    }
}

There is no need for line.assign("</settings>"); as you can do this when you define the variable. Unless you want the practice. Also line = "</settings>"; would also work.

Andy
For the copy part, this can be done simply as:

1
2
for (std::string strInput; std::getline(inf, strInput) && strInput != "</settings>"; outf << strInput << '\n');

Last edited on
Thank you Handy Andy!!! I am back on track. I like your suggestion on the renaming at the end or even creating a new one and not having so change the xml to a txt so will most likely incorporate it with the final version. Here is what I have and it is all working now so I can continue:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
//

#include <fstream>
#include <iostream>
#include <string>
#include <stdio.h>
#include <Windows.h>

int main()
{
    int results;
    char oldname[] = "c:\\synergyii\\config\\clientconfig.xml";
    char newname[] = "c:\\synergyii\\config\\clientconfig.txt";
    results = rename(oldname, newname);
    if (results == 0)
        puts("File successfully renamed");
    else
        perror("Error renaming file");

    
    std::ifstream inf{ "c:\\synergyii\\config\\clientconfig.txt" };

    std::ofstream outf{ "c:\\synergyii\\config\\clientconfig.xml" };

    if (!inf)
    {
        std::cerr << "Cannot open client config for reading" << std::endl;
        return 1;
    }

    if (!outf)
    {
        std::cerr << "Cannot open client config for writing" << std::endl;
        return 1;
    }

    std::string line{ "</settings>" };
    std::string strInput;

    while (std::getline(inf, strInput))
    {
        if (strInput == line)
        {
            break;
        }
   
        else 
        {
            outf << strInput << '\n';
        }

    }
    return 0;
}
Right now the program is working great with the code below. However, I am looking for a way to possibly compare the lines in the newlines.txt and make sure they aren't duplicated in what was already printed to the output file just so there isn't duplicated settings. Any suggestions?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78

//

#include <fstream>
#include <iostream>
#include <string>
#include <stdio.h>
#include <Windows.h>

int main()
{
    int results;
    char oldname[] = "c:\\synergyii\\config\\clientconfig.xml";
    char newname[] = "c:\\synergyii\\config\\clientconfig.txt";
    results = rename(oldname, newname);
    if (results == 0)
        puts("File successfully renamed");
    else
        perror("Error renaming file");

    
    std::ifstream inf{ "c:\\synergyii\\config\\clientconfig.txt" };

    std::ofstream outf{ "c:\\synergyii\\config\\clientconfig.xml" };

    if (!inf)
    {
        std::cerr << "Cannot open client config for reading" << std::endl;
        return 1;
    }

    if (!outf)
    {
        std::cerr << "Cannot open client config for writing" << std::endl;
        return 1;
    }

    std::string line{ "</settings>" };
    std::string strInput;

    while (std::getline(inf, strInput))
    {
        if (strInput == line)
        {
            break;
        }
   
        else 
        {
            outf << strInput << '\n';
        }

    }

    inf.close();

    std::ifstream inf2{ "c:\\synergyii\\config\\newlines.txt" };

    if (!inf2)
    {
        std::cerr << "Cannot open client config for reading" << std::endl;
        return 1;
    }

    while (std::getline(inf2, strInput))
    {
        outf << strInput << '\n';
    }

    inf2.close();

    outf << "</settings>" << '\n';
    outf << "</config>" << '\n';

    outf.close();

    return 0;
}


Thanks,
V
Try this [not tested]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <fstream>
#include <iostream>
#include <string>
#include <cstdlib>
#include <set>

int main()
{
	const char oldname[] = "c:\\synergyii\\config\\clientconfig.xml";
	const char newname[] = "c:\\synergyii\\config\\clientconfig.txt";

	if (rename(oldname, newname) == 0)
		puts("File successfully renamed");
	else
		perror("Error renaming file");

	std::ifstream inf {"c:\\synergyii\\config\\clientconfig.txt"};
	std::ofstream outf {"c:\\synergyii\\config\\clientconfig.xml"};
	std::ifstream inf2 {"c:\\synergyii\\config\\newlines.txt"};

	if (!inf2) {
		std::cerr << "Cannot open client newlines.txt for reading" << std::endl;
		return 3;
	}

	if (!inf) {
		std::cerr << "Cannot open clientconfig.txt for reading" << std::endl;
		return 1;
	}

	if (!outf) {
		std::cerr << "Cannot open clientconfig.xml for writing" << std::endl;
		return 2;
	}

	std::set<std::string> lines;

	for (std::string strInput; std::getline(inf, strInput) && strInput != "</settings>"; lines.insert(strInput));
	for (std::string strInput; std::getline(inf2, strInput); lines.insert(strInput));

	for (const auto& l : lines)
		outf << l << '\n';

	outf << "</settings>" << '\n';
	outf << "</config>" << '\n';
}

Hey seeplus,

This does work in preventing duplicated lines however, it is totally messing up the orders. It is putting everything in order alphabetically instead of keeping the format which is required.

Thanks,
V
Expanding on seeplus's code.
Note that you don't need to use double backslashes to separate files.
A forward slash works just fine, even for Windows (I believe).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <fstream>
#include <iostream>
#include <string>
#include <cstdlib>
#include <set>
#include <vector>
using namespace std;

int main() {
    const string Dir { "C:/synergyii/config/" },
                 oldname = Dir + "clientconfig.xml",
                 newname = Dir + "clientconfig.txt";

    if (rename(oldname.c_str(), newname.c_str()) != 0) {
        perror("Error renaming file");
        return 1;
    }

    ifstream in_config   { newname },
             in_newlines { Dir + "newlines.txt" };
    ofstream out         { oldname };

    if (!in_config) {
        cerr << "Cannot open clientconfig.txt for reading\n";
        return 1;
    }
    if (!in_newlines) {
        cerr << "Cannot open client newlines.txt for reading\n";
        return 1;
    }
    if (!out) {
        cerr << "Cannot open clientconfig.xml for writing\n";
        return 1;
    }

    vector<string> vlines;
    set<string>    slines;

    for (string line; getline(in_config, line) && line != "</settings>"; ) {
        const auto& result = slines.insert(line);
        if (result.second)
            vlines.push_back(line);
    }

    for (string line; getline(in_newlines, line); ) {
        const auto& result = slines.insert(line);
        if (result.second)
            vlines.push_back(line);
    }

    for (const auto& line : vlines)
        out << line << '\n';
    out << "</settings>\n</config>\n";
}

By golly I think we have something here! Now to read up and understand why it is working so well. It's not enough for me that it works but I need to understand it LOL :)

This will actually be added to a larger project that I will be developing which will include a user interface to allow for options and changes so more to come.

You guys are the best!!
The basis behind this is that a set can't contain duplicates. If you try to insert the same item to a set it will be rejected. The return value indicates whether the insertion was successful (not duplicate) or failed (duplicate). There is also multiset which can contain duplicate values. set maintains its data as a sorted tree for quick insert/find. That's why using my original code you got the new file in sorted order. As that wasn't acceptable, dutch's solution uses a vector to store the items in order if a set insertion succeeded (ie not duplicate).

In any scenario in which non-duplicates are required, think set (or map if an associated value is also needed).
I think I am following it now seeplus.

One more question, I want to include 2 not equal </settings>. One being "</settings>" and the other being " </settings>" (with a TAB space in front of it). I was thinking:

1
2
3
4
5
6

    for (string line; getline(in_config, line) && line != "</settings>" , "     </settings>"; ) {
        const auto& result = slines.insert(line);
        if (result.second)
            vlines.push_back(line);


but it doesn't seem to work. The reason is there might be a TAB space in front and want to account for it.

Thanks,
V
I even tried:

1
2
3
4
    for (string line; getline(in_config, line) && line != "</settings>" && line != "'\t'</settings>"; ) {
        const auto& result = slines.insert(line);
        if (result.second)
            vlines.push_back(line);


and:

1
2
3
4
    for (string line; getline(in_config, line) && line != "</settings>" || "'\t</settings>"; ) {
        const auto& result = slines.insert(line);
        if (result.second)
            vlines.push_back(line);


and those did not work either.

Try (not tested):

 
for (string line; getline(in_config, line) && line.find("</settings>") != string::npos); ) {

So this was weird. It only copied all the stuff from the newlines.txt and discarded all the stuff from the original.
I said it wasn't tested. Didn't actually insert into the vector. Ahhh.....

 
for (string line; getline(in_config, line) && line.find("</settings>") != string::npos); vlines.push_back(line)) {



Oh I know you said it wasn't tested. I have no issues testing things as they are suggested. The best part about this project is I have no deadline as it is helping me learn.

Now I understand what you are saying as it wasn't inserted into the vector, but now I am being told line is undefined and VS is expecting a ; in there but when I get the code to check out it still drops the stuff from clientconfig and only does the newlines. I am trying to see if there is something I am not seeing in the line that needs to be formatted differently.
 
for (string line; getline(in_config, line) && line.find("</settings>") == string::npos; vlines.push_back(line)) {


It's not my day for having typing trouble. Extra ).......



Last edited on
Pages: 123