How to change one line with another in C ++

My goal is to find a specific line in a file, modify it and save the file in C ++. I am trying to do this via fstream, for example as shown below

fstream someFile(fileName, filestream::ate | filestream::in | filestream::out); streampos endPos = someFile.tellg(); streampos.seekg(0, fstream::beg); string line; while(someFile && someFile.tellg() != endPos && getline(somefile, line)) { if(correctLine(line)) { modifyLine(line); //save Line.. How do i do this? } } 

My question is how to replace the string obtained from getline function with a modified string? Also, after getline, will the file position be at the beginning of the current line or will it be placed at the end of the current line?

+2
c ++
source share
5 answers

@harper is true that you cannot just change the file if the new line is no different from the old one. What you can do is read stringstream and then write stringstream back, completely overwriting the file with the new version.

Here is an example of replacing one line from another question . In your case, you just need to do something more complex than line == target , but this is basically the same.

Note that this reads the entire file into memory, so it will use large memory if the file is large.

 #include <fstream> #include <iostream> #include <sstream> int main(int argc, char** argv) { /* Accept filename, target and replacement string from arguments for a more useful example. */ if (argc != 4) { std::cout << argv[0] << " [file] [target string] [replacement string]\n" << " Replaces [target string] with [replacement string] in [file]" << std::endl; return 1; } /* Give these arguments more meaningful names. */ const char* filename = argv[1]; std::string target(argv[2]); std::string replacement(argv[3]); /* Read the whole file into a stringstream. */ std::stringstream buffer; std::fstream file(filename, std::fstream::in); for (std::string line; getline(file, line); ) { /* Do the replacement while we read the file. */ if (line == target) { buffer << replacement; } else { buffer << line; } buffer << std::endl; } file.close(); /* Write the whole stringstream back to the file */ file.open(filename, std::fstream::out); file << buffer.str(); file.close(); } 

Note. If you want to be smart, you could leave this file alone until you find a replacement string, and then replace it only after that.

+2
source share

A line is a thing of unknown length. Therefore, you cannot just replace it. You will need to have a source file stream and a target file stream. When copying a stream, you can replace any desired line.

+4
source share

If two lines are the same length , you can simply go back to the position to start with it and write the line again. I think the code would look something like this:

 // we need to go back 1 extra to take the line break into account someFile.seekp(-line.size() - 1, std::ios_base::cur); someFile.write(line.data(), line.size()); 
+3
source share

If you use memory mapped files, you can reduce the actual disk access (write access). If the corresponding line is in the β€œlater” blocks, you can avoid (re) recording any of the previous blocks.

Following code

  • uses boost to not be platform dependent.
  • allows replacement that is shorter and longer than the matched target string
  • resizes file in place
  • demonstrates string search by regular expression
  • pretty well tested (on linux) before posting it here.

See inline comments for rationale. The code:

 #include <boost/iostreams/device/mapped_file.hpp> #include <boost/regex.hpp> #include <boost/filesystem.hpp> using namespace boost; const auto MAX_INCREASE = 1024; // allow 1 kilobyte room to grow void process( filesystem::path const& spec, std::string const& pattern, std::string const& replace) { // get current size of file on disk auto const cur_size = file_size(spec); // map, with MAX_INCREASE room to spare iostreams::mapped_file mmap( spec.native(), iostreams::mapped_file::readwrite, cur_size+MAX_INCREASE); // find the line matching 'pattern' char *bof = mmap.data(); char *eof = bof + cur_size; // don't read beyond cur_size! regex re(pattern); match_results<char*> match; if (regex_search(bof, eof, match, re)) { // replace the matched contents! auto b = match[0].first, e = match[0].second; std::cout << "Matching line: '" << std::string(b, e) << "'\n"; // figure out whether we need to grow/shrink auto delta = (b + replace.size()) - e; std::cout << "Delta: " << delta << "\n"; if (delta < 0) { // shrinking std::copy(replace.begin(), replace.end(), b); // replacement std::copy(e, eof, e + delta); // shift back resize_file(filesystem::path(spec), cur_size + delta); } else if (delta < MAX_INCREASE) { // growing resize_file(filesystem::path(spec), cur_size + delta); std::copy_backward(b, eof, eof + delta); // shift onwards std::copy(replace.begin(), replace.end(), b); // insert replacement } else { // error handling (MAX_INCREASE exceeded) } } // TODO error handling (no match)? } int main() { process("input.txt", "^int .*?$", "void foo()\n// mmap was here"); //process("input.txt", "^int .*?$", ""); } 
0
source share

First, position the pointer on the desired position to begin the replacement. fileobj.seekp (fileobj.tellp (-lengthOfLine, IOS :: current), IOS :: back);

then just write a line.

Fileobj <LineString;

If the strings have different sizes, check brendan answer

-2
source share

All Articles