I'm reading a file into a vector, to read bytes off of it. The standalone function works fine, but I don't want to open and close a file and create a vector every time i access, since im going to be grabbing lots of bytes. So I opted to put it in a file class. However, this has lead to me getting entirely different results when I use the file class.
This is a working standalone function:
template<class T>
T readFromNewFile(std::string path, size_t size = 0, uint offset = 0)
{
std::ifstream in(path.c_str(), std::ios::binary | std::ios::ate);
if (in.is_open())
{
std::streamoff max = in.tellg();
size_t _size = size == 0 ? (size_t)max : size;
in.seekg(offset, std::ios::beg);
std::vector<uint> v(_size);
in.read((char*)v.data(), _size);
uint temp = 0;
std::stringstream ss;
for (uint it = 0; it < v.size(); it++)
{
std::cout << "readFromNewFile: Adding offset: " << offset + it << ", with value: " << v[it] << "\n";
temp += v[it];
}
ss << temp;
T myVar;
ss >> myVar;
return myVar;
}
return T();
}
The file class in question:
class File
{
public:
File() {}
File(std::string path)
{
LOAD(path);
}
~File() {}
void LOAD(std::string path)
{
std::ifstream in(path.c_str(), std::ios::binary | std::ios::ate);
if (in.is_open())
{
std::streamoff max = in.tellg();
size_t _size = (size_t)max;
in.seekg(0, std::ios::beg);
data.clear();
data.resize(_size);
in.read((char*)data.data(), _size);
}
}
template<class T>
T readFromVector(size_t size = 0, uint offset = 0)
{
uint temp = 0;
std::stringstream ss;
for (uint it = offset; it < (offset + size) && it < data.size(); it++)
{
std::cout << "readFromVector: reading offset: " << it << ", with value: " << data[it] << "\n";
temp += data[it];
}
ss << temp;
T myVar;
ss >> myVar;
return myVar;
}
private:
std::vector<uint> data;
};
The code is all called in this block:
bool ArmourManager::init()
{
std::string armourFile = "resources\\armor.am_dat";
Offsets::ArmourOffsets offsets;
data.LOAD(armourFile); // data is a File object in ArmourManager class
uint armourOffset = 0x0A;
uint textOffset = 0x41C06;
// armours start at offset 0x0A, increase by 0x3C for every armour in file
// appears to end at 187390 ? 0x2DBFE
//std::cout << "[OFFSET]\t[IDX]\t[RAR]\t[SLOT]\t[DEF]\t[SLOTS]\n";
while (armourOffset < 0x2DBFE)
{
uint ind = SetMaker::readFromNewFile<uint>(armourFile, 4, armourOffset + offsets.Index);
uint x = data.readFromVector<uint>(4, armourOffset + offsets.Index);
std::cout << ind << " / " << x << "\n";
Console Output Here
From what i can tell with debugging, the data vector isn't being populated correctly, it's being filled with weird data.
Checking first index of both vectors
Even when I tried something like this,
std::vector<uint> x(_size);
in.read((char*)x.data(), _size);
std::cout << "x byte 0: " << x[0] << "\n";
the result is still the exact same.
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.
Firstly here is my code:
header:
#pragma once
#include <iostream>
using namespace std;
class CString
{
private://Main attribute
char* str;
private:// Aux attribute
int len;
public:
//constructor and destructor
CString(char* x);
CString() { len = 0; str = NULL; }
~CString()
{
if (NULL != str)
delete[] str;
str = NULL;
}
//some operator
CString operator+(CString x);
CString operator+(char* x);
void operator=(CString x);
//operator() is to extract a part of other CString object
CString operator()(unsigned pos, unsigned c_len);
//operator[] return position of CString::str[pos]
char& operator[](unsigned pos);
//Ostream output
friend ostream& operator<<(ostream& os, CString x);
};
//to do char+CString
CString operator+(char* a, CString x);
header cpp code:
#include "CString.h"
CString::CString(char * x)
{
len = 0;
while(x[len])
len++;
str = new char[len];
for (int i = 0;i < len;i++)
str[i] = x[i];
if (str[len - 1] != '\0')
{
len++;
char* tmp;
tmp = new char[len];
for (int i = 0;i < len - 1;i++)
tmp[i] = str[i];
delete[]str;
str = tmp;
tmp = NULL;
str[len - 1] = '\0';
}
}
CString CString::operator+(CString x)
{
CString* result;
result = new CString;
result->len = this->len + x.len - 1;
result.str=new char[result.len];
for (int i = 0; i < this->len - 1;i++)
{
result->str[i] = this->str[i];
}
for (int i = 0, j = this->len - 1;i < x.len;i++, j++)
{
result->str[j] = x.str[i];
}
return *result;
}
CString CString::operator+(char * x)
{
return CString(*this+CString(x));
}
void CString::operator=(CString x)
{
str = new char[x.len];
for (int i = 0; i < x.len;i++)
str[i] = x.str[i];
len = x.len;
}
CString CString::operator()(unsigned pos, unsigned c_len)
{
CString* result;
result = new CString;
result->len = c_len;
result.str=new char[c_len];
for (int i = pos;i < pos + c_len;i++)
result->str[i - pos] = str[i];
return *result;
}
char& CString::operator[](unsigned pos)
{
if (pos < len - 1)
{
char* ptr;
ptr = this->str + pos;
return *ptr;
}
else
{
int o_len = len;
len = pos + 2;
char* tmp;
tmp = new char[len];
for (int i = 0;i < o_len;i++)
{
tmp[i] = str[i];
}
tmp[len - 1] = '\0';
delete[]str;
str = tmp;
tmp = NULL;
return *(str + pos);
}
}
ostream & operator<<(ostream & os, CString x)
{
os << x.str;
return os;
}
CString operator+(char * a, CString x)
{
return CString(CString(a) + x);
}
main:
CString a("string 1"), b = "Initialize " + a;
b[15] = '2'; cout << a + " - " + b << endl;
CString c = a + b;
cout << "String extracted from string \"" << c
<< "\" from position 3 with length of 6 is: \""
<< c(3, 6) << "\"" << endl;
Problem:
When I try to compile this program, there is no error, but still operator+(CString), operator(), and destructor seem to malfunction.
When I debug this program, my program triggered a breakpoint some where at line CString a("String 1"), b = "initilize " + a; I have no idea why. I am do not know if there is any problem for the rest of the program because I always get stuck at that line. SO if somebody can find out any other problem, please tell me, that will save me another day.
Btw, I just wonder at operator+(CString) and operator(), I have create a pointer then I use new and then I return that pointer, so that I have no change to delete that pointer, will it leave me an orphan memory? Since I have read another question about return a class object, I found out that if I use CString result and then return result, result would be destroyed before return. SO is there any better way to do that?
Summary:
1.My program triggered a breakpoint some where in the second line of main().
2.How to properly return a class object?
P.S: I am really bad at communicating and just have 1 year period of C/C++ learning. So if I have type some thing could give you a cancer, please forgive me.
Sincerely thank you.
I have a question concerning this code from Thinking in c++ book , this is a tiny c style library for learning the process of memory allocation , what does it mean to write
int startBytes = s->next * s->size;
in this code , what does this multiplication mean ?
//: C04:CLib.h
// Header file for a C-like library
// An array-like entity created at runtime
typedef struct CStashTag {
int size;
// Size of each space
int quantity; // Number of storage spaces
int next;
// Next empty space
// Dynamically allocated array of bytes:
unsigned char* storage;
} CStash;
void initialize(CStash* s, int size);
void cleanup(CStash* s);
int add(CStash* s, const void* element);
void* fetch(CStash* s, int index);
int count(CStash* s);
void inflate(CStash* s, int increase);
///:~
//: C04:CLib.cpp {O}
// Implementation of example C-like library
// Declare structure and functions:
#include "CLib.h"
#include <iostream>
#include <cassert>
using namespace std;
/ Quantity of elements to add
// when increasing storage:
const int increment = 100;
void initialize(CStash* s, int sz) {
s->size = sz;
s->quantity = 0;
s->storage = 0;
s->next = 0;
}
int add(CStash* s, const void* element) {
if(s->next >= s->quantity) //Enough space left?
inflate(s, increment);
// Copy element into storage,
// starting at next empty space:
int startBytes = s->next * s->size;
unsigned char* e = (unsigned char*)element;
for(int i = 0; i < s->size; i++)
s->storage[startBytes + i] = e[i];
s->next++;
return(s->next - 1); // Index number
}
void* fetch(CStash* s, int index) {
// Check index boundaries:
assert(0 <= index);
if(index >= s->next)
return 0; // To indicate the end
// Produce pointer to desired element:
return &(s->storage[index * s->size]);
}
int count(CStash* s) {
return s->next; // Elements in CStash
}
void inflate(CStash* s, int increase) {
assert(increase > 0);
int newQuantity = s->quantity + increase;
int newBytes = newQuantity * s->size;
int oldBytes = s->quantity * s->size;
unsigned char* b = new unsigned char[newBytes];
for(int i = 0; i < oldBytes; i++)
b[i] = s->storage[i]; // Copy old to new
delete [](s->storage); // Old storage
s->storage = b; // Point to new memory
s->quantity = newQuantity;
}
void cleanup(CStash* s) {
if(s->storage != 0) {
cout << "freeing storage" << endl;
delete []s->storage;
}
} ///:~
//: C04:CLibTest.cpp
//{L} CLib
// Test the C-like library
#include "CLib.h"
#include <fstream>
#include <iostream>
#include <string>
#include <cassert>
using namespace std;
int main() {
// Define variables at the beginning
// of the block, as in C:
CStash intStash, stringStash;
int i;
char* cp;
ifstream in;
string line;
const int bufsize = 80;
// Now remember to initialize the variables:
initialize(&intStash, sizeof(int));
for(i = 0; i < 100; i++)
add(&intStash, &i);
for(i = 0; i < count(&intStash); i++)
cout << "fetch(&intStash, " << i << ") = "
<< *(int*)fetch(&intStash, i)
<< endl;
// Holds 80-character strings:
initialize(&stringStash, sizeof(char)*bufsize);
in.open("CLibTest.cpp");
assert(in);
while(getline(in, line))
add(&stringStash, line.c_str());
i = 0;
while((cp = (char*)fetch(&stringStash,i++))!=0)
cout << "fetch(&stringStash, " << i << ") = "
<< cp << endl;
cleanup(&intStash);
cleanup(&stringStash);
} ///:~
It looks like it's getting the next free location by multiplying the size of the object by the index number of the next available space. So if the next space is 10, and the object size is 10, it will start allocating at byte index 100.
It is a straight multiplication of the values of next and size, I'm going to guess it's calculating an offset somewhere. From looking at the code, size is set by the sz parameter of the initialize function.