Sign in to follow this  
JamesCobras

Writing to the top of a text file preserving the rest of the doc.

Recommended Posts

Hi guys, i have a load of HTML files that are lacking things like the <title> tags and stuff, and the files lose the changed stuff every time i re-publish, it's a problem with frontpage and templates. Anyway i thought hay, i can fix this, get a file read the first line for the file and the second line for the text that needs to go at the top of the file e.g. <title> title <title>. Now i planned it all out but i came to write a file with fstream (and it's associated classes). The only way i can open write and save (i have found) is with fstream::ate. I try to seek to the beggining of the doc, i've tried several times and realised that it only seeks within the current line. Also there is no function i have found that lets you seek lines. Apart from reading the whole file editing it in memory and re-writing the whole thing back to file (this seems a bit excessive), is there any way i can fill the empty line at the top of the file? I'm surprised i have encountered this problem, is there a libary that can be used, a portable lib would be best. JamesCobras

Share this post


Link to post
Share on other sites
Quote:
Original post by JamesCobras
Apart from reading the whole file editing it in memory and re-writing the whole thing back to file (this seems a bit excessive), is there any way i can fill the empty line at the top of the file?
Nope - that's the way it's always done. Files have no concept of lines, they're just a stream of bytes. Which is also why you can't seek to line X; only seek forwards X bytes.

Share this post


Link to post
Share on other sites
Right well if it's just 1 long line, why can't i seek to the first charater in the whole file.

e.g.

1------------ (10bytes)
------------- (10bytes)
------------- (10bytes)
------------- (10bytes)
2------------


I'm on line 2 how do i seek to point 1? It doesn't seem to want to.
I uderstand that to a computer it looks like one long line, but how do i seek to the start of the long line? e.g. back 40 bytes, it just doesn't seem to work?! I'd of thought it would.

JamesCobras

[Edited by - JamesCobras on July 23, 2009 6:30:10 AM]

Share this post


Link to post
Share on other sites
If you want to *overwrite* data at the start of a file, you can do that like this:


fstream file("filename.txt", ios::ate | ios::in | ios::out );

file.seekp(0, ios::beg);
file << "Overwriting the first characters in file.";



However, if your first line is nothing but a new line (which is most likely) then you have to realize that to you'll merely overwrite that newline and then start overwriting the second line's data. The new line character(s) is/are nothing more than additional characters to the file stream. It doesn't understand the difference between one line and the text.

Share this post


Link to post
Share on other sites
Ah, lovely just what i was looking for.

But why wasn't the following code working like that:


#include <fstream>
using namespace std;

int main () {

fstream HTML;

HTML.open ("test.txt", fstream.out | fstream::ate);

// >> i/o operations here <<


HTML.seekg(0,ios_base::beg);
HTML.write("\nhello4\n",8);


HTML.close();

return 0;
}

Share this post


Link to post
Share on other sites
Err no it didn't work guys.

text file before:

asdf
a
sdg
asdfgs
df
gas
dg
adsfg sad
fg

fg sdffg h hfgv asghfv asmnhdfv sadfg
asdfg as
dg
sdf
h a
dfg
a
sdfgas
fg
adf
g
ad
g
adfg
ad
fhadfhasdfg
adfgh
adfh
asdf
g
adsfg
asdf
ga
sdfg a
fg
a
sd
fah
a
fg
a
fg
adfh




After:

hello




Code:

#include <fstream>
using namespace std;

int main () {

fstream HTML;

HTML.open ("test.txt", ios::out | ios::ate);

// >> i/o operations here <<


HTML.seekg(0,ios_base::beg);
HTML << "hello";


HTML.close();

return 0;
}





Wanted:
After:

hello
asdf
asd
fa
sg
as
dfg

rg
aserg





Hope that clears a load of stuff up and helps you help me.

JamesCobras

P.S. i think that the code i was using was synominous with the code you gave me (got same results)

Share this post


Link to post
Share on other sites
Read the entire file into memory. Replace the lines. Write the entire file back out.

This is the easiest way.

#include <string>
#include <vector>
#include <fstream>
#include <iterator>
#include <algorithm>

typedef std::vector<std::string> FileData;

FileData read(const std::string &filename)
{
FileData result;
std::ifstream in(filename.c_str());
std::string line;
while(std::getline(in, line))
{
result.push_back(line);
}
return result;
}

void write(const std::string &filename, const FileData &data)
{
std::ofstream out(filename.c_str());
std::copy(data.begin(), data.end(), std::ostream_iterator<std::string>(out, "\n"));
}

void go(const std::string &filename)
{
FileData data = read(filename);

// modify data

write(filename, data);
}


This gives you a nice line based view of the file in the vector.

Share this post


Link to post
Share on other sites
Your code is overwriting the entire file because you forgot to include ios::in in your opening flags.


HTML.open("test.txt", ios::in | ios::out | ios::ate);


However, you still do not appear to be listening to the more important information that others are giving you in this thread.

