Parsing a text file C++ from specific line to specific line - c++

so, I`m new to c++. My task is parse text file that look like:
RE002%%
RE002%%
RE002%%
RE002%%
RE002%%
RE004%on%
$GPGGA,124749.80,5543.3227107,N,03739.1366738,E,1,08,1.11,147.9635,M,14.4298,M,,*5C
$GPGSV,3,1,10,27,13,078,43,05,31,307,48,16,24,042,43,02,10,267,43*7D
$GPGSV,3,2,10,26,03,031,36,07,75,215,51,09,57,121,53,30,40,234,50*76
$GPGSV,3,3,10,23,29,117,46,04,36,114,46*70
$GPGGA,124749.90,5543.3227105,N,03739.1366737,E,1,08,1.11,147.9664,M,14.4298,M,,*54
RE005%off%
And it continuous for few thousand lines.I need to find where it writes RE004%on% and start processing lines in this loop until it finds RE005%off% and do it over and over until it file ends. I was trying to do it with line.find, but I am pretty sure it is wrong way to solve this problem
#include <iostream>
#include <fstream>
#include <string>
#include <stdlib.h>
using namespace std;
int main() {
string line, dollar, star, Checksum;
float *t0 = NULL;
int tount = 0;
int k;
ifstream logs_("C:/Users/Olya/Desktop/LogGLO.txt");
ofstream tout("outLOGTime.txt");
ofstream pout("outLOGPot.txt");
if (logs_.is_open())
{
while(getline(logs_,line))
{
line.find("RE004%on%")
k = 0;
if
dollar = line.find_first_of('$');
star = line.find_first_of('*');
Checksum = line.substr(line, dollar, star - dollar);
for (size_t i = 0; i < Checksum.size(); i++)
{
}
if (line.substr(0,6) == "$GPGSV")
{
for (size_t i = 0, N = 7; i < line.size(); i++)
{
if (line[i] == ',') k++;
if(k == N)
{
pout << line.substr(i+1,2) << endl;
if ((N += 4) > 19) break;
}
}
}
logs_.close();
}
}
else
cout<<"File is not open"<<'\n';
tout.close();
pout.close();
return 0;
}

Unfortunately your description si very unclear. Also by reading your code, I can really not understand, what you intent to do. And you edited your text and changed description. Not so easy for me
But, I made an educated guess. . .
I read all data between your given delimiters, validate the checksum and split the lines into tokens. Finally I store all the lines-with-Tokens in a vector. Then I filter for a specific value and output a column.
Please stude and try to understand. It is not so complicated.
Thank you
#include <iostream>
#include <regex>
#include <vector>
#include <iterator>
#include <string>
#include <utility>
#include <algorithm>
#include <functional>
#include <numeric>
#include <fstream>
const std::regex re{ R"(\$(.*)\*[abcdefABCDEF\d]{2})" };
const std::regex delimiter{ "," };
using Tokens = std::vector<std::string>;
std::tuple<bool, Tokens> checkString(const std::string& str) {
// Return value of the function. Assume that string is not ok
std::tuple<bool, std::vector<std::string>> result(false, {});
// We want to find a string in the given format
std::smatch sm{};
if (std::regex_match(str, sm, re)) {
// OK, found. Validate checksum
if (std::string s = sm[1];std::stoul(str.substr(str.size() - 2), nullptr, 16) == std::accumulate(s.begin(), s.end(), 0U, std::bit_xor<unsigned char>())) {
// Tokenize string
Tokens tokens(std::sregex_token_iterator(str.begin(), str.end(), delimiter, -1), {});
// Build return value
result = std::make_tuple(true, std::move(tokens));
}
}
return result;
}
int main() {
std::vector<Tokens> csvData{};
// Open file and check if it is open
if (std::ifstream logs("r:\\LogGLO.txt"); logs) {
// Shall we process text lines or not
bool processingActive{ false };
// Read all lines of files
for (std::string line{}; std::getline(logs, line);) {
// Check, if we should start or stio processing of the lines
if (line.substr(0, 9) == std::string("RE004%on%")) processingActive = true;
if (line.substr(0, 10) == std::string("RE005%off%")) processingActive = false;
// Check and read csv data
if (processingActive) {
const auto [ok, data] = checkString(line);
if (ok) csvData.push_back(std::move(data));
}
}
}
// So, now we have read all csv data
// Show eight column of GPGSV data
for (const Tokens& t : csvData)
if (t[0] == "$GPGSV")
std::cout << t[7] << "\n";
return 0;
}

Related

How to replace specific numbers by another number in a text file (C++)

First, I want to mention that I'm completely new to C++, so it's possible that I've overlooked any obvious solutions.
My task is to replace any non-zero number with 1.
The file looks like this:
Some text
0;0;0;0;0;0.236223;0;0;0;0;0;0.312757;0;0;0;0;0;0;0;0;0;0.367119;... (multiple lines)
and should turn into:
Some text
0;0;0;0;0;1;0;0;0;0;0;1;0;0;0;0;0;0;0;0;0;1,... (multiple lines)
My idea was to start out with a string replacement code. I tried the following:
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
ifstream filein( "input.txt" );
ofstream fileout( "output.txt" );
string toreplace = "0";
string replacewith = "1";
string text;
while ( getline( filein, text ) )
{
for ( size_t p = text.find( toreplace ); p != string::npos;
p = text.find( toreplace , p ) )
text.replace( p, toreplace.length(), replacewith );
fileout << text << endl;
}
return 0;
}
This gives back:
1;1;1;1;1;1.236223;1;1;1;1;1;1.312757;1;1;1;1;1;1;1;1;1;1.367119,...
which is the opposite of what I want (not entirely). So, I thought it would be easy to declare toreplace and replacewith as floats and use != 0, which didn't work, especially because I can't define text as float because it contains ";". (Do I need to remove this delimiter? I would still need it in the final text file.)
The other problem in that code is that it replaces every zero, which includes "0.236223" turning into "1.236223". I assume this wouldn't matter when eventually working with floats instead of strings.
Is this the right approach to the given task, or would it be better to do it a different way? Thanks for any help given.
EDIT: There was a ";" at the end of each line, which I didn't want and used the string::pop_back() function to resolve the problem.
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
ifstream filein("input.txt");
ofstream fileout("output.txt");
string toreplace = "0";
string replacewith = "1";
string text, new_text="";
while (getline(filein, text))
{
new_text = "";
double num;
while (sscanf_s(text.c_str(), "%lf;", &num))
{
if (num)
new_text += "1;";
else
new_text += "0;";
while (text.length() && text.at(0) != ';')
{
text.erase(0,1);
}
text.erase(0,1);
if (text.empty())
break;
}
fileout << new_text << endl;
}
return 0;
}
If you are able to simplify your input file the solution can be one-liner.
#include <iostream>
#include <iterator>
#include <algorithm>
#include <fstream>
int main()
{
std::ifstream filein("./data/input.txt"); // Contains 1. 2.3 0. 3.2
std::ofstream fileout("./data/output.txt"); // Should contain 1. 1. 0. 1.
std::replace_copy_if(std::istream_iterator<double>(filein), std::istream_iterator<double>(), std::ostream_iterator<double>(fileout," "),
[](auto v) {return v != 0.; }, 1.);
return EXIT_SUCCESS;
}
If you have to stick with the ';' delimiter it can be done with a two-liner:
#include <iostream>
#include <sstream>
#include <iterator>
#include <algorithm>
#include <fstream>
int main()
{
std::ifstream filein("./data/input.txt"); // Contains 1. 2.3 0. 3.2
std::ofstream fileout("./data/output.txt"); // Should contain 1. 1. 0. 1.
std::stringstream replaced;
std::replace_copy_if(
std::istream_iterator<char>(filein),
std::istream_iterator<char>(),
std::ostream_iterator<char>(replaced),
[](auto v) { return v == ';'; },
' ');
std::replace_copy_if(
std::istream_iterator<double>(replaced),
std::istream_iterator<double>(),
std::ostream_iterator<double>(fileout,";"),
[](auto v) {return v != 0.; },
1.);
return EXIT_SUCCESS;
}
And, just to be complete. The solution with a regex. The regex gives you the ability to specify more precisely what you want to search:
And the result is also an one-liner (one Statement)
Please see:
#include <iostream>
#include <sstream>
#include <regex>
#include <iterator>
const std::string testData{"0;0;0;0 ; 0;0.236223;0;0;0;0;0;0.312757;0;0;0;0;0;0;0;0;0;0.367119"};
// This is, what we would like to match. Any number, int or double
const std::regex re{"([-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?)"};
int main()
{
std::transform(
std::sregex_token_iterator(testData.begin(), testData.end(), re, 1),
std::sregex_token_iterator(),
std::ostream_iterator<int>(std::cout,";"),
[](const std::string& s) -> int { double d=stod(s); return d==0.0 ? 0 : 1; }
);
return 0;
}
If you do not want to have the trailing ';' then you can use.
std::experimental::make_ostream_joiner(std::cout,";"),
instead of the std::ostream_iterator.

How to start reading file from a particular position c++

I am reading a file using fstream and getline functions. I want to give a starting position e.g. my file has 13 lines I want to start reading it from 7th line for example. Here is my code:
#include<iostream>
#include <stdlib.h>
#include <string>
#include <vector>
#include<iterator> // for iterators
#include<map>
using namespace std;
int main()
{
string line;
int start= 7;
unsigned long int index;
For( int z=1; z<=13; z++){
if (f_node.is_open())
{
getline(f_node, line);
if ((line.find("$EndNodes") != string::npos))
{
cout << "$EndNodes found file closed .... " << endl;
f_node.close();
return false;
}
// Point index.
int i = 0;
int j = line.find_first_of(" ", i);
index = strtoul((line.substr(i, j)).c_str(), NULL, 0);//
}
}
I am reading only indexes and I want to start it from 7th index How to do it?
To discard some number of lines, something like:
#include <fstream>
#include <string>
int main() {
std::ifstream infile{"myfile.txt"};
std::string line;
int starting_line = 7;
// Read and discard beginning lines
for (int n = 1; n < starting_line; n += 1) {
if (!std::getline(infile, line)) {
// Error or premature end of file! Handle appropriately.
}
}
while (std::getline(infile, line)) {
// Do something with the lines you care about.
}
return 0;
}
Except with actual error checking and handling and such.
"there is no way to tell code the starting position like seekg and tellg?" No. NL is just like any other character, it does not receive any special treatment.
You simply must scan the stream, counting the new-line character:
std::istream& seek_line(std::istream& is, const int n, std::ios_base::seekdir way = std::ios_base::beg)
{
is.seekg(0, way);
int i = 0;
char c;
while (is.get(c) && i < n)
if (c == '\n')
++i;
is.putback(c);
return is;
}
And this is how you use the above function:
int main()
{
using namespace std;
ifstream is{ "c:\\temp\\test.txt" };
if (!is)
return -1;
if (!seek_line(is, 3))
return -2;
string s;
getline(is, s);
cout << s << endl;
return 0;
}

C++ Problem with space detection in an Array String

I'm currently writting a program where I try to filter extra spaces so if there are more than 1 spaces in a row, I discard the rest leaving only one
But this is only the first step because the aim of the program is to parse a txt file with mips assembly instructions.
So far I've opened the file, stored the content in a vector and then stored the vector content in an array. Then I check, if you find a char 2 times in a row shift the array to the left.
The problem is that the code works well for any other letter, except for the space character. (On the code below I test it with the 'D' character and it works)
#include <iostream>
#include <cmath>
#include <fstream>
#include <cstdlib>
#include <vector>
#include <algorithm>
using namespace std;
class myFile {
vector<string> myVector;
public:
void FileOpening();
void file_filter();
};
void myFile::FileOpening() {
string getcontent;
ifstream openfile; //creating an object so we can open files
char filename[50];
int i = 0;
cout << "Enter the name of the file you wish to open: ";
cin.getline(filename, 50); //whatever name file the user enters, it's going to be stored in filename
openfile.open(filename); //opening the file with the object I created
if (!openfile.is_open()) //if the file is not opened, exit the program
{
cout << "File is not opened! Exiting the program.";
exit(EXIT_FAILURE);
};
while (!openfile.eof()) //as long as it's not the end of the file do..
{
getline(openfile, getcontent); //get the whole text line and store it in the getcontent variable
myVector.push_back(getcontent);
i++;
}
}
void myFile::file_filter() {
unsigned int i = 0, j = 0, flag = 0, NewLineSize, k, r;
string Arr[myVector.size()];
for (i = 0; i < myVector.size(); i++) {
Arr[i] = myVector[i];
}
//removing extra spaces,extra line change
for (i = 0; i < myVector.size(); i++) {
cout << "LINE SIZE" << myVector[i].size() << endl;
for (j = 0; j < myVector[i].size(); j++) {
//If I try with this character for example,
//it works (Meaning that it successfully discards extra 'Ds' leaving only one.
// But if I replace it with ' ', it won't work. It gets out of the loop as soon
//as it detects 2 consecutive spaces.
if ((Arr[i][j] == 'D') && (Arr[i][j + 1] == 'D')) {
for (k = j; k < myVector[i].size(); k++) {
Arr[i][k] = Arr[i][k + 1];
flag = 0;
j--;
}
}
}
}
for (i = 0; i < myVector.size(); i++) {
for (j = 0; j < myVector[i].size(); j++) //edw diapernw tin kathe entoli
{
cout << Arr[i][j];
}
}
}
int main() {
myFile myfile;
myfile.FileOpening();
myfile.file_filter();
}
My question is, why does it work with all the characters except the space one, and how do I fix this?
Thanks in advace.
Wow. Many lines of code. I can only recomend to learn more about the STL and algorithms.
You can read the complete file into a vector using the vectors "range"-constructor and std::istream_iterator. Then you can replace one or more spaces in a string by using a std::regex. This is really not complicated.
In the below example, I do all the work, with 2 lines of code in function main. Please have a look:
#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
#include <string>
#include <fstream>
#include <regex>
using LineBasedTextFile = std::vector<std::string>;
class CompleteLine { // Proxy for the input Iterator
public:
// Overload extractor. Read a complete line
friend std::istream& operator>>(std::istream& is, CompleteLine& cl) { std::getline(is, cl.completeLine); return is; }
// Cast the type 'CompleteLine' to std::string
operator std::string() const { return completeLine; }
protected:
// Temporary to hold the read string
std::string completeLine{};
};
int main()
{
// Open the input file
std::ifstream inputFile("r:\\input.txt");
if (inputFile)
{
// This vector will hold all lines of the file. Read the complete file into the vector through its range constructor
LineBasedTextFile text{ std::istream_iterator<CompleteLine>(inputFile), std::istream_iterator<CompleteLine>() };
// Replace all "more-than-one" spaces by one space
std::for_each(text.begin(), text.end(), [](std::string& s) { s = std::regex_replace(s, std::regex("[\\ ]+"), " "); });
// For Debug purposes. Print Result to std::out
std::copy(text.begin(), text.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
}
return 0;
}
I hope, I could give you some idea on how to proceed.

How to read string from stdin until meet blank lines

Consider a simple program. It must take string from stdin and save to variable.
It is not stated how many lines of input will be taken, but program must terminate if meet newline.
For example:
stdin:
abc
abs
aksn
sjja
\n
I tried but it doesn't work. Here is my code:
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
// Constant
#define max 100000
struct chuoi
{
char word[10];
};
chuoi a[max];
void readStr()
{
int i=0;
while ( fgets(a[i].word, 10,stdin) != NULL)
{
if (a[i].word[0] == ' ') break;
a[i].word[strlen(a[i].word)-1] = '\0'; //replaced \n by \0
i++;
}
//length = i;
}
int main()
{
readStr();
return 0;
}
So, how to solve this problem?
One alternative here is to use std::getline to get each line. If the line is empty, or the input fails, then exit the loop.
void readStr()
{
std::string str;
while ( std::getline(std::cin, str) && str.length() )
{
// use the string...
}
}
Adding the std::getline and use of std::vector to your sample code, and keeping with the spirit of your original sample;
#include <string>
#include <iostream>
#include <vector>
const std::size_t Max = 100000;
struct chuoi
{
explicit chuoi(std::string const& str) : word(str)
{
}
std::string word;
};
void readStr(std::vector<chuoi>& a)
{
std::string str;
while ( std::getline(std::cin, str) && str.length() )
{
a.push_back(chuoi(str));
}
}
void writeStr(std::vector<chuoi> const& a)
{
for (auto i = a.begin(); i != a.end(); ++i) {
std::cout << i->word << std::endl;
}
}
int main()
{
std::vector<chuoi> a;
a.reserve(Max);
readStr(a);
writeStr(a);
return 0;
}
To solve you immediate problem, minimal changes in the code can be made as follows;
void readStr()
{
int i = 0;
while ( fgets(a[i].word, 10, stdin) != NULL)
{
a[i].word[strlen(a[i].word) - 1] = '\0'; // transform the end of line character to NULL
if (strlen(a[i].word) == 0) {
break;
}
i++;
}
}
If the standard input will always be used (stdin), the gets function can also be used;
while ( gets(a[i].word) != NULL)
{
if (strlen(a[i].word) == 0) {
break;
}
i++;
}
Notes;
fgets reads until the "enter" key on the stdin but includes the new line character
gets also reads until the return, but excludes the new line character
Both functions NULL terminate the input
Be careful of the form of gets it does not check for buffer overflow conditions
I would do something like this:
#include <string>
#include <iostream>
int main()
{
std::string line; // will contain each line of input
// Stop when line is empty or when terminal input has an error
while(std::getline(std::cin, line) && !line.empty())
{
// do stuff with line
}
}

C++ split string by line

I need to split string by line.
I used to do in the following way:
int doSegment(char *sentence, int segNum)
{
assert(pSegmenter != NULL);
Logger &log = Logger::getLogger();
char delims[] = "\n";
char *line = NULL;
if (sentence != NULL)
{
line = strtok(sentence, delims);
while(line != NULL)
{
cout << line << endl;
line = strtok(NULL, delims);
}
}
else
{
log.error("....");
}
return 0;
}
I input "we are one.\nyes we are." and invoke the doSegment method. But when i debugging, i found the sentence parameter is "we are one.\\nyes we are", and the split failed. Can somebody tell me why this happened and what should i do. Is there anyway else i can use to split string in C++. thanks !
I'd like to use std::getline or std::string::find to go through the string.
below code demonstrates getline function
int doSegment(char *sentence)
{
std::stringstream ss(sentence);
std::string to;
if (sentence != NULL)
{
while(std::getline(ss,to,'\n')){
cout << to <<endl;
}
}
return 0;
}
You can call std::string::find in a loop and the use std::string::substr.
std::vector<std::string> split_string(const std::string& str,
const std::string& delimiter)
{
std::vector<std::string> strings;
std::string::size_type pos = 0;
std::string::size_type prev = 0;
while ((pos = str.find(delimiter, prev)) != std::string::npos)
{
strings.push_back(str.substr(prev, pos - prev));
prev = pos + delimiter.size();
}
// To get the last substring (or only, if delimiter is not found)
strings.push_back(str.substr(prev));
return strings;
}
See example here.
#include <sstream>
#include <string>
#include <vector>
std::vector<std::string> split_string_by_newline(const std::string& str)
{
auto result = std::vector<std::string>{};
auto ss = std::stringstream{str};
for (std::string line; std::getline(ss, line, '\n');)
result.push_back(line);
return result;
}
#include <iostream>
#include <string>
#include <regex>
#include <algorithm>
#include <iterator>
using namespace std;
vector<string> splitter(string in_pattern, string& content){
vector<string> split_content;
regex pattern(in_pattern);
copy( sregex_token_iterator(content.begin(), content.end(), pattern, -1),
sregex_token_iterator(),back_inserter(split_content));
return split_content;
}
int main()
{
string sentence = "This is the first line\n";
sentence += "This is the second line\n";
sentence += "This is the third line\n";
vector<string> lines = splitter(R"(\n)", sentence);
for (string line: lines){cout << line << endl;}
}
We have a string with multiple lines
we split those into an array (vector)
We print out those elements in a for loop
Using the library range-v3:
#include <range/v3/all.hpp>
#include <string>
#include <string_view>
#include <vector>
std::vector<std::string> split_string_by_newline(const std::string_view str) {
return str | ranges::views::split('\n')
| ranges::to<std::vector<std::string>>();
}
Using C++23 ranges:
#include <ranges>
#include <string>
#include <string_view>
#include <vector>
std::vector<std::string> split_string_by_newline(const std::string_view str) {
return str | std::ranges::views::split('\n')
| std::ranges::to<std::vector<std::string>>();
}
This fairly inefficient way just loops through the string until it encounters an \n newline escape character. It then creates a substring and adds it to a vector.
std::vector<std::string> Loader::StringToLines(std::string string)
{
std::vector<std::string> result;
std::string temp;
int markbegin = 0;
int markend = 0;
for (int i = 0; i < string.length(); ++i) {
if (string[i] == '\n') {
markend = i;
result.push_back(string.substr(markbegin, markend - markbegin));
markbegin = (i + 1);
}
}
return result;
}