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; }