In a file, a newline is represented not by a long empty line of spaces ready for you to overwrite, but by a single newline character (or possibly a newline and a carriage return sequence on some systems).

So the following:



this is some
text






looks like this in terms of characters in the file (\n is a newline character):


\nthis is some\ntext


If I now overwrite from pos 0 with the word "hello", it becomes:


hello is some\ntext


which in an editor is shown as:


hello is some
text






Quote:
Original post by JamesCobras
Apart from reading the whole file editing it in memory and re-writing the whole thing back to file (this seems a bit excessive), is there any way i can fill the empty line at the top of the file?


There is no empty line at the top of the file. There is (maybe some spaces then) a newline character at the start of the file.

James - load the entire file into memory using an ios::in file stream, then close the stream, then reopen the file with an ios::out file stream, then write your custom header to this stream, then write the previously loaded input file to the stream (or even better use std::ifstream and std::ofstream then you can't accidentally muddle up your read/write operations on the wrong stream).

This is not excessive - this is how these things are done.

[EDIT] And while I was typing this, rip-off has provided the code to do just that.

Share this post


Link to post
Share on other sites
Oh, kk, yeah thanks guys yep missing that iso::in was the killer.
Wow, i thought because I was only outputing that it wasn't needed.

Also i quite like the array idea to get things sorted and exact.

Thanks Guys, your help is appreciated.

James

Share this post


Link to post
Share on other sites
Have your template in file: template.txt.
Let your real file be: real.html

sed '1r template.txt' < real.html > newReal.html


This will insert contents of template.txt after first line, producing newReal.html.

Share this post


Link to post
Share on other sites
Quote:
Original post by Antheus
Have your template in file: template.txt.
Let your real file be: real.html

sed '1r template.txt' < real.html > newReal.html


This will insert contents of template.txt after first line, producing newReal.html.


That's all well and good if you're on a unix-like system that includes sed...

Share this post


Link to post
Share on other sites
Quote:
Original post by Zahlman

That's all well and good if you're on a unix-like system that includes sed...


Well....

Exactly the same as distribution problems as writing one's own C++ application, except that sed is already available, tested, documented and google friendly.

Share this post


Link to post
Share on other sites
Hi guys, if you care i have the finished product here!

code:

#include <string>
#include <vector>
#include <fstream>





std::vector<std::string> ConfigFile;

std::vector<std::string> read(const std::string &filename); //base for read operations
void write(const std::string &filename, const std::vector<std::string> &data); //base for write operations




void ParseConfigFile (std::vector<std::string> ConfigFile);//proccessing config file (file addresses and header stuff)
std::vector<std::string> filefiddle (const std::string &pstring, std::vector<std::string> data);
//moves all stuff down a line and places string in top of the file.


int main ()
{
ConfigFile = read("HtmlHeader.txt");//Read Config into memory

ParseConfigFile(ConfigFile);//Main function for dealing with the config file


return 0;
}



std::vector<std::string> read(const std::string &filename)
{
std::vector<std::string> data;
std::ifstream inputfile(filename.c_str());
std::string fileline;
while(std::getline(inputfile, fileline))
{
data.push_back(fileline);
}
return data;
}

void write(const std::string &filename, const std::vector<std::string> &data)
{
std::ofstream outfile(filename.c_str());
std::copy(data.begin(), data.end(), std::ostream_iterator<std::string>(outfile, "\n"));
}



std::vector<std::string> filefiddle (const std::string &pstring, std::vector<std::string> data)
{
std::vector<std::string> temp;
std::vector<std::string>::const_iterator i;
int a = 0;//secondary counter

temp.push_back("");//enlargening the vector, very poor way of doing it but hay if it works and it's fine....
for (i = data.begin(); i != data.end(); ++i)
{
temp.push_back(data[a]);//flick everything back one slot in the vector
++a;
}
temp[0] = pstring;
return temp;
}

void ParseConfigFile (std::vector<std::string> Config)
{

std::vector<std::string>::const_iterator itr;
std::vector<std::string> code;

int a = 0;

for (itr = Config.begin(); itr != Config.end(); itr = itr + 2)
{
code = read(Config[a]);//get file address and read the file into memory

code = filefiddle (Config[a+1],code);////get the stuff that needs to go in the file and place it at the top of the file

write(Config[a],code); // when processed ship it out


a =a +2;//secondary counter
}
}



The stuff i haven't done is error checking, i'll work on that now and i have a bad practice in the way i expand the vector, i need to offset it but i'm not sure i do it in the best/ most perfect way.

Anyway, thanks

James

Share this post


Link to post
Share on other sites
vector has an insert function.

Something like:
lines.insert(lines.begin(), 3, "I've been inserted");


Alternatively, to insert multiple lines:
std::vector<std::string> lines;
std::vector<std::string> to_insert;

lines.insert(lines.begin() + 3, to_insert.begin(), to_insert.end());


My syntax might be a bit off.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this