parsing text and output "time of appearance" of some lines [closed] - c++

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 3 years ago.
Improve this question
So I got file that look like:
$GPGGA,124613.90,5543.3221231,N,03739.1368442,E,1,15,0.69,147.0851,M,14.4298,M,,*54
$GPGSV,3,1,10,27,12,078,41,05,31,308,49,16,25,043,44,02,11,268,44*7E
$GPGSV,3,2,10,26,03,031,39,07,74,216,52,09,58,121,52,30,39,234,48*71
$GPGSV,3,3,10,23,30,116,46,04,37,114,47*79
$GLGSV,2,1,07,84,17,338,43,78,15,212,48,85,12,032,46,67,84,223,53*67
$GLGSV,2,2,07,77,67,195,47,76,50,047,54,66,32,144,52*5C
$GPGGA,124614.00,5543.3221239,N,03739.1368445,E,1,15,0.69,147.0864,M,14.4298,M,,*53
$GPGSV,3,1,10,27,12,078,41,05,31,308,49,16,25,043,43,02,11,268,44*79
$GPGSV,3,2,10,26,03,031,39,07,74,216,52,09,58,121,52,30,39,234,48*71
$GPGSV,3,3,10,23,30,116,46,04,37,114,47*79
$GLGSV,2,1,07,84,17,338,43,78,15,212,48,85,12,032,46,67,84,223,53*67
$GLGSV,2,2,07,77,67,195,47,76,50,047,54,66,32,144,52*5C
My cod is checking check sum of string and output some values in strings.
In $GPGGA line "124614.00" is time. 12 hours 46 minutes 14.00 sec. I need to output time of "appearance" $GPGSV lines. I`ve tried subtract first value and the following ones through the pointer, but I must have messed up somewhere.
#include <iostream>
#include <fstream>
#include <string>
#include <stdlib.h>
#include <numeric>
#include <cstdlib>
#include <cstring>
#include <stdio.h>
int checksum(const char* s) {
int c = 0;
while (*s)
c ^= *s++;
return c;
}
int main() {
char linec_h[200];
int k, key;
int* hour = NULL;
int* minute = NULL;
float* sec = NULL;
std::string line, key_s;
std::ifstream logs_("C:/Users/Olya/Desktop/broken.txt");
std::ofstream pout("C:/Users/Olya/Desktop/outLOG.txt");
if (logs_.is_open()) {
while (getline(logs_, line)) {
key_s = line.substr(line.length() - 2, 2);
key = strtol(key_s.c_str(), NULL, 16);
line = line.substr(1, line.length() - 4);
strcpy_s(linec_h, line.c_str());
if (key != checksum(linec_h))
pout << "Line is corrupted!" << std::endl;
else {
k = 0;
if (line.substr(0, 5) == "GPGGA") {
if (hour, minute, sec) {
*hour = stoi(line.substr(5, 2)) - *hour;
*minute = stoi(line.substr(7, 2)) - *minute;
*sec = stof(line.substr(9, 4)) - *sec;
}
else {
hour = new int;
minute = new int;
sec = new float;
*hour = stoi(line.substr(5, 2));
*minute = stoi(line.substr(7, 2));
*sec = stof(line.substr(9, 4));
}
} else if (line.substr(0, 5) == "GPGSV") {
for (size_t i = 0, SNR = 7, N = 4; i < line.size(); i++) {
if (line[i] == ',')
k++;
if (k == N) {
pout << "Satellite number -- " << line.substr(i + 1, 2) << " ";
if ((N += 4) > 16)
;
} else if (k == SNR) {
pout << "SNR -- " << line.substr(i + 1, 2) << " time -- " << hour
<< "." << minute << "." << sec << std::endl;
if ((SNR += 4) > 19)
break;
}
}
}
}
delete hour;
delete minute;
delete sec;
}
logs_.close();
std::cout << "Success" << std::endl;
} else
std::cout << "File is not open" << '\n';
pout.close();
return 0;
}

Just for the FUn of it. I created a complete solution which parses your GPS NMEA format completely and put all results in structs. So you can get ALL satellite data.
However. I show only the values that you used in your example.
I adapted my coding style to yours. In C++ I would do things completel different. Anyway.
Please find attached an complete example:
#include <string>
#include <ctime>
#include <cstring>
#include <iostream>
#include <fstream>
#include <iomanip>
constexpr size_t NumberOfFixQualityStrings = 9;
constexpr size_t NumberOfSatellitesPerGSVSentencePart = 4;
constexpr size_t MaxNumberOfPartsInSentence = 10;
constexpr size_t MaxTokensInSentence = 64;
constexpr size_t NumberOfFieldsInGGA = 12;
std::string fixQualityString[NumberOfFixQualityStrings]{
"invalid", "GPS fix (SPS)", "DGPS fix", "PPS fix", "Real Time Kinematic", "Float RTK",
"estimated (dead reckoning", "Manual input mode", "Simulation mode" };
// essential fix data which provide 3D location and accuracy data
struct GGA {
// Time of last satellite fix
unsigned int fixTimeInUtcHours{};
unsigned int fixTimeInUtcMinutes{};
unsigned int fixTimeInUtcSeconds{};
unsigned int fixTimeInUtcMilliSeconds{};
// Position: Lattitude
unsigned int lattitudeInDegree{};
double lattitudeInMinutes{};
std::string lattitideDirection{};
// Position: Longitude
unsigned int longitudeInDegree{};
double longitudeInMinutes{};
std::string longitudeDirection{};
// FixQuality // see dteails as string above
unsigned int fixQuality{};
std::string fixQualityString{};
// Number of satellites being tracked (can be more than shown in GSV, not all are beeing used for calculation)
unsigned int numberOfTrackedSatellites{};
// Horizontal dilution of position
double horizontalDilution{};
// Altitude, Meters, above mean sea level
double altitude{};
std::string altitudeDimension{};
// Height of geoid (mean sea level) above WGS84 ellipsoid
double goidHight{};
std::string goidHightDimension{};
};
// Detail information for satellites in satellit view (GSV)
struct SatelliteData {
std::string satellitePRNnumber{};
double elevationInDegress{};
double azimuthInDegrees{};
double snr{}; // signal noise ratio
};
// Part of a GSV sentence
struct GSVSentencePart {
size_t numberOfSentencesForFullData{};
size_t sentencePartNumber{};
size_t numberOfSatellitesInView{};
size_t numberOfSatellitesInThisPart{};
SatelliteData satelliteData[NumberOfSatellitesPerGSVSentencePart];
};
struct GSV
{
GSVSentencePart gsvSentencePart[MaxNumberOfPartsInSentence];
size_t numberOfParts{};
};
bool checksumTest(std::string& line) {
bool result{ false };
// Check, if there is a 2 digt checksum at the end and convert it to decimal
if (size_t pos{}, checkSumGiven{ std::stoul(line.substr(line.size() - 2), &pos, 16) }; pos == 2)
{
// Strip off checksum part
line = line.substr(1,line.size() - 4);
// Calculate checksum
unsigned char calculatedChecksum{ 0U }; for (const unsigned char c : line) calculatedChecksum ^= c;
// Get result
result = (calculatedChecksum == checkSumGiven);
}
return result;
}
// Split all strings into a tokens
size_t splitIntoTokens(std::string& s, std::string (&tokens)[MaxTokensInSentence]) {
// Number of converted tokens
size_t numberOfTokens{ 0 };
// First check checksum
if (checksumTest(s)) {
// Now split along each comma
for (size_t i{ 0U }, startpos{ 0U }; i < s.size(); ++i) {
// So, if there is a comma or the end of the string
if ((s[i] == ',') || (i == (s.size() - 1))) {
// Copy substring
tokens[numberOfTokens++] = s.substr(startpos, i - startpos);
startpos = i + 1;
}
}
}
return numberOfTokens;
}
GGA convertStringToGGA(std::string& s) {
GGA gga;
// Split string into tokens and check, if it worked
if (std::string tokens[MaxTokensInSentence]; splitIntoTokens(s, tokens) > NumberOfFieldsInGGA && tokens[0] == "GPGGA") {
gga.fixTimeInUtcHours = std::stoul(tokens[1].substr(0, 2));
gga.fixTimeInUtcMinutes = std::stoul(tokens[1].substr(2, 2));
gga.fixTimeInUtcSeconds = std::stoul(tokens[1].substr(4, 2));
gga.fixTimeInUtcMilliSeconds = std::stod(tokens[1].substr(6, 2))*1000.0;
gga.lattitudeInDegree = std::stoul(tokens[2].substr(0, 2));
gga.lattitudeInMinutes = std::stod(tokens[2].substr(2));
gga.lattitideDirection = tokens[3];
gga.longitudeInDegree = std::stoul(tokens[4].substr(0, 2));
gga.longitudeInMinutes = std::stod(tokens[4].substr(2));
gga.longitudeDirection = tokens[5];
gga.fixQuality = std::stoul(tokens[6]);
gga.fixQualityString = (gga.fixQuality < NumberOfFixQualityStrings) ? fixQualityString[gga.fixQuality] : fixQualityString[0];
gga.numberOfTrackedSatellites = std::stoul(tokens[7]);
gga.horizontalDilution = std::stod(tokens[8]);
gga.altitude = std::stod(tokens[9]);
gga.altitudeDimension = tokens[10];
gga.goidHight = std::stod(tokens[11]);
gga.goidHightDimension = tokens[12];
}
return gga;
}
GSVSentencePart convertToGSVSentencePart(std::string& s) {
GSVSentencePart gsvsp;
// Split string into tokens and check, if it worked
std::string tokens[MaxTokensInSentence];
if (size_t numberOfCOnvertedTokens = splitIntoTokens(s, tokens); numberOfCOnvertedTokens > 0 && tokens[0] == "GPGSV") {
gsvsp.numberOfSentencesForFullData = std::stoul(tokens[1]);
gsvsp.sentencePartNumber = std::stoul(tokens[2]);
gsvsp.numberOfSatellitesInView = std::stoul(tokens[3]);
gsvsp.numberOfSatellitesInThisPart = 0;
for (size_t currentToken = 4; currentToken < numberOfCOnvertedTokens; currentToken += 4) {
gsvsp.satelliteData[gsvsp.numberOfSatellitesInThisPart].satellitePRNnumber = tokens[currentToken];
gsvsp.satelliteData[gsvsp.numberOfSatellitesInThisPart].elevationInDegress = stod(tokens[currentToken + 1]);
gsvsp.satelliteData[gsvsp.numberOfSatellitesInThisPart].azimuthInDegrees= stod(tokens[currentToken + 2]);
gsvsp.satelliteData[gsvsp.numberOfSatellitesInThisPart].snr = stod(tokens[currentToken + 3]);
++gsvsp.numberOfSatellitesInThisPart;
}
}
return gsvsp;
}
std::string calculateElapsedTime(const GGA& previousGGA, const GGA& nextGGA) {
std::tm tmPrevious{}, tmNext{};
tmPrevious.tm_year = 100; tmPrevious.tm_mon = 1; tmPrevious.tm_mday = 1;
tmNext.tm_year = 100; tmNext.tm_mon = 1; tmNext.tm_mday = 1;
tmPrevious.tm_hour = previousGGA.fixTimeInUtcHours;
tmPrevious.tm_min = previousGGA.fixTimeInUtcMinutes;
tmPrevious.tm_sec = previousGGA.fixTimeInUtcSeconds;
std::time_t previousTime = std::mktime(&tmPrevious);
tmNext.tm_hour = nextGGA.fixTimeInUtcHours;
tmNext.tm_min = nextGGA.fixTimeInUtcMinutes;
tmNext.tm_sec = nextGGA.fixTimeInUtcSeconds;
std::time_t nextTime = std::mktime(&tmNext);
double diff = std::difftime(nextTime, previousTime);
diff = diff + 1.0*nextGGA.fixTimeInUtcMilliSeconds/1000.0- 1.0*previousGGA.fixTimeInUtcMilliSeconds/1000.0;
return std::to_string(diff);
}
int main() {
// Open file and check, if it is open
if (std::ifstream nmeaFile("r:\\log.txt"); nmeaFile) {
GGA previousGGA;
GGA nextGGA;
GSV gsv;
size_t state{ 0 };
for (std::string line{}; std::getline(nmeaFile, line); ) {
switch ( state) {
case 0: // wait for first GGA data
if (line.substr(0, 6) == "$GPGGA") {
previousGGA = nextGGA;
nextGGA = convertStringToGGA(line);
state = 1;
gsv = {};
}
break;
case 1: // wait for GSV
if (line.substr(0, 6) == "$GPGSV") {
gsv.gsvSentencePart[gsv.numberOfParts] = convertToGSVSentencePart(line);
if (gsv.gsvSentencePart[gsv.numberOfParts].numberOfSentencesForFullData ==
gsv.gsvSentencePart[gsv.numberOfParts].sentencePartNumber) {
state = 0;
++gsv.numberOfParts;
// Now all data are available in reable and structed format.
// You can do, what you want with them
// For example, we can print all Satellite Data:
size_t counter{ 0 };
for (size_t i = 0; i < gsv.numberOfParts; ++i) {
for (size_t j = 0; j < gsv.gsvSentencePart[i].numberOfSatellitesInThisPart; j++) {
std::cout << "Satellite: " << std::setw(2) << ++counter << " Satellite name: " <<
std::setw(3) << gsv.gsvSentencePart[i].satelliteData[j].satellitePRNnumber <<
" SNR: " << std::setw(8) << gsv.gsvSentencePart[i].satelliteData[j].snr <<
" Elapsed time: "<< calculateElapsedTime(previousGGA, nextGGA)<< " s\n";
}
}
--gsv.numberOfParts;
}
++gsv.numberOfParts;
}
break;
}
}
}
return 0;
}

I can see bugs like if (hour, minute, sec) { and many C-Style code, operating with pointers or so. I do not want to debug you code.
As a samll hint for you, I created a parser that reads all source lines, splits tem into tokens and checks the checksum.
Only a few lines of code will do the trick. From that on you can develop further.
#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:\\log.txt"); logs) {
// Read all lines of files
for (std::string line{}; std::getline(logs, line);) {
if (const auto& [ok, data] = checkString(line); ok) {
csvData.push_back(std::move(data));
}
else {
std::cerr << "**** Coruppted: " << line << "\n";
}
}
}
// So, now we have read all csv data
// Show eight column of GPGSV data
for (const Tokens& t : csvData) {
if (t[0] == "$GPGGA") {
std::cout << "$GPGGA -->" << t[1] << "\n";
}
else if (t[0] == "$GPGSV") {
std::cout << "$GPGSV -->" << t[4] << " " << t[7] << "\n";
}
}
return 0;
}
Of course there are many other possibilities . .

Related

Code ends before it actually should, I'm using std::time()

I'm writing a code that tries to paint all dots in graph correctly by randomly giving colors (according to some simple algorithm) while I still have time left. Correctly means that no two dots with same color are adjacent. Also every dot must have color distinct from the initial.
I noticed that in a simple test it gives wrong answer when I set time limit <=3 sec, but it doesn't work 3 seconds, it almost instantly throws "Impossible", here is part of the code (start, end and tl are global):
std::string new_paint;
bool success = false;
while (!success && end - start < tl) {
std::time(&end);
new_paint = TryPaint(edges, paint, success, v);
}
if (success) {
for (int i = 1; i < new_paint.size(); ++i) {
std::cout << new_paint[i];
}
} else {
std::cout << "Impossible";
}
Test is:
3 3
RGB
1 2
2 3
1 3
It means "3 dots with 3 edges, initial color RGB, edges between 1 2, 1 3 and 2 3"
Also I noticed that when i try to cout end - start it gives 6 in this test. I can't understand what is wrong can smn help?
Im using CLion, Cmake looks like this:
cmake_minimum_required(VERSION 3.21)
project(untitled1)
set(CMAKE_CXX_STANDARD 14)
add_executable(untitled1 main.cpp)
Here is full version of code:
#include <chrono>
#include <iostream>
#include <vector>
#include <set>
#include <random>
#include <algorithm>
time_t start, end;
const int tl = 20;
void check(std::vector<bool>& color, const std::string& paint, int n) {
if (paint[n] == 'R') {
color[0] = false;
} else if (paint[n] == 'G') {
color[1] = false;
} else {
color[2] = false;
}
}
std::string available_color(std::vector<bool>& color) {
std::string s;
if (color[0]) {
s += 'R';
}
if (color[1]) {
s += 'G';
}
if (color[2]) {
s += 'B';
}
return s;
}
std::string TryPaint(std::vector<std::set<int>>& edges, std::string paint, bool& success, int v) {
std::vector<bool> was(v + 1);
int count = 0;
std::vector<int> deck;
for (int i = 0; i < v; ++i) {
deck.push_back(i + 1);
}
std::random_shuffle(deck.begin(), deck.end());
while (count != v) {
auto now = deck[count];
std::vector<bool> color = {true, true, true};
check(color, paint, now);
// std::cout << now << '\n';
for (const auto& i : edges[now]) {
std::time(&end);
if (end - start >= tl) {
success = false;
return "";
}
if (was[i]) {
check(color, paint, i);
}
}
std::string choice = available_color(color);
// std::cout << choice << '\n';
if (choice.empty()) {
success = false;
return "";
} else {
++count;
was[now] = true;
char new_color = choice[0];
paint[now] = new_color;
}
}
success = true;
return paint;
}
int main(){
std::time(&start);
std::time(&end);
int v, e;
std::cin >> v >> e;
std::string paint;
std::cin >> paint;
paint = '#' + paint;
std::vector<std::set<int>> edges(v + 1);
for (int i = 0; i < e; ++i) {
int a, b;
std::cin >> a >> b;
edges[a].insert(b);
edges[b].insert(a);
}
std::string new_paint;
bool success = false;
while (!success && end - start < tl) {
std::time(&end);
new_paint = TryPaint(edges, paint, success, v);
// std::cout << "-------------------------------------------------\n";
}
if (success) {
for (int i = 1; i < new_paint.size(); ++i) {
std::cout << new_paint[i];
}
} else {
std::cout << "Impossible";
}
std::cout << '\n';
return 0;
}
Use difftime() to calculate the number of seconds between two time_t variables. The time_t is opaque and can contain different values on different systems according to the doc.

How should I parse and check through a string?

Okay so I have a C++ project I want to complete which involves receiving NMEA sentences and parsing through them to check if they conform to the "grammar rules" of NMEA.
The current method I have been doing this is using if statements but this is obviously not good coding practice.
What are other methods I could use to attempt to check string sentences for specific characters and groups of characters?
Let us assume that you have NMEA data like this
$GPGGA,124613.90,5543.3221231,N,03739.1368442,E,1,15,0.69,147.0851,M,14.4298,M,,*54
$GPGSV,3,1,10,27,12,078,41,05,31,308,49,16,25,043,44,02,11,268,44*7E
$GPGSV,3,2,10,26,03,031,39,07,74,216,52,09,58,121,52,30,39,234,48*71
$GPGSV,3,3,10,23,30,116,46,04,37,114,47*79
$GLGSV,2,1,07,84,17,338,43,78,15,212,48,85,12,032,46,67,84,223,53*67
$GLGSV,2,2,07,77,67,195,47,76,50,047,54,66,32,144,52*5C
$GPGGA,124614.00,5543.3221239,N,03739.1368445,E,1,15,0.69,147.0864,M,14.4298,M,,*53
$GPGSV,3,1,10,27,12,078,41,05,31,308,49,16,25,043,43,02,11,268,44*79
$GPGSV,3,2,10,26,03,031,39,07,74,216,52,09,58,121,52,30,39,234,48*71
$GPGSV,3,3,10,23,30,116,46,04,37,114,47*79
$GLGSV,2,1,07,84,17,338,43,78,15,212,48,85,12,032,46,67,84,223,53*67
$GLGSV,2,2,07,77,67,195,47,76,50,047,54,66,32,144,52*5C
And if we want to extract GGA and GSV data, you may want to use the following code:
#include <string>
#include <ctime>
#include <cstring>
#include <iostream>
#include <fstream>
#include <iomanip>
constexpr size_t NumberOfFixQualityStrings = 9;
constexpr size_t NumberOfSatellitesPerGSVSentencePart = 4;
constexpr size_t MaxNumberOfPartsInSentence = 10;
constexpr size_t MaxTokensInSentence = 64;
constexpr size_t NumberOfFieldsInGGA = 12;
std::string fixQualityString[NumberOfFixQualityStrings]{
"invalid", "GPS fix (SPS)", "DGPS fix", "PPS fix", "Real Time Kinematic", "Float RTK",
"estimated (dead reckoning", "Manual input mode", "Simulation mode" };
// essential fix data which provide 3D location and accuracy data
struct GGA {
// Time of last satellite fix
unsigned int fixTimeInUtcHours{};
unsigned int fixTimeInUtcMinutes{};
unsigned int fixTimeInUtcSeconds{};
unsigned int fixTimeInUtcMilliSeconds{};
// Position: Lattitude
unsigned int lattitudeInDegree{};
double lattitudeInMinutes{};
std::string lattitideDirection{};
// Position: Longitude
unsigned int longitudeInDegree{};
double longitudeInMinutes{};
std::string longitudeDirection{};
// FixQuality // see dteails as string above
unsigned int fixQuality{};
std::string fixQualityString{};
// Number of satellites being tracked (can be more than shown in GSV, not all are beeing used for calculation)
unsigned int numberOfTrackedSatellites{};
// Horizontal dilution of position
double horizontalDilution{};
// Altitude, Meters, above mean sea level
double altitude{};
std::string altitudeDimension{};
// Height of geoid (mean sea level) above WGS84 ellipsoid
double goidHight{};
std::string goidHightDimension{};
};
// Detail information for satellites in satellit view (GSV)
struct SatelliteData {
std::string satellitePRNnumber{};
double elevationInDegress{};
double azimuthInDegrees{};
double snr{}; // signal noise ratio
};
// Part of a GSV sentence
struct GSVSentencePart {
size_t numberOfSentencesForFullData{};
size_t sentencePartNumber{};
size_t numberOfSatellitesInView{};
size_t numberOfSatellitesInThisPart{};
SatelliteData satelliteData[NumberOfSatellitesPerGSVSentencePart];
};
struct GSV
{
GSVSentencePart gsvSentencePart[MaxNumberOfPartsInSentence];
size_t numberOfParts{};
};
bool checksumTest(std::string& line) {
bool result{ false };
// Check, if there is a 2 digt checksum at the end and convert it to decimal
if (size_t pos{}, checkSumGiven{ std::stoul(line.substr(line.size() - 2), &pos, 16) }; pos == 2)
{
// Strip off checksum part
line = line.substr(1,line.size() - 4);
// Calculate checksum
unsigned char calculatedChecksum{ 0U }; for (const unsigned char c : line) calculatedChecksum ^= c;
// Get result
result = (calculatedChecksum == checkSumGiven);
}
return result;
}
// Split all strings into a tokens
size_t splitIntoTokens(std::string& s, std::string (&tokens)[MaxTokensInSentence]) {
// Number of converted tokens
size_t numberOfTokens{ 0 };
// First check checksum
if (checksumTest(s)) {
// Now split along each comma
for (size_t i{ 0U }, startpos{ 0U }; i < s.size(); ++i) {
// So, if there is a comma or the end of the string
if ((s[i] == ',') || (i == (s.size() - 1))) {
// Copy substring
tokens[numberOfTokens++] = s.substr(startpos, i - startpos);
startpos = i + 1;
}
}
}
return numberOfTokens;
}
GGA convertStringToGGA(std::string& s) {
GGA gga;
// Split string into tokens and check, if it worked
if (std::string tokens[MaxTokensInSentence]; splitIntoTokens(s, tokens) > NumberOfFieldsInGGA && tokens[0] == "GPGGA") {
gga.fixTimeInUtcHours = std::stoul(tokens[1].substr(0, 2));
gga.fixTimeInUtcMinutes = std::stoul(tokens[1].substr(2, 2));
gga.fixTimeInUtcSeconds = std::stoul(tokens[1].substr(4, 2));
gga.fixTimeInUtcMilliSeconds = std::stod(tokens[1].substr(6, 2))*1000.0;
gga.lattitudeInDegree = std::stoul(tokens[2].substr(0, 2));
gga.lattitudeInMinutes = std::stod(tokens[2].substr(2));
gga.lattitideDirection = tokens[3];
gga.longitudeInDegree = std::stoul(tokens[4].substr(0, 2));
gga.longitudeInMinutes = std::stod(tokens[4].substr(2));
gga.longitudeDirection = tokens[5];
gga.fixQuality = std::stoul(tokens[6]);
gga.fixQualityString = (gga.fixQuality < NumberOfFixQualityStrings) ? fixQualityString[gga.fixQuality] : fixQualityString[0];
gga.numberOfTrackedSatellites = std::stoul(tokens[7]);
gga.horizontalDilution = std::stod(tokens[8]);
gga.altitude = std::stod(tokens[9]);
gga.altitudeDimension = tokens[10];
gga.goidHight = std::stod(tokens[11]);
gga.goidHightDimension = tokens[12];
}
return gga;
}
GSVSentencePart convertToGSVSentencePart(std::string& s) {
GSVSentencePart gsvsp;
// Split string into tokens and check, if it worked
std::string tokens[MaxTokensInSentence];
if (size_t numberOfCOnvertedTokens = splitIntoTokens(s, tokens); numberOfCOnvertedTokens > 0 && tokens[0] == "GPGSV") {
gsvsp.numberOfSentencesForFullData = std::stoul(tokens[1]);
gsvsp.sentencePartNumber = std::stoul(tokens[2]);
gsvsp.numberOfSatellitesInView = std::stoul(tokens[3]);
gsvsp.numberOfSatellitesInThisPart = 0;
for (size_t currentToken = 4; currentToken < numberOfCOnvertedTokens; currentToken += 4) {
gsvsp.satelliteData[gsvsp.numberOfSatellitesInThisPart].satellitePRNnumber = tokens[currentToken];
gsvsp.satelliteData[gsvsp.numberOfSatellitesInThisPart].elevationInDegress = stod(tokens[currentToken + 1]);
gsvsp.satelliteData[gsvsp.numberOfSatellitesInThisPart].azimuthInDegrees= stod(tokens[currentToken + 2]);
gsvsp.satelliteData[gsvsp.numberOfSatellitesInThisPart].snr = stod(tokens[currentToken + 3]);
++gsvsp.numberOfSatellitesInThisPart;
}
}
return gsvsp;
}
std::string calculateElapsedTime(const GGA& previousGGA, const GGA& nextGGA) {
std::tm tmPrevious{}, tmNext{};
tmPrevious.tm_year = 100; tmPrevious.tm_mon = 1; tmPrevious.tm_mday = 1;
tmNext.tm_year = 100; tmNext.tm_mon = 1; tmNext.tm_mday = 1;
tmPrevious.tm_hour = previousGGA.fixTimeInUtcHours;
tmPrevious.tm_min = previousGGA.fixTimeInUtcMinutes;
tmPrevious.tm_sec = previousGGA.fixTimeInUtcSeconds;
std::time_t previousTime = std::mktime(&tmPrevious);
tmNext.tm_hour = nextGGA.fixTimeInUtcHours;
tmNext.tm_min = nextGGA.fixTimeInUtcMinutes;
tmNext.tm_sec = nextGGA.fixTimeInUtcSeconds;
std::time_t nextTime = std::mktime(&tmNext);
double diff = std::difftime(nextTime, previousTime);
diff = diff + 1.0*nextGGA.fixTimeInUtcMilliSeconds/1000.0- 1.0*previousGGA.fixTimeInUtcMilliSeconds/1000.0;
return std::to_string(diff);
}
int main() {
// Open file and check, if it is open
if (std::ifstream nmeaFile("r:\\log.txt"); nmeaFile) {
GGA previousGGA;
GGA nextGGA;
GSV gsv;
size_t state{ 0 };
for (std::string line{}; std::getline(nmeaFile, line); ) {
switch ( state) {
case 0: // wait for first GGA data
if (line.substr(0, 6) == "$GPGGA") {
previousGGA = nextGGA;
nextGGA = convertStringToGGA(line);
state = 1;
gsv = {};
}
break;
case 1: // wait for GSV
if (line.substr(0, 6) == "$GPGSV") {
gsv.gsvSentencePart[gsv.numberOfParts] = convertToGSVSentencePart(line);
if (gsv.gsvSentencePart[gsv.numberOfParts].numberOfSentencesForFullData ==
gsv.gsvSentencePart[gsv.numberOfParts].sentencePartNumber) {
state = 0;
++gsv.numberOfParts;
// Now all data are available in reable and structed format.
// You can do, what you want with them
// For example, we can print all Satellite Data:
size_t counter{ 0 };
for (size_t i = 0; i < gsv.numberOfParts; ++i) {
for (size_t j = 0; j < gsv.gsvSentencePart[i].numberOfSatellitesInThisPart; j++) {
std::cout << "Satellite: " << std::setw(2) << ++counter << " Satellite name: " <<
std::setw(3) << gsv.gsvSentencePart[i].satelliteData[j].satellitePRNnumber <<
" SNR: " << std::setw(8) << gsv.gsvSentencePart[i].satelliteData[j].snr <<
" Elapsed time: "<< calculateElapsedTime(previousGGA, nextGGA)<< " s\n";
}
}
--gsv.numberOfParts;
}
++gsv.numberOfParts;
}
break;
}
}
}
return 0;
}
Coding style is "beginner"-level for easier understanding.
Modern C++ approach would be totally different, but not so easy to understand.

How to insert an integer with leading zeros into a std::string?

In a C++14 program, I am given a string like
std::string s = "MyFile####.mp4";
and an integer 0 to a few hundred. (It'll never be a thousand or more, but four digits just in case.) I want to replace the "####" with the integer value, with leading zeros as needed to match the number of '#' characters. What is the slick C++11/14 way to modify s or produce a new string like that?
Normally I would use char* strings and snprintf(), strchr() to find the "#", but figure I should get with modern times and use std::string more often, but know only the simplest uses of it.
What is the slick C++11/14 way to modify s or produce a new string like that?
I don't know if it's slick enough but I propose the use of std::transform(), a lambda function and reverse iterators.
Something like
#include <string>
#include <iostream>
#include <algorithm>
int main ()
{
std::string str { "MyFile####.mp4" };
int num { 742 };
std::transform(str.rbegin(), str.rend(), str.rbegin(),
[&](auto ch)
{
if ( '#' == ch )
{
ch = "0123456789"[num % 10]; // or '0' + num % 10;
num /= 10;
}
return ch;
} // end of lambda function passed in as a parameter
); // end of std::transform()
std::cout << str << std::endl; // print MyFile0742.mp4
}
I would use regex since you're using C++14:
#include <iostream>
#include <regex>
#include <string>
#include <iterator>
int main()
{
std::string text = "Myfile####.mp4";
std::regex re("####");
int num = 252;
//convert int to string and add appropriate number of 0's
std::string nu = std::to_string(num);
while(nu.length() < 4) {
nu = "0" + nu;
}
//let regex_replace do it's work
std::regex_replace(std::ostreambuf_iterator<char>(std::cout),
text.begin(), text.end(), re, nu);
std::cout << std::endl;
return 0;
}
WHy not use std::stringstream and than convert it to string.
std::string inputNumber (std::string s, int n) {
std::stringstream sstream;
bool numberIsSet = false;
for (int i = 0; i < s; ++i) {
if (s[i] == '#' && numberIsSet == true)
continue;
else if (s[i] == '#' && numberIsSet == false) {
sstream << setfill('0') << setw(5) << n;
numberIsSet = true;
} else
sstream << s[i];
}
return sstream.str();
}
I would probably use something like this
#include <iostream>
using namespace std;
int main()
{
int SomeNumber = 42;
std:string num = std::to_string(SomeNumber);
string padding = "";
while(padding.length()+num.length()<4){
padding += "0";
}
string result = "MyFile"+padding+num+".mp4";
cout << result << endl;
return 0;
}
Mine got out of control while I was playing with it, heh.
Pass it patterns on its command line, like:
./cpp-string-fill file########.jpg '####' test###this### and#this
#include <string>
#include <iostream>
#include <sstream>
std::string fill_pattern(std::string p, int num) {
size_t start_i, end_i;
for(
start_i = p.find_first_of('#'), end_i = start_i;
end_i < p.length() && p[end_i] == '#';
++end_i
) {
// Nothing special here.
}
if(end_i <= p.length()) {
std::ostringstream os;
os << num;
const std::string &ns = os.str();
size_t n_i = ns.length();
while(end_i > start_i && n_i > 0) {
end_i--;
n_i--;
p[end_i] = ns[n_i];
}
while(end_i > start_i) {
end_i--;
p[end_i] = '0';
}
}
return p;
}
int main(int argc, char *argv[]) {
if(argc<2) {
exit(1);
}
for(int i = 1; i < argc; i++) {
std::cout << fill_pattern(argv[i], 1283) << std::endl;
}
return 0;
}
I would probably do something like this:
using namespace std;
#include <iostream>
#include <string>
int main()
{
int SomeNumber = 42;
string num = std::to_string(SomeNumber);
string guide = "myfile####.mp3";
int start = static_cast<int>(guide.find_first_of("#"));
int end = static_cast<int>(guide.find_last_of("#"));
int used = 1;
int place = end;
char padding = '0';
while(place >= start){
if(used>num.length()){
guide.begin()[place]=padding;
}else{
guide.begin()[place]=num[num.length()-used];
}
place--;
used++;
}
cout << guide << endl;
return 0;
}

Taking Each Individual Word From a String in C++

I am writing a method in C++ which will take a string of 2 or more words and output each individual word of the string separated by a second or so, using the sleep() method. I am trying to do this using a for loop and substrings. I am unsure also of the regexs which should be used, and how they should be used, to achieve the desired output.
I have reviewed this and this and find my question differs since I am trying to do this in a loop, and not store the individual substrings.
Input:
"This is an example"
Desired output:
"This " (pause) "is " (pause) "an " (pause) "example."
Use std::stringstream, no regular expressions required:
#include <iostream>
#include <sstream>
using namespace std;
int main() {
stringstream ss("This is a test");
string s;
while (ss >> s) {
cout << s << endl;
}
return 0;
}
Also, see How do I tokenize a string in C++?
Here are a pair of implementations that don't involve creating any extraneous buffers.
#include <boost/range/adaptor/filtered.hpp>
#include <boost/range/algorithm/copy.hpp> //for boost::copy
#include <chrono>
#include <iostream>
#include <string>
#include <experimental/string_view> //in clang or gcc; or use boost::string_ref in boost 1.53 or later; or use boost::iterator_range<char*> in earlier version of boost
#include <thread>
void method_one(std::experimental::string_view sv)
{
for(auto b = sv.begin(), e = sv.end(), space = std::find(b, e, ' ')
; b < e
; b = space + 1, space = std::find(space + 1, e, ' '))
{
std::copy(b, space, std::ostreambuf_iterator<char>(std::cout));
std::cout << " (pause) "; //note that this will spit out an extra pause the last time through
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
void method_two(std::experimental::string_view sv)
{
boost::copy(
sv | boost::adaptors::filtered([](const char c) -> bool
{
if(c == ' ')
{
std::cout << " (pause) "; //note that this spits out exactly one pause per space character
std::this_thread::sleep_for(std::chrono::seconds(1));
return false;
}
return true;
})
, std::ostreambuf_iterator<char>(std::cout)
);
}
int main() {
const std::string s{"This is a string"};
method_one(s);
std::cout << std::endl;
method_two(s);
std::cout << std::endl;
return 0;
}
Live on coliru, if you're into that.
you can implement your own method:
//StrParse.h
#pragma once
#include <iostream>
static counter = 0;
char* strPar(char* pTxt, char c)
{
int lenAll = strlen(pTxt);
bool strBeg = false;
int nWords = 0;
for(int i(0); i < lenAll; i++)
{
while(pTxt[i] != c)
{
strBeg = true;
i++;
}
if(strBeg)
{
nWords++;
strBeg = false;
}
}
int* pLens = new int[nWords];
int j = 0;
int len = 0;
for(i = 0; i < lenAll; i++)
{
while(pTxt[i] != c)
{
strBeg = true;
i++;
len++;
}
if(strBeg)
{
pLens[j] = len;
j++;
strBeg = false;
len = 0;
}
}
char** pStr = new char*[nWords + 1];
for(i = 0; i < nWords; i++)
pStr[i] = new char[pLens[i] + 1];
int k = 0, l = 0;
for(i = 0; i < lenAll; i++)
{
while(pTxt[i] != c)
{
strBeg = true;
pStr[k][l] = pTxt[i];
l++;
i++;
}
if(strBeg)
{
pStr[k][l] = '\0';
k++;
l = 0;
strBeg = false;
}
}
counter++;
if(counter <= nWords)
return pStr[counter - 1];
else
return NULL;
}
//main.cpp
#include "StrParse.h"
void main()
{
char* pTxt = " -CPlusPlus -programming -is -a - superb thing ";
char* pStr1 = NULL;
int i = 1;
char sep;
std::cout << "Separator: ";
sep = std::cin.get();
std::cin.sync();
while(pStr1 = strPar(pTxt, sep))
{
std::cout << "String " << i << ": " << pStr1 << std::endl;
delete pStr1;
i++;
}
std::cout << std::endl;
}

How to insert spaces in a big number to make it more readable?

I came up with this, since other examples provided on stackoverflow were in C#
string number_fmt(ulong n)
{
// cout << "(" << n << ")" << endl;
char s[128];
sprintf(s, "%lu", n);
string r(s);
reverse(r.begin(), r.end());
int space_inserted = 0;
size_t how_many_spaces = r.length() / 3;
if(r.length() % 3 != 0)
how_many_spaces += 1;
for(int i = 1; i < how_many_spaces; ++i)
{
r.insert(3 * i + space_inserted, " ");
space_inserted += 1;
}
reverse(r.begin(), r.end());
return r;
}
Do you know any better solution ?
I don't know about "better", but this version uses std::locale, etc.
#include <iostream>
#include <locale>
#include <sstream>
template<class Char>
class MyFacet : public std::numpunct<Char> {
public:
std::string do_grouping() const { return "\3"; }
Char do_thousands_sep() const { return ' '; }
};
std::string number_fmt(unsigned long n)
{
std::ostringstream oss;
oss.imbue(std::locale(oss.getloc(), new MyFacet<char>));
oss << n;
return oss.str();
}
int main() {
std::cout << number_fmt(123456789) << "\n";
}
EDIT: Of course, if your final goal is to print the values on an ostream, you can skip storing them in a string altogether.
#include <iostream>
#include <locale>
#include <sstream>
#include <cwchar>
template <class Char>
class MyFacet : public std::numpunct<Char> {
public:
std::string do_grouping() const { return "\3"; }
Char do_thousands_sep() const { return ' '; }
};
int main(int ac, char **av) {
using std::locale;
using std::cout;
// Show how it works to start with
cout << 123456789 << "\n";
// Switch it to spacey mode
locale oldLoc =
cout.imbue(locale(cout.getloc(), new MyFacet<char>));
// How does it work now?
cout << 456789123 << "\n";
// You probably want to clean up after yourself
cout.imbue(oldLoc);
// Does it still work?
cout << 789123456 << "\n";
}
This is already done by the locale.
The default local is "C" which does no formatting. But you can set it to your current language-specific local (as defined by your computer's setting by setting the current local as the first line of main).
int main()
{
std::locale::global(std::locale("")); // Set the default local of the machine
// Will be used by all streams.
// The "" will find the machine specific local
// and use that instead of the "C" locale
// Note: The C local should only be used for programmers.
// Alternatively you can imbue particular stream with the local
// To achieve a localized effect
// std::cout.imbue(std::locale(""));
// Now all you do is print the number.
std::cout << "123456789\n"; // This will print the number according to your local
} // For me US-en this is 123,456,789
// Your may very.
If you want to do something explicitly then you can set a facet in the local for printing numbers.
#include <iostream>
#include <locale>
#include <string>
template<typename CharT>
struct Sep : public std::numpunct<CharT>
{
virtual std::string do_grouping() const {return "\003";}
virtual CharT do_thousands_sep() const {return ':';}
};
int main()
{
std::cout.imbue(std::locale(std::cout.getloc(), new Sep <char>()));
std::cout << 123456789 << "\n"; // this prints 123:456:789
}
This one is different, but better is subjective. I think it's very succinct and clear what it's doing though:
string number_fmt(unsigned long long n, char sep = ',') {
stringstream fmt;
fmt << n;
string s = fmt.str();
s.reserve(s.length() + s.length() / 3);
// loop until the end of the string and use j to keep track of every
// third loop starting taking into account the leading x digits (this probably
// can be rewritten in terms of just i, but it seems more clear when you use
// a seperate variable)
for (int i = 0, j = 3 - s.length() % 3; i < s.length(); ++i, ++j)
if (i != 0 && j % 3 == 0)
s.insert(i++, 1, sep);
return s;
}
Using it like
cout << number_fmt(43615091387465) << endl;
prints
43,615,091,387,465
Admittedly, if one wanted to have the most possible efficient version and didn't mind specializing it for the case at hand, using a local char buffer can help a lot.
#include <iostream>
#include <string>
std::string format(unsigned long long i) {
char buffer[128]; // can be adapted more tightly with std::numeric_limits
char* p = buffer + 128;
*(--p) = '\0';
unsigned char count = 0;
while (i != 0) {
*(--p) = '0' + (i % 10);
i /= 10;
if (++count == 3) { count = 0; *(--p) = ' '; }
}
return p;
}
int main() {
std::cout << format(1234567890) << '\n';
}
In action at ideone:
1 234 567 890
(Key point: for number printing, go backward)
Not very optimal but small
QString str = QString::number(d);
for (int i = 3; str.size() > i; i += 4)
str.insert(str.size() - i, ' ');
If "better" means more efficient, you should:
use reserve on the output string (you know its size...)
avoid the insert in the middle of the string, because you have to copy a big part of the string each time you do that.
I would say something like this (untested):
std::string number_fmt (ulong n)
{
std::ostringstream buff;
buff << n;
std::string without_spaces = buff.str ();
std::string with_spaces;
with_spaces.reserve ((without_spaces.size () * 4) / 3);
std::size_t nb_inserted = 0;
for (auto it = without_spaces.rbegin (); it != without_spaces.rend (); ++it)
{
if (nb_inserted % 3 == 0 && nb_inserted != 0)
{
with_spaces.push_back (' ');
}
++ nb_inserted;
with_spaces.push_back (*it);
}
std::reverse (with_spaces.begin (), with_spaces.end ());
return with_spaces;
}