How should I parse and check through a string? - c++
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.
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.
parsing text and output "time of appearance" of some lines [closed]
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 . .
Dictionary flush in LZW algorithm
I have compressor/decompressor for LZW. Now i want to use custom variable(size) to keep dictionary size in bytes and then it becomes large enough i revert dictionary to initial state and start fill it again. But i dont realize how to implement it. I had several tries. First time i flushed dict then it had maxSize elements, however it may become to large. Next time i increased size by size of new element in dict, but program took several Gb from memory and broke everything at the end. Coder void lzwCodeFile(istream &in, ostream &out, uint32_t maxSize) { unordered_map<string, uint32_t> mp; clearAndFill(mp); uint32_t code = 256; uint32_t size = 256; string w = ""; string tmp; uint8_t c; while (in.read((char *) &c, sizeof c)) { tmp = w; tmp += c; if (mp.find(tmp) != mp.end()) { w = tmp; } else { uint32_t val = mp[w]; out.write((const char *) &val, sizeof(mp[w])); //cout << val << " "; mp[tmp] = code++; size += tmp.size(); w = c; } } if (w.size()) { uint32_t val = mp[w]; out.write((const char *) &val, sizeof(mp[w])); //cout << val << " "; } } Decoder void lzwDecodeFile(istream &in, ostream &out, uint32_t maxSize) { unordered_map<uint32_t, string> mp; uint32_t code = 256; uint32_t size = 256; clearAndFillRev(mp); string tmp, w; uint32_t k; in.read((char *) &k, sizeof k); w = "" + mp[k]; string entry; out << w; while (in.read((char *) &k, sizeof k)) { // finded if (mp.find(k) != mp.end()) { entry = mp[k]; } else { entry = w + w[0]; } out << entry; tmp = w + entry[0]; mp[code++]=tmp; w = entry; } }
use std::regex to find contents of a function
so let's say I have a main function with some arbitrary code: void main(){ //Some random code int a = 5; int b = a + 7; } and the text of this function is stored inside an std::string: std::string mystring("void main(){ //Some random code int a = 5; int b = a + 7;}"); I want to use std::regex in order to extract out the body of the function. So the result I would be getting back is: "//Some random code int a= 5; int b = a + 7;" My issue is I do not know how to format the regular expression to get what I want. Here is my code I have right now: std::string text("void main(){ //Some random code int a = 5; int b = a + 7;}"); std::regex expr ("void main()\\{(.*?)\\}"); std::smatch matches; if (std::regex_match(text, matches, expr)) { for (int i = 1; i < matches.size(); i++) { std::string match (matches[i].first, matches[i].second); std::cout << "matches[" << i << "] = " << match << std::endl; } } My regex is completely off and returns no matches. What do I need to make my regex in order for this to work?
As discussed in the comments OP only wants to "extract the text inside the function body, regardless of what that text is". #OP: Your regex is wrong as you don't escape the parenthesis for main(). Changing the regex to "void main\\(\\)\\{(.*?)\\}" will work. I also recommend to use size_t for i in your for-loop so you don't compare signed with unsigned (std::smatch::size() returns size_t). #include <iostream> #include <regex> int main() { std::string text("void main(){ //Some random code int a = 5; int b = a + 7;}"); std::regex expr("void main\\(\\)\\{(.*?)\\}"); std::smatch matches; if (std::regex_match(text, matches, expr)) { for (size_t i = 1; i < matches.size(); i++) { std::string match(matches[i].first, matches[i].second); std::cout << "matches[" << i << "] = " << match << std::endl; } } } Output: matches[1] = //Some random code int a = 5; int b = a + 7; This solution fails for the input "void main(){ while(true){ //Some random code int a = 5; int b = a + 7; } }" The easiest solution to this would be to change the regex to "^void main\\(\\)\\{(.*?)\\}$" but that requires the input to start with "void main(){" and end with "}" As proposed by Revolver_Ocelot you can also add some whitespace matching into the regex to make it a little bit more flexible.
As suggested in your use case it would probably be the best to just rely on string search and matching of braces. #include <iostream> #include <regex> std::string getBody(const std::string& functionDef, const std::string& text) { size_t pos = 0; do { if ((pos = text.find(functionDef, pos)) == std::string::npos) continue; pos += functionDef.length(); size_t firstSemicolon = text.find(";", pos); size_t firstOpen = text.find("{", pos); size_t firstClose = text.find("}", pos); if (firstSemicolon != std::string::npos && firstSemicolon < firstOpen) //Only function declaration continue; if (firstOpen == std::string::npos || firstClose == std::string::npos || firstClose < firstOpen) //Mismatch continue; size_t bodyStart = pos = firstOpen + 1; size_t bracesCount = 1; do { firstOpen = text.find("{", pos); firstClose = text.find("}", pos); if (firstOpen == std::string::npos && firstClose == std::string::npos)//Mismatch { pos = std::string::npos; continue; } //npos is always larger if (firstOpen < firstClose) { bracesCount++; pos = firstOpen + 1; } else if (firstOpen > firstClose) { bracesCount--; if (bracesCount == 0) { size_t bodySize = firstClose - bodyStart; return text.substr(bodyStart, bodySize); } pos = firstClose + 1; } else { //Something went terribly wrong... pos = std::string::npos; continue; } } while (pos != std::string::npos); } while (pos != std::string::npos); return std::string(); } int main() { std::string text("void main(); int test(); void main(){ while(true){ //Some {random} code int a = 5; int b = a + 7; } } int test(){ return hello; } "); std::cout << getBody("void main()", text) << std::endl; std::cout << getBody("int test()", text) << std::endl; } Output: while(true){ //Some {random} code int a = 5; int b = a + 7; } return hello; The code can also handle newlines and skips function declarations. I tried to write it as clear as possible. If there are still questions feel free to ask.
Odd performance issue with nested for loops
Below is the full source code you can just copy paste into Visual Studio for easy repro. #include <Windows.h> #include <algorithm> #include <vector> #include <iostream> #include <sstream> LARGE_INTEGER gFreq; struct CProfileData; // Yes, we map the pointer itself not the string, for performance reasons std::vector<CProfileData*> gProfileData; // simulate a draw buffer access to avoid CBlock::Draw being optimized away float gDrawBuffer = 0; struct CTimer { CTimer() { Reset(); } size_t GetElapsedMicro() { LARGE_INTEGER now; ::QueryPerformanceCounter(&now); return (1000000 * (now.QuadPart - m_timer.QuadPart)) / gFreq.QuadPart; } inline void Reset() { ::QueryPerformanceCounter(&m_timer); } LARGE_INTEGER m_timer; }; struct CProfileData { CProfileData() : m_hitCount(0), m_totalTime(0), m_minTime(-1), m_maxTime(0), m_name(NULL) { gProfileData.push_back(this); } size_t m_totalTime; size_t m_minTime; size_t m_maxTime; size_t m_hitCount; const char * m_name; }; class CSimpleProfiler { public: CSimpleProfiler(const char * aLocationName, CProfileData * aData) : m_location(aLocationName), m_data(aData) { ::QueryPerformanceCounter(&m_clock); } ~CSimpleProfiler() { CProfileData & data = *m_data; data.m_name = m_location; ++data.m_hitCount; LARGE_INTEGER now; ::QueryPerformanceCounter(&now); size_t elapsed = (1000000 * (now.QuadPart - m_clock.QuadPart)) / gFreq.QuadPart; data.m_totalTime += elapsed; elapsed < data.m_minTime ? data.m_minTime = elapsed : true; elapsed > data.m_maxTime ? data.m_maxTime = elapsed : true; } static void PrintAll() { std::stringstream str; str.width(20); str << "Location"; str.width(15); str << "Total time"; str.width(15); str << "Average time"; str.width(15); str << "Hit count"; str.width(15); str << "Min"; str.width(15); str << "Max" << std::endl; ::OutputDebugStringA(str.str().c_str()); for (auto i = gProfileData.begin(); i != gProfileData.end(); ++i) { CProfileData & data = **i; std::stringstream str; str.width(20); str << data.m_name; str.width(15); str << data.m_totalTime; str.width(15); str << data.m_totalTime / (float)data.m_hitCount; str.width(15); str << data.m_hitCount; str.width(15); str << data.m_minTime; str.width(15); str << data.m_maxTime << std::endl; ::OutputDebugStringA(str.str().c_str()); } } static void Clear() { for (auto i = gProfileData.begin(); i != gProfileData.end(); ++i) { (*i)->m_totalTime = 0; (*i)->m_minTime = 0; (*i)->m_maxTime = 0; (*i)->m_hitCount = 0; } } private: LARGE_INTEGER m_clock; const char * m_location; CProfileData * m_data; }; #define PROFILING_ENABLED #ifdef PROFILING_ENABLED #define SIMPLE_PROFILE \ static CProfileData pdata ## __LINE__; \ CSimpleProfiler p ## __LINE__(__FUNCTION__, & pdata ## __LINE__) #define SIMPLE_PROFILE_WITH_NAME(Name) \ static CProfileData pdata ## __LINE__; \ CSimpleProfiler p ## __LINE__(Name, & pdata ## __LINE__) #else #define SIMPLE_PROFILE __noop #define SIMPLE_PROFILE_WITH_NAME(Name) __noop #endif void InvalidateL1Cache() { const int size = 256 * 1024; static char *c = (char *)malloc(size); for (int i = 0; i < 0x0fff; i++) for (int j = 0; j < size; j++) c[j] = i*j; } int _tmain(int argc, _TCHAR* argv[]) { ::QueryPerformanceFrequency(&gFreq); LARGE_INTEGER pc; ::QueryPerformanceCounter(&pc); struct CBlock { float x; float y; void Draw(float aBlend) { for (size_t i = 0; i < 100; ++i ) gDrawBuffer += aBlend; } }; typedef std::vector<std::vector<CBlock>> Layer; typedef std::vector<Layer> Layers; Layers mBlocks; // populate with dummy data; mBlocks.push_back(Layer()); Layer & layer = mBlocks.back(); layer.resize(109); srand(0); // for reprodicibility (determinism) for (auto i = layer.begin(); i != layer.end(); ++i) { i->resize(25 + rand() % 10 - 5); } // end populating dummy data while (1) { CSimpleProfiler::Clear(); float aBlend = 1.f / (rand() % 100); { for (auto i = mBlocks.begin(); i != mBlocks.end(); ++i) { for (auto j = i->begin(); j != i->end(); ++j) { CTimer t; { SIMPLE_PROFILE_WITH_NAME("Main_Draw_3"); for (auto blockIt = j->begin(); blockIt != j->end();) { CBlock * b = nullptr; { b = &*blockIt; } { b->Draw(aBlend); } { ++blockIt; } } } if (t.GetElapsedMicro() > 1000) { ::OutputDebugStringA("SLOWDOWN!\n"); CSimpleProfiler::PrintAll(); } } } } } return 0; } I get the following profiling from time to time, expressed in microseconds: SLOWDOWN! Location Total time Average time Hit count Min Max Main_Draw_3 2047 36.5536 56 0 1040 This spikes from time to time. Normally, it takes 100 microseconds for Main_Draw_3 block to finish, but it spikes to 1000 (the Max column) from time to time. What causes this? I'm aware cache misses could play a role, but is it really that in this case?... What is happening here and how can I mitigate this? More info: compiler VS 2013, compiled with Maximize Speed (/O2)
I think there might be two issues: Are you compiling with optimizations on? What are the flags? Maybe you could increase the sample size (by doing for instance ten (or hundred, or thousand etc) runs of this code in one profiling run). The reason is that if the sample size is small, the standard deviation is very high