0

I am looking for a way to search for a specific character in a text file and add a new-line after every ;

It's a pretty big document (2,7MB) if that matters.

Adam Liss
  • 45,068
  • 11
  • 101
  • 142
My511
  • 31
  • 2
  • 2
    What you are waiting for? Should someone write the complete program and put it here? This site is for Q&A related to programming and not the "do my job" ;) So please show us what you have already programmed and what is the problem you need our help for. Requesting a complete program is not what we expect here on SO! – Klaus Dec 30 '18 at 14:05
  • 5
    "Inserting" a character into an existing text-file can't really be done, since you must move the text after the insertion-point, and for a file that's non-trivial. The easiest way is to rewrite the file from scratch, inserting the new characters whenever needed. Either using temporary file and file renames, or by loading it all into memory. – Some programmer dude Dec 30 '18 at 14:07
  • @Someprogrammerdude Would it be safe/wise to read it all into a single `std::string`? Considering it is a rather large text file? – Ayxan Haqverdili Dec 30 '18 at 14:11
  • 2
    Unless you're on a very small system with limited memory, it should be safe. I recommend you preallocate memory for the string first though, if you go that way. Otherwise I'd recommend a vector of strings, where each string is the "line" that should be written, i.e. you read the semicolon-separated strings. It will make the insertion of the newlines very trivial when you write the data back out. – Some programmer dude Dec 30 '18 at 14:17
  • 1
    Sounds like a job for `sed` or `awk`. – Eljay Dec 30 '18 at 15:00
  • ***Would it be safe/wise to read it all into a single std::string? Considering it is a rather large text file?*** At work I read text files that are 500 to 1000 times as large into a `std::string`. – drescherjm Dec 30 '18 at 15:17
  • @drescherjm And what is the best way to read an entire file into an `std::string`? Does it matter (from a performance point of view) if I read it a single character at a time in a while loop? – Ayxan Haqverdili Dec 30 '18 at 15:20
  • @Ayxan see [What is the best way to read an entire file into a std::string in C++?](https://stackoverflow.com/questions/116038/) – Remy Lebeau Dec 30 '18 at 17:47
  • @RemyLebeau awesome, but wouldn't `input >> sstr.rdbuf()` miss every blank space in between words? – Ayxan Haqverdili Dec 30 '18 at 18:22
  • 1
    @Ayxan no, because [that overload of `operator>>`](https://en.cppreference.com/w/cpp/io/basic_istream/operator_gtgt) (see #11) performs **unformatted** reading. It will read the input stream as-is until it reaches the end of the stream or an error occurs. As such, the loop in the example you are looking at is unnecessary. – Remy Lebeau Dec 30 '18 at 18:47

2 Answers2

1

As SomeProgrammerdude points out in the comments, inserting into an existing file isn't really feasible and one of your options is to read it all into a single std::string and write back into the same text file.
Here is an example implementation. We read a charter at a time and check if it is a semicolon, and add a newline after every semicolon. Notice the reserve part, although advised, it is not mandatory:

#include <string>
#include <fstream>
#include <stdexcept>

int main()
{
  std::fstream f("test.txt", std::ios::in);

  if (!f.is_open()) {
    throw std::runtime_error("Failed to open file");
  }

  std::string contents;
  contents.reserve(10000); // reserve some space for better efficiency
  char ch;
  while (f.get(ch)) {
    contents.push_back(ch);
    if (ch == ';')
      contents.push_back('\n');
  }

  f.close();
  f.open("test.txt", std::ios::out);
  if (!f.is_open()) {
    throw std::runtime_error("Failed to open file");
  }
  f << contents;
  f.close();
  return 0;
}

Input:

line line line line; line line line line; line line line; line line

Output:

line line line line;
 line line line line;
 line line line;
 line line

Note the leading spaces before lines. This is because we appended '\n' after every ';', did not replace the spaces.

Ayxan Haqverdili
  • 17,764
  • 5
  • 27
  • 57
  • You are not initializing `ch` before calling `push_back(ch)`, as you are missing an initial call to `f.get()` before entering the loop (see [Why is iostream::eof inside a loop condition considered wrong?](https://stackoverflow.com/questions/5605125/)). You should replace `while (!f.eof())` with `while (f.get(ch))`, and get rid of the `f.get()` call inside the loop. – Remy Lebeau Dec 30 '18 at 17:56
  • @RemyLebeau Thanks for the feedback! Updated the answer. Any further suggestions? – Ayxan Haqverdili Dec 30 '18 at 18:15
  • you are not checking if the second `open()` succeeds or fails. And, if you are going to use exception handling, consider using the fstream's [`exceptions()`](https://en.cppreference.com/w/cpp/io/basic_ios/exceptions) method instead. – Remy Lebeau Dec 30 '18 at 18:39
1

You can't insert new characters into the middle of an existing file, only append to the end. You will have to create a new file, copying characters from the old file to the new file, inserting new characters as needed. For example:

#include <string>
#include <fstream>

int main()
{
    std::ifstream inFile("input.txt");
    if (!inFile.is_open())
    {
        std::cerr << "Failed to open input file";
        return 1;
    }

    std::ofstream outFile("output.txt");
    if (!outFile.is_open())
    {
        std::cerr << "Failed to create output file";
        return 1;
    }

    std::string line;
    while (std::getline(inFile, line, ';'))
    {
        outFile << line;
        if (!inFile.eof())
            outFile << ";\n";
    }

    return 0;
}
Remy Lebeau
  • 454,445
  • 28
  • 366
  • 620