I'm trying to write a function whose first parameter is a string and the second parameter is vector of real numbers. The function should return as a result a new string in which each occurrence replaces the sequences "%d" or "%f" with one number each from the vector, in the order in which they appear. In doing so, if the sequence is "%d", any decimals in the number are truncated, while in the sequence "%f" they are retained.
For example, if the string reads “abc%dxx%fyy %d” and if the vector contains the numbers 12.25, 34.13, 25 and 47, the new string should read “abc12xx34.13yy 25” (data 47 which is “redundant” is simply ignored).
#include <iostream>
#include <string>
#include <vector>
std::string Replace(std::string s, std::vector < double > vek) {
std::string str;
int j = 0;
for (int i = 0; i < s.length(); i++) {
while (s[i] != '%' && i < s.length()) {
if (s[i] != 'f' && s[i] != 'd')
str += s[i];
i++;
}
if (s[i] == '%' && (s[i + 1] == 'd' || s[i + 1] == 'f')) {
if (s[i + 1] == 'd')
str += (std::to_string(int(vek[j])));
if (s[i + 1] == 'f') {
std::string temp = std::to_string(vek[j]);
int l = 0;
while (temp[l] != '0') {
str += temp[l];
l++;
}
}
j++;
if (j > vek.size())
throw std::range_error("Not enough elements");
if (i == s.length()) break;
}
}
return str;
}
int main() {
try {
std::cout<<Replace("abc%dxx%fyy %d",{12.25, 34.13, 25});
std::cout << "\n" << "abc12xx34.13yy 25";
} catch (std::range_error e) {
std::cout << e.what();
}
return 0;
}
OUTPUT:
abc12xx34.13yy 25
abc12xx34.13yy 25
Output is correct. How could I modify this to work with less lines of code? Is there any way to make this more elegant and efficient?
You could use:
regular expressions to search for the pattern (%d|%f), i.e., %d or %f, and
a string stream to create the string to return.
Going into some more detail:
The code is basically a while (std::regex_search).
std::regex_search will return whatever was in the input string before the matched pattern (what you want in the output string), the matched pattern (what you will need to check in order to decide if you want to write out an int or a double), and whatever is left to parse.
By using std::ostringstream, you can simply write out ints or doubles without converting them to strings yourself.
vek.at() will throw an std::out_of_range exception if you run out of data in the vector.
Notice as well that, whereas for this implementation it's good to pass the string s by value (since we are modifying it within the function), you should pass vek as a const reference to avoid a copy of the whole vector.
[Demo]
#include <iostream>
#include <regex>
#include <stdexcept>
#include <sstream>
#include <string>
#include <vector>
std::string Replace(std::string s, const std::vector<double>& vek) {
std::regex pattern{"(%d|%f)"};
std::smatch match{};
std::ostringstream oss{};
for (auto i{0}; std::regex_search(s, match, pattern); ++i) {
oss << match.prefix();
auto d{vek.at(i)};
oss << ((match[0] == "%d") ? static_cast<int>(d) : d);
s = match.suffix();
}
return oss.str();
}
int main() {
try {
std::cout << Replace("abc%dxx%fyy %d", {12.25, 34.13, 25});
std::cout << "\n"
<< "abc12xx34.13yy 25";
} catch (std::out_of_range& e) {
std::cout << e.what();
}
}
// Outputs:
//
// abc12xx34.13yy 25
// abc12xx34.13yy 25
[EDIT] A possible way to do it without std::regex_search would be to search for the (%d|%f) pattern manually, using std::string::find in a loop until the end of the string is reached.
The code below takes into account that:
the input string could not have that pattern, and that
it could have a % character followed by neither d nor f.
[Demo]
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <string>
#include <vector>
std::string Replace(std::string s, const std::vector<double>& vek) {
std::ostringstream oss{};
size_t previous_pos{0};
size_t pos{0};
auto i{0};
while (previous_pos != s.size()) {
if ((pos = s.find('%', previous_pos)) == std::string::npos) {
oss << s.substr(previous_pos);
break;
}
oss << s.substr(previous_pos, pos - previous_pos);
bool pattern_found{false};
if (s.size() > pos + 1) {
auto c{s[pos + 1]};
if (c == 'd') {
oss << static_cast<int>(vek.at(i));
pattern_found = true;
} else if (c == 'f') {
oss << vek.at(i);
pattern_found = true;
}
}
if (pattern_found) {
++i;
previous_pos = pos + 2;
} else {
oss << s[pos];
previous_pos = pos + 1;
}
}
return oss.str();
}
int main() {
try {
std::cout << Replace("abc%%dx%x%fyy %d", {12.25, 34.13, 25}) << "\n";
std::cout << "abc%12x%x34.13yy 25\n";
std::cout << Replace("abcdxxfyy d", {12.25, 34.13, 25}) << "\n";
std::cout << "abcdxxfyy d\n";
} catch (std::out_of_range& e) {
std::cout << e.what();
}
}
// Outputs:
//
// abc%12x%x34.13yy 25
// abc%12x%x34.13yy 25
// abcdxxfyy d
// abcdxxfyy d
#include <iostream>
#include <vector>
#include <string>
std::string replace(std::string str, std::vector<double> vec) {
std::string result = "";
int i = 0;
// loop through the string
while (i < str.size()) {
// if the current character is a %
if (str[i] == '%') {
// if the next character is a d
if (str[i+1] == 'd') {
// if the vector is not empty
if (vec.size() > 0) {
// add the first element of the vector to the result
result += std::to_string(vec[0]);
// remove the first element of the vector
vec.erase(vec.begin());
}
// move the index to the next character
i += 2;
}
// if the next character is a f
else if (str[i+1] == 'f') {
// if the vector is not empty
if (vec.size() > 0) {
// add the first element of the vector to the result
result += std::to_string(vec[0]);
// remove the first element of the vector
vec.erase(vec.begin());
}
// move the index to the next character
i += 2;
}
// if the next character is not a d or f
else {
// add the current character to the result
result += str[i];
// move the index to the next character
i += 1;
}
}
// if the current character is not a %
else {
// add the current character to the result
result += str[i];
// move the index to the next character
i += 1;
}
}
// return the result
return result;
}
int main() {
std::vector<double> vec = {12.25, 34.13, 25, 47};
std::string str = "abc%dxx%fyy %d";
std::cout << replace(str, vec);
return 0;
}
How should I go about finding the length of a char array in C++? I've tried two methods already, but they both have resulted in the wrong number of characters in the array. I've used strlen and the sizeof operator so far, to no avail.
void countOccurences(char *str, string word)
{
char *p;
string t = "true";
string f = "false";
vector<string> a;
p = strtok(str, " ");
while (p != NULL)
{
a.push_back(p);
p = strtok(NULL, " ");
}
int c = 0;
for (int i = 0; i < a.size(); i++)
{
if (word == a[i])
{
c++;
}
}
int length = sizeof(str); //This is where I'm having the problem
string result;
cout << length << "\n";
if (length % 2 != 0)
{
if (c % 2 == 0)
{
result = "False";
}
else
{
result = "True";
}
}
else
{
if (c % 2 == 0)
{
result = "True";
}
else
{
result = "False";
}
}
if (strlen(str) != 0)
{
cout << result;
}
}
int boolean()
{
char str[1000];
cin.getline(str, sizeof(str));
string word = "not";
countOccurences(str, word);
return 0;
}
sizeof(str) is wrong. It gives you the size of a pointer (str is a pointer), which is a fixed number, normally either 4 or 8 depending at your platform.
std::strlen(str) is correct, but strtok inserts a bunch of \0 into your array before you try to obtain the size. strlen will stop at the first \0, and give you the number of characters preceeding it.
Call strlen before strtok and save its return value to a variable.
Here you can find a modern c++ solution:
#include <iostream>
#include <string_view>
#include <string>
#include <type_traits>
template<typename String>
inline std::size_t StrLength(String&& str)
{
using PureString = std::remove_reference_t<std::remove_const_t<String>>;
if constexpr(std::is_same_v<char, PureString>){
return 1;
}
else
if constexpr(std::is_same_v<char*, PureString>){
return strlen(str);
}
else{
return str.length();
}
}
template<
typename String,
typename Lambda,
typename Delim = char
>
void ForEachWord(String&& str, Lambda&& lambda, Delim&& delim = ' ')
{
using PureStr = std::remove_reference_t<std::remove_reference_t<String>>;
using View = std::basic_string_view<typename PureStr::value_type>;
auto start = 0;
auto view = View(str);
while(true)
{
auto wordEndPos = view.find_first_of(delim, start);
auto word = view.substr(start, wordEndPos-start);
if (word.length() > 0){
lambda(word);
}
if (wordEndPos == PureStr::npos)
{
break;
}
start = wordEndPos + StrLength(delim);
}
}
int main() {
std::string text = "This is not a good sentence.";
auto cnt = 0;
ForEachWord(
text,
[&](auto word)
{
//some code for every word... like counting or printing
if (word == "not" ){
++cnt;
}
},
' '
);
std::cout << cnt << "\n";
}
The "end of a string" is the char '\0' check for that character to stop the search.
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 . .
So i need to check if a specific sub-string exists in a Token and if it exists add 1 to the variable int a.
String input = "aaabbb#abbb#"
tokenize input
tokens =
abbba
abbbb
a exists in both tokens so output should be
a:2
However my code outputs
a:1
a:1
If there is only one token with a then it prints
a:1
If there are no tokens with a such as :
bbb#bbb#
it correctly outputs nothing.
How can i get my code to output
a:2
and not
a:1
a:1
---This is my code
main.cpp
#include "Tokenizer.h"
#include <string>
#include <iostream>
#include <algorithm>
using std::string;
using std::cout;
using std::endl;
int countSubstring(const std::string& str, const std::string& sub)
{
if (sub.length() == 0) return 0;
int count = 0;
for (size_t offset = str.find(sub); offset != std::string::npos;
offset = str.find(sub, offset + sub.length()))
{
++count;
}
return count;
}
int main(int argc, char* argv[])
{
// instanciate Tokenizer class
Tokenizer str;
string token;
string input;
getline(std::cin , input);
// set source string with delimiter "#"
str.set(input, "#");
// Tokenizer::next() returns a next available token from source string
// If it reaches EOS, it returns zero-length string, "".
while((token = str.next()) != "")
{
int a = 0;
if(countSubstring(token,"a") >= 1)
{
a++;
}
cout << "a: " << a << endl;
}
cout << endl;
return 0;
}
Tokenizer.h
#ifndef TOKENIZER_H
#define TOKENIZER_H
#include <string>
#include <vector>
// default delimiter string (space, tab, newline, carriage return, form feed)
const std::string DEFAULT_DELIMITER = "";
class Tokenizer
{
public:
// ctor/dtor
Tokenizer();
Tokenizer(const std::string& str, const std::string& delimiter=DEFAULT_DELIMITER);
~Tokenizer();
// set string and delimiter
void set(const std::string& str, const std::string& delimiter=DEFAULT_DELIMITER);
void setString(const std::string& str); // set source string only
void setDelimiter(const std::string& delimiter); // set delimiter string only
std::string next(); // return the next token, return "" if it ends
std::vector<std::string> split(); // return array of tokens from current cursor
protected:
private:
void skipDelimiter(); // ignore leading delimiters
bool isDelimiter(char c); // check if the current char is delimiter
std::string buffer; // input string
std::string token; // output string
std::string delimiter; // delimiter string
std::string::const_iterator currPos; // string iterator pointing the current position
};
#endif // TOKENIZER_H
Tokenizer.cpp
#include "Tokenizer.h"
///////////////////////////////////////////////////////////////////////////////
// constructor
///////////////////////////////////////////////////////////////////////////////
Tokenizer::Tokenizer() : buffer(""), token(""), delimiter(DEFAULT_DELIMITER)
{
currPos = buffer.begin();
}
Tokenizer::Tokenizer(const std::string& str, const std::string& delimiter) : buffer(str), token(""), delimiter(delimiter)
{
currPos = buffer.begin();
}
///////////////////////////////////////////////////////////////////////////////
// destructor
///////////////////////////////////////////////////////////////////////////////
Tokenizer::~Tokenizer()
{
}
///////////////////////////////////////////////////////////////////////////////
// reset string buffer, delimiter and the currsor position
///////////////////////////////////////////////////////////////////////////////
void Tokenizer::set(const std::string& str, const std::string& delimiter)
{
this->buffer = str;
this->delimiter = delimiter;
this->currPos = buffer.begin();
}
void Tokenizer::setString(const std::string& str)
{
this->buffer = str;
this->currPos = buffer.begin();
}
void Tokenizer::setDelimiter(const std::string& delimiter)
{
this->delimiter = delimiter;
this->currPos = buffer.begin();
}
///////////////////////////////////////////////////////////////////////////////
// return the next token
// If cannot find a token anymore, return "".
///////////////////////////////////////////////////////////////////////////////
std::string Tokenizer::next()
{
if(buffer.size() <= 0) return ""; // skip if buffer is empty
token.clear(); // reset token string
this->skipDelimiter(); // skip leading delimiters
// append each char to token string until it meets delimiter
while(currPos != buffer.end() && !isDelimiter(*currPos))
{
token += *currPos;
++currPos;
}
return token;
}
///////////////////////////////////////////////////////////////////////////////
// skip ang leading delimiters
///////////////////////////////////////////////////////////////////////////////
void Tokenizer::skipDelimiter()
{
while(currPos != buffer.end() && isDelimiter(*currPos))
++currPos;
}
///////////////////////////////////////////////////////////////////////////////
// return true if the current character is delimiter
///////////////////////////////////////////////////////////////////////////////
bool Tokenizer::isDelimiter(char c)
{
return (delimiter.find(c) != std::string::npos);
}
///////////////////////////////////////////////////////////////////////////////
// split the input string into multiple tokens
// This function scans tokens from the current cursor position.
///////////////////////////////////////////////////////////////////////////////
std::vector<std::string> Tokenizer::split()
{
std::vector<std::string> tokens;
std::string token;
while((token = this->next()) != "")
{
tokens.push_back(token);
}
return tokens;
}
Also, a is not allowed to be printed if it does not appear in a token.
A while would work better I think. This is my solution:
#include <iostream>
#include <string>
using namespace std;
size_t countSubString(const string& str, const string& sub) {
size_t ret = 0;
size_t loc = str.find(sub);
while (loc != string::npos) {
++ret;
loc = str.find(sub, loc+1);
}
return ret;
}
int main() {
static const char* tokens[] = {"aaa", "aba" };
for (int i=0; i<2; ++i) {
cout << tokens[i] << " " << countSubString(tokens[i], "a") << endl;
}
return 0;
}
#include <string>
#include <iostream>
int countOccurances( const std::string &token, const std::string &substring )
{
const size_t token_len = token.length();
const size_t substring_len = substring.length();
if ( substring_len > token_len ) { return 0; }
if ( substring_len == token_len )
return ((token.compare(substring) == 0) ? 1 : 0);
const size_t range = token_len-substring_len;
size_t counter = 0;
for (size_t i=0; i <= range; i++) {
if ( token.compare(i,substring_len,substring) == 0 )
++counter;
}
return counter;
}
int main () {
std::string token = "abbb";
std::string substring = "a";
std::cout << "Token " << token << " has " << countOccurances(token,substring) <<
" occurances of " << substring << std::endl;
token = "abba";
substring = "a";
std::cout << "Token " << token << " has " << countOccurances(token,substring) <<
" occurances of " << substring << std::endl;
return 0;
}
The heart of your code is this
int a = 0;
if(countSubstring(token,"a") >= 1)
{
a++;
}
cout << "a: " << a << endl;
This code creates a variable called a and assigns it the value 0. Then it calls countSubstring and tests if the return value for being >= 1. The return value is then discarded.
If the condition above matched, we increase a by 1.
Then we print a.
All of the above happens for each iteration of the while loop in which this code sits.
So this code can only ever print a: 0 or a: 1.
This is a shame, because the value you were looking for was returned to you by countSubstring yet you chose to discard it.
Your code can be fixed this simply:
int count = countSubstring(token, "a");
std::cout << "a: " << count << '\n';
As in:
while((token = str.next()) != "")
{
int count = countSubstring(token, "a");
std::cout << "a: " << count << '\n';
}