I would like to make an array of words like: "Tom", "Mike","Tamara","Nik"... I would like to make for user to be possible to enter for instance a number 3, and get a random return of words that have the length of 3 so eather ("Tom" or "Nik"). I think this is done with pointers but I don't know how. Words should be stored in different arrays depending on their length. And with pointers you would point to each array ("Tom","Nik" in same array "Tamara" in different array and "Mike" in different array and so on... because their length is not the same). Can someone please help ?
#include<iostream>
#include <string>
using namespace std;
void IzpisPolja(char **polje,int velikost){
int tab[100];
for (int i=0; i<velikost; i++) {
cout<<polje[i]<<endl;
char *zacasni;
tab[i] = strlen(polje[i]);
// cout<<tab[i]<<endl;
}
}
int main(){
const int size = 4;
char* tabelaOseb[size] = {"Tom", "Mike","Tamara","Nik"};
IzpisPolja(tabelaOseb,size);
return 0;
}
Do you want to do it efficiently ? Storing them in separate arrays will increase search time but also increase insertion, deletion complexity.
Otherwise you can just count number of instances of n length words in an array, then generate random number and return the ith of them.
Also suggest using std::vector
const string* getRandNameOfLength(const string* arr,
const int arrlen,
const int length)
{
int num = 0, j, i;
// Counting number of such names
for (i = 0; i < arrlen; ++i)
{
if (arr[i].size() == length)
num++;
}
// No such name found
if (num == 0)
return NULL;
j = rand() % num;
// Returning random entry of given length
for (i = 0; i < arrlen; ++i)
{
if (arr[i].size() == length && j-- == 0)
return &arr[i];
}
// Function shouldn't get here
return NULL;
}
You can use raw pointers to perform your task, of course, but you can also start using some of the many safer facilities that the language (references, iterators and smart pointers) and the C++ standard library can offer.
I'll show you a complete program that can do what you are asking using conteiners (std::vector, std::map) and algorithms (like std::lower_bound) that can really simplify your work once understood.
Note that as a learning exercise (for both of us), I have used as many "new" features as I could, even when maybe wasn't necessary or handy. Read the comments for better understanding.
The words are stored and managed in a class, while the interaction with the user is performed in main().
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <limits>
#include <algorithm>
#include <random> // for mt19937, uniform_int_distribution
#include <chrono> // for high_resolution_clock
size_t random_index( size_t a, size_t b ) {
// Initialize Random Number Generator Engine as a static variable. - Since c++11, You can use those instead of old srand(time(NULL))
static std::mt19937 eng{static_cast<long unsigned int>(std::chrono::high_resolution_clock::now().time_since_epoch().count())};
// use the RNG to generate random numbers uniformly distributed in a range
return std::uniform_int_distribution<size_t>(a,b)(eng);
}
using svs_t = std::vector< std::string >; // I store the words with equal length in a std::vector of std::string
// like typedef, I'll use svs_t instead of std::vector<std::string>
auto string_less_then = [] (const std::string & a, const std::string & b) -> bool { return a.compare(b) < 0; };
// A lambda function is a mechanism for specifying a function object, its primary use is to specify a simple
// action to be performed by some function. I'll use it to compare two string and return true only if a<b
class word_table {
std::map< size_t, svs_t > words; // std::map store elements formed by a combination of a key value and a mapped value, sorted by key
// I'll use word's length as a key for svs_t values
public:
word_table() {}
word_table( std::initializer_list<std::string> vs ) {
insert_words(vs);
}
void insert_words( svs_t vs ) {
for ( auto && s : vs ) add_word(s); // loop for each value in vs, "auto" let the compiler infer the right type of the variable
}
bool add_word( std::string s ) { // I choose to keep the vector sorted and with unique elements
size_t sl = s.length();
if ( sl > 0 ) {
auto & v = words[sl]; // If sl doesn't match the key of any element in the map, a new element is created
// lower_bound return an iterator that poins to the first element in range (begin,end)
auto it = std::lower_bound(v.begin(), v.end(), s, string_less_then); // which does not compare less than s
// I pass the compare function as a lambda
if ( it != v.end() && it->compare(s) == 0 ) return false; // Already present, duplicates not allowed
v.insert(it, s); // Not the most efficient way, but you seem focused on the random access part
return true;
}
return false;
}
bool remove_word( std::string s) {
size_t sl = s.length();
if ( sl > 0 ) {
auto itvw = words.find(sl); // first find the right element in the map, using the string length as a key, but if word is found
if ( itvw == words.end() ) return false; // an iterator to the element following the last element of the container is returned
auto & v = itvw->second; // In a map the elements are stored in pairs, first is the key, second the value
auto it = std::lower_bound(v.begin(), v.end(), s, string_less_then);
if ( it != v.end() && it->compare(s) == 0 ) {
v.erase(it);
if ( v.empty() ) words.erase(itvw);
return true;
}
}
return false;
}
std::string get_random_word( size_t length ) {
if ( length == 0 ) return "";
auto itvw = words.find(length);
if ( itvw == words.end() || itvw->second.empty() ) return "";
return itvw->second[random_index(0, itvw->second.size() - 1)];
}
void show_all() {
for ( auto && i : words ) {
std::cout << " ";
for (auto && w : i.second ) {
std::cout << w << ' ';
}
std::cout << '\n';
}
}
};
constexpr size_t ss_max = std::numeric_limits<std::streamsize>::max();
namespace opt {
enum options { wrong = -1, exit, show, random, add, remove, menu };
}
class menu {
std::map<int,std::string> opts;
public:
menu( std::initializer_list<std::pair<int,std::string>> il ) {
for ( auto && i : il ) opts.insert(i);
}
void show() {
std::cout << "\nYou can choose among these options:\n\n";
for ( auto && i : opts ) {
std::cout << " " << i.first << ". " << i.second << ".\n";
}
}
};
int main()
{
word_table names({"Tom", "Mike","Tamara","Robert","Lenny","Nick","Alex","Sue","Irina","Beth","Anastacia","Bo"});
int choise = opt::exit;
menu menu_options { {opt::exit, "Exit program"}, {opt::show, "Show all stored names"},
{opt::random, "Show a random name"}, {opt::add, "Add a new name"},
{opt::remove, "Remove a name"} };
menu_options.show();
do {
std::cout << "\nPlease, enter a number (" << opt::menu << " to show again all options): ";
std::cin >> choise;
if ( std::cin.fail() ) { // the user enter something that is not a number
choise = opt::wrong;
std::cin.clear();
std::cin.ignore(ss_max,'\n');
}
if ( std::cin.eof() ) break; // use only if you are redirecting input from file
std::string str;
switch ( choise ) {
case opt::exit:
std::cout << "\nYou choose to quit, goodbye.\n";
break;
case opt::show:
std::cout << "\nAll the stored names, classified by word\'s length:\n\n";
names.show_all();
break;
case opt::random:
size_t l;
std::cout << "Please, enter the length of the name: ";
std::cin >> l;
if ( std::cin.good() ) {
std::string rs = names.get_random_word(l);
if ( rs == "" ) {
std::cout << "\nNo name of length " << l << " has been found.\n";
} else {
std::cout << "\n " << rs << '\n';
}
}
break;
case opt::add:
std::cout << "Please, enter the name You want to add: ";
std::cin >> str; // read a string from cin, you can write more than a word (separeted by spaces)
std::cin.ignore(ss_max,'\n'); // but only the first is stored
if ( names.add_word(str) ) {
std::cout << "\n The name " << str << " has been successfully added.\n";
} else {
std::cout << "\n No name has been added";
if ( str != "" ) std::cout << ", "<< str << " is already present.\n";
else std::cout << ".\n";
}
break;
case opt::remove:
std::cout << "Please, enter the name You want to remove: ";
std::cin >> str;
if ( names.remove_word(str) ) {
std::cout << "\n " << str << " has been succesfully removed.\n";
} else {
std::cout << "\n No name has been removed";
if ( str != "" ) std::cout << ", " << str << " wasn't found.\n";
else std::cout << ".\n";
}
break;
case opt::menu:
menu_options.show();
break;
default:
std::cout << "\n Sorry, that's not an option.\n";
}
} while ( choise != opt::exit );
return 0;
}
I hope it could help.
#include <iostream>
#include <vector>
#include <cstring>
#include <ctime>
#include <cstdlib>
using namespace std;
const char* return_rand_name(const char** names, size_t length)
{
std::vector<size_t> indexes;
for(int i=0; names[i][0] != 0; ++i)
if(strlen(names[i]) == length)
indexes.push_back(i);
if(indexes.size()==0)
return NULL;
return names[indexes[rand()%indexes.size()]];
}
int main()
{
srand(time(NULL));
const char* names[] = {"Alex","Tom","Annie","Steve","Jesus","Leo","Jerry",""};
std::cout << return_rand_name(names, 3) << std::endl;
return 0;
}
And if you want to use functions like strlen etc, include <cstring>, not <string> (which contains class template std::string (which you should use in C++ (instead of char*) ) )
Related
I've got a large text document that including adjacent numbers and letters.
Just like that,
JACK1940383DAVID30284HAROLD68372TROY4392 etc.
How can i split this like below in C++
List: Jack / 1940383 , David/30284, ...
You can use std::string::find_first_of() and std::string::find_first_not_of() in a loop, using std::string::substr() to extract each piece, eg:
std::string s = "JACK1940383DAVID30284HAROLD68372TROY4392";
std::string::size_type start = 0, end;
while ((end = s.find_first_of("0123456789", start)) != std::string::npos) {
std::string name = s.substr(start, end-start);
start = end;
int number;
if ((end = s.find_first_not_of("0123456789", start)) != std::string::npos) {
number = std::stoi(s.substr(start, end-start));
}
else {
number = std::stoi(s.substr(start));
}
start = end;
// use name and number as needed...
}
Online Demo
You can use regex like this:
#include <iostream>
#include <string>
#include <regex>
#include <vector>
// create a struct to group your data
// this makes it easy to store it in a vector.
struct person_t
{
std::string name;
std::string number;
};
// overloaded output operator for printing one person's details
std::ostream& operator<<(std::ostream& os, const person_t& person)
{
std::cout << person.name << ": " << person.number << std::endl;
return os;
}
// get a vector of person_t based on the input
auto get_persons(const std::string& input)
{
// make a regex in this case a regex that will match one or more capital letters
// and groups them using the ()
// then match one or more digits and group them too.
static const std::regex rx{ "([A-Z]+)([0-9]+)" };
std::smatch match;
// a vector to hold all the persons
std::vector<person_t> persons;
// start at begin of string and look for first part of the string
// that matches the regex.
auto cbegin = input.cbegin();
while (std::regex_search(cbegin, input.cend(), match, rx))
{
// match[0] will contain the whole match,
// match[1]-match[n] will contain the groups from the regular expressions
// match[1] will contain the match with characters and thus the name
// match[2] will contain the match with the numbers and thus the number.
// create a person_t struct with this info
person_t person{ match[1], match[2] };
// and add it to the vector
persons.push_back(person);
cbegin = match.suffix().first;
}
return persons;
}
int main()
{
// parse and split the string
auto persons = get_persons("JACK1940383DAVID30284HAROLD68372TROY4392");
// show the output
for (const auto& person : persons)
{
std::cout << person;
}
}
As pointed in other good answers you can use
find_first_of(), find_first_not_of() and substr() from std::string in a loop
regex
But it may be too much. I will add 3 more examples that you may find
simpler.
The first 2 programs expects the file name on the command line for (my) convenience here, and the test file is in.txt. Contents are the same as posted
JACK1940383DAVID30284HAROLD68372TROY4392
The last example just parses the string data declared as a char[]
1. Using fscanf()
Since the target is to consume formatted data, fscanf() is an option. As the data structure is very simple, the program is just a one line loop:
char mask[] = "%50[^0-9]%50[0-9]";
while ( 2 == fscanf(F, mask, tk_key, tk_value))
std::cout << tk_key << "/" << tk_value << "\n";
program output
output is the same for all examples
JACK/1940383
DAVID/30284
HAROLD/68372
TROY/4392
code for ex. 1
#include <errno.h>
#include <iostream>
int main(int argc,char** argv)
{
if (argc < 2)
{ std::cerr << "Use: pgm FileName\n";
return -1;
}
FILE* F = fopen(argv[1], "r");
if (F == NULL)
{
perror("Could not open file");
return -1;
}
std::cerr << "File: \"" << argv[1] << "\"\n";
char tk_key[50], tk_value[50];
char mask[] = "%50[^0-9]%50[0-9]";
while ( 2 == fscanf(F, mask, tk_key, tk_value))
std::cout << tk_key << "/" << tk_value << "\n";
fclose(F);
return 0;
}
using a state machine
There are just 2 states so it is not a fancy FSA ;) State machines are good for representing this kind of stuff, albeit here this seems to be overkill.
#define S_LETTER 0
#define S_DIGIT 1
#include <algorithm>
#include <iostream>
#include <fstream>
using iich = std::istream_iterator<char>;
int main(int argc,char** argv)
{
std::ifstream in_file{argv[1]};
if ( not in_file.good()) return -1;
iich p {in_file}, eofile{};
std::string token{}; // string to build values
char st = S_LETTER; // state value for FSA
std::for_each(p, eofile,
[&token,&st](char ch)
{
char temp = 0;
switch (st)
{
case S_LETTER:
if ((ch >= '0') && (ch <= '9'))
{
std::cout << token << "/";
token = ch;
st = S_DIGIT; // now in number
}
else token += ch; // concat in string
break;
case S_DIGIT:
default:
if ((ch < '0') || (ch > '9'))
{ // is a letter
std::cout << token << "\n";
token = ch;
st = S_LETTER; // now in name
}
else token += ch; // concat in string
break;
}; // switch()
});
std::cout << token << "\n"; // print last token
}
Here we have no loop. for_each gets the data from an iterator and passes it to a function that builds the name and the value as strings and couts them
Output is the same
3. a simple FSA to consume the data
#define S_LETTER 0
#define S_DIGIT 1
#include <iostream>
int main(void)
{
char one[] = "JACK1940383DAVID30284HAROLD68372TROY4392";
char* p = (char*)&one;
char* token = p;
char st = S_LETTER;
char temp = 0;
while (*p != 0)
{
switch (st)
{
case S_LETTER:
if ((*p >= '0') && (*p <= '9'))
{
temp = *p;
*p = 0;
std::cout << token << "/";
*p = temp;
token = p;
st = S_DIGIT; // now in number
}
break;
case S_DIGIT:
default:
if ( (*p < '0') || (*p > '9'))
{ // letter
temp = *p;
*p = 0;
std::cout << token << "\n";
*p = temp;
token = p;
st = S_LETTER; // now in name
}
break;
}; // switch()
p += 1; // next symbol
}; // while()
std::cout << token << "\n"; // print last token
}
This code just uses a C-style loop to parse the input data
#include <iostream>
#include <string>
using namespace std;
void ReverseString(string &S, int size)
{
static int start = 0;
if (start == size - 1 || start == size)
{
return;
}
else
{
swap(S[start++], S[size - 1]);
ReverseString(S, size - 1);
}
}
int main()
{
cout << "enter a string to reverse" << endl;
string s;
getline(cin, s);
cout << "Before Reversing" << endl;
cout << s << endl;
ReverseString(s, s.size());
cout << "After Reversing" << endl;
cout << s << endl;
return 0;
}
I am trying to nail recursions as much as i can,and i was trying to reverse a string using recursion
i didn't know how to do it at first,tried many different ways to do it,but i saw code samples on string reversing,but none of it made sense to me,so i made my own one,but not quite sure of it,i'm just asking for opinion,is it clean and functional??
Thank You
Using a function local static variable in a recursive function is a bad idea. Recursive functions should get all their state as input arguments.
Here's a simplified version that divides the logic into two functions.
void ReverseString(string &S, int start, int end)
{
if ( start < end )
{
swap(S[start], S[end - 1]);
ReverseString(S, start+1, end - 1);
}
}
void ReverseString(string &S)
{
ReverseString(S, 0, S.size());
}
Most of the time, higher level functions would only call the second function. The first function can be called from a higher level function if there is a need to reverse only a subset of a string.
Here's a sample program
#include <iostream>
#include <string>
using namespace std;
void ReverseString(string &S, int start, int end)
{
if ( start < end )
{
swap(S[start], S[end - 1]);
ReverseString(S, start+1, end - 1);
}
}
void ReverseString(string &S)
{
ReverseString(S, 0, S.size());
}
int main()
{
string s = "The string to reverse" ;
cout << "Before Reversing" << endl;
cout << s << endl;
ReverseString(s);
cout << "After Reversing" << endl;
cout << s << endl;
ReverseString(s, 0, 7);
cout << "After Reversing a subset" << endl;
cout << s << endl;
return 0;
}
and its output
Before Reversing
The string to reverse
After Reversing
esrever ot gnirts ehT
After Reversing a subset
reverse ot gnirts ehT
See it working at https://ideone.com/9nMlsP.
is it ... functional??
If by "functional" you mean "does it work", then you tell me.
If you mean "functional" as in "functional" programming style, then no it isn't. In functional style, you don't modify arguments in place, but instead return a new value. Also relying on global state (i.e. static objects) is very anti-functional.
Here is an example:
std::string
ReverseString(std::string_view sv)
{
if (sv.empty())
return "";
std::string_view x = sv.substr(0, 1)
std::string_view xs = sv.substr(1);
return ReverseString(xs) + x;
}
// usage
s = ReverseString(s);
In future, if Pattern matching was introduced to the language, then it could potentially be written like this:
std::string
ReverseString(std::string_view sv)
{
inspect(sv) {
"": return "";
[x:xs]: return ReverseString(xs) + x;
}
}
However, the current proposal does not suggest introducing support for matching ranges like this, so this is highly theoretical.
Local static variables are dangerous. Since their state will remain between function calls. In my approach i used slen as the length of a string and the currentIndex as the last swapped index on the string. Since it is enough to swap till the middle of the string, finish case is when (currentIndex == slen/2).
I also added some test cases as an example.(even length, odd length, zero case and palindrome)
#include <iostream>
#include <string>
using namespace std;
void ReverseString(string &S, int currentIndex, int slen)
{
if (slen / 2 == currentIndex) return;
swap(S[currentIndex], S[slen - 1 - currentIndex]);
currentIndex++;
ReverseString(S, currentIndex, slen);
}
void testReverseString() {
string s = "";
ReverseString(s, 0, s.length());
assert(s == "");
s = "ahmet";
ReverseString(s, 0, s.length());
assert(s == "temha");
s = "ahaha";
ReverseString(s, 0, s.length());
assert(s == "ahaha");
s = "haha";
ReverseString(s, 0, s.length());
assert(s == "ahah");
}
int main()
{
testReverseString();
return 0;
}
Your function with a static variable can be called only once because after its recursive calls the static variable start will not be equal to 0 as it is required. So the function is not "functional".
Here is a demonstrative program that shows how the function can be written with using a static variable and without using a static variable.
#include <iostream>
#include <string>
#include <utility>
void ReverseString1( std::string &s )
{
static std::string::size_type i = 0;
if ( not ( s.size() - 2 * i < 2 ) )
{
std::swap( s[i], s[s.size() - i - 1] );
++i;
ReverseString1( s );
--i;
}
}
void ReverseString2( std::string &s, std::string::size_type pos = 0 )
{
if ( not ( s.size() - 2 * pos < 2 ) )
{
std::swap( s[pos], s[s.size() - pos - 1] );
ReverseString2( s, pos + 1 );
}
}
int main()
{
std::string s( "Hello World!" );
std::cout << s << '\n';
ReverseString1( s );
std::cout << s << '\n';
ReverseString2( s );
std::cout << s << '\n';
return 0;
}
The program output is
Hello World!
!dlroW olleH
Hello World!
Anyone can reverse a string one char at a time, but much cooler is to reverse each third of the string and swap the outer thirds. This cuts stack depth as well as sowing confusion amongst the competition. Note that max stack depth of recursion per character is N, whereas this is cube root of N.
#include <iostream>
#include <string>
using namespace std;
void ReverseRegion(string &s, int start, int sz)
{
// regions < 2 need no action
if (sz == 2) {
char tmp = s[start];
s[start] = s[start+1];
s[start+1] = tmp;
} else if (sz > 2) {
int s3 = sz/3;
ReverseRegion(s, start, s3);
string tmp = s.substr(0,start) + s.substr(start+sz-s3,s3) + s.substr(start+s3, sz-2*s3) + s.substr(start,s3) + s.substr(start+sz);
// cout << "do: " << tmp << "\n";
s = tmp;
ReverseRegion(s, start+s3, sz-2*s3);
ReverseRegion(s, start, s3);
}
}
void ReverseString(string &S)
{
ReverseRegion(S, 0, S.size());
}
int main()
{
cout << "enter a string to reverse" << endl;
string s;
getline(cin, s);
cout << "Before Reversing" << endl;
cout << s << endl;
ReverseString(s);
cout << "After Reversing" << endl;
cout << s << endl;
return 0;
}
Let's say I have a string array with 5 words and I want to only output the first 3 letters of each word. How do I go upon doing this? I know how to do it with one string but with an array of strings I get lost.
This is how to do it with one string
std::string test = "hello";
std::cout << test << std::endl;
test = test.substr(0,3);
std::cout << test << std::endl;
What I want to do is this
std::string test[5] = {"hello", "pumpkin", "friday", "snowboard", "snacks"};
I want to cout the first 3 letters of each word. I tried test[5] = test[5].substr(0,3); and that did not work.
test[5] doesn't work because you only have 5 items in your array, only indexes 0 to 4 are valid.
Generally with arrays you need to write a loop to go through each array item in turn, for instance
for (int i = 0; i < 5; ++i)
test[i] = test[i].substr(0,3);
for (int i = 0; i < 5; ++i)
cout << test[i] << endl;
With test[5] you are reading out of bounds thus invoking undefined behavior. Arrays in C++ are zero indexed so the last element would be test[4]. Create a function that utilizes for example the std::next function or string's substr member function. Call inside a range based loop:
#include <iostream>
#include <string>
void foo(const std::string& s) {
if (s.size() >= 3) {
std::cout << std::string(s.begin(), std::next(s.begin(), 3)) << '\n';
// or simply:
std::cout << s.substr(0, 3) << '\n';
}
}
int main() {
std::string test[5] = { "hello", "pumpkin", "friday", "snowboard", "snacks" };
for (const auto& el : test) {
foo(el);
}
}
test[5] = test[5].substr(0,3); won't work and more over you don't have `test[5]`, index starts from `0`.
you may want to do like this
for(int i=0 ; i<5; i++) {
test[i] = test[i].substr(0,3);
cout << test[i] << endl;
}
substr is what you are looking for. Here is my implementation.
#include <array>
#include <string>
#include <iostream>
int main () {
std::array<std::string,5> list {"hello", "pumpkin", "friday", "snowboard", "snacks"};
for (const auto &word : list){
std::cout << word << std::endl;
}
for (auto &word : list){
word = word.substr(0,3);
}
for (const auto &word : list){
std::cout << word << std::endl;
}
}
Use the standard library.
std::for_each(std::begin(test), std::end(test), [] (auto& s) { s.erase(3); });
Or even a simple range-based for loop:
for (auto&& s : test) {
s.erase(3); // Erase from index 3 to end of string.
}
Or maybe even create another container with views of the original strings:
auto test2 = std::accumulate(std::begin(test), std::end(test),
std::vector<std::string_view>{},
[] (auto& prev, std::string_view sv) -> decltype(prev)& {
prev.push_back(sv.substr(0, 3));
return prev;
});
I need help for my university homework. i'm still new to this.
Basically i am doing a run-length encoding and i don't know how to add the letter after the counter:
#include <iostream>
#include <string>
#include <vector>
#include <stdexcept>
void error(std::string str)
{
throw std::runtime_error(str);
}
int main()
{ int counter = 1;
std::string id;
std::vector<int> v;
std::cout << "Enter the data to be compressed: ";
std::cin >> id;
try
{ for(int i = 0; i < id.size(); i++)
{
if(std::isdigit(id[i]))
error("invalid input");
}
std::cout << "The compressed data is: ";
for(int i = 0; i < id.size(); i++)
{
if(id[i] == id[i+1])
{
counter++;
}
else if(id[i]!= id[i+1])
{
v.push_back(counter);
v.push_back(id[i]);
counter=1;
}
}
for(int j = 0; j < v.size(); j++)
std::cout << v[j];
}
catch(std::runtime_error& str)
{
std::cerr << "error: " << str.what() << std::endl;
return 1;
}
return 0;
}
For example if i input aaabbb, the probram should output 3a3b. The problem is that it outputs 397398 97 and 98 being the ascii code for a and b.
i don't know how to put the letter after the counter and for them to be in the same vector.
If you want to serialize as a string try this :
#include <iostream>
#include <string>
#include <vector>
#include <stdexcept>
#include <sstream>
void error(std::string str) {
throw std::runtime_error(str);
}
int main() {
std::ostringstream stream;
int counter = 1;
std::string id;
std::cout << "Enter the data to be compressed: ";
std::cin >> id;
try {
for (int i = 0; i < id.size(); i++) {
if (std::isdigit(id[i]))
error("invalid input");
}
std::cout << "The compressed data is: ";
for (int i = 0; i < id.size(); i++) {
if (id[i] == id[i + 1]) {
counter++;
} else if (id[i] != id[i + 1]) {
stream << counter;
stream << (char) id[i];
counter = 1;
}
}
std::cout << stream.str() << std::endl;
} catch (std::runtime_error& str) {
std::cerr << "error: " << str.what() << std::endl;
return 1;
}
}
v[j] from std::cout << v[j] is of type int and that is why std::cout writes a number. To write it as a character, you should cast v[j] to char as follows: std::cout << (char)v[j]. In this way, std::cout will use the char specialization, not the int one.
While the other answers might give you the output you need, I believe the idiomatic way to solve this is using a class to hold both the character and its count. There are two obvious choices.
std::pair
Could also be std::tuple if you prefer it for consistency or whatever reason. Save your results in a std::vector<std::pair<char, int>. This saves the information, but to print it you would need to define an appropriate function. Add elements via
v.emplace_back(character, count);
Wrapper Class
If you want to offer some functionality without outside helper classes, define a custom wrapper class such as the following.
class CharacterCount {
private:
char character;
int count;
public:
CharacterCount(char character, int count):
character(character), count(count) {}
explicit operator std::string() const { return std::to_string(count) + character;
// Other helper functions or constructors you require
}
This simplifies printing
for (auto& character_count : v)
std::cout << static_cast<std::string>(character_count);
I believe because std::ostream::operator<< is templated, you cannot get an implicit conversion to std::string to work. I would advise against implicit conversion anyway.
You can use the same emplace_back syntax as before because we offer an appropriate constructor.
So you take your input in a string and ultimately just need to stream this information out, ultimately meaning there's really no reason to store the information in a vector, just output it! You can use find_if with a lambda to find the non-consecutive character (or find_if_not if you prefer.)
for(string::const_iterator finish, start = cbegin(id); start != cend(id); start = finish) {
finish = find_if(start, cend(id), [value = *start](const auto i) { return i != value; } );
cout << distance(start, finish) << *start;
}
Live Example
I have a little problem with a few simple lines of code.
Following lines I used to call my method:
char** paras = new char*;
inputLength = charUtils::readParameterFromConsole(paras, paraCount, stringBeginningIndex);
The method looks like following:
int charUtils::readParameterFromConsole(char** &inputs, int ¶Count, int &stringBeginningIndex) {
char input[BUFFER_STRING_LENGTH];
cin.getline(input, BUFFER_STRING_LENGTH);
if(strlen(input) > 0)
{
bool stringBeginning = false;
char* part = "";
string partString = "";
for(int i = 0; i < paraCount; i++)
{
if (i == 0)
part = strtok(input, " ");
else
part = strtok(NULL, " ");
inputs[i] = part;
}
} else
{
cout << "Error! No Input!" << endl;
}
cout << &inputs[0] << endl;
cout << inputs[0] << endl;
return strlen(input);
}
In the method readParameterFromConsole are the values correct, but in the calling method they aren't correcy any longer.
I am facing that problem since I refactored the code and make an new class.
Can anyone give me an advice please?
You are passing back pointers into a stack allocated variable, input when you say inputs[i] = part, because part is a pointer into input handed back by strtok.
http://www.cplusplus.com/reference/clibrary/cstring/strtok/
Your code as I'm writing this:
int charUtils::readParameterFromConsole(char** &inputs, int ¶Count, int &stringBeginningIndex) {
char input[BUFFER_STRING_LENGTH];
cin.getline(input, BUFFER_STRING_LENGTH);
if(strlen(input) > 0)
{
bool stringBeginning = false;
char* part = "";
string partString = "";
for(int i = 0; i < paraCount; i++)
{
if (i == 0)
part = strtok(input, " ");
else
part = strtok(NULL, " ");
inputs[i] = part;
}
} else
{
cout << "Error! No Input!" << endl;
}
cout << &inputs[0] << endl;
cout << inputs[0] << endl;
return strlen(input);
}
A main problem is that you're setting inputs[i] = pointer into local array. That array doesn't exist anymore when the function returns. Undefined behavior if you use any of those pointers.
As I understand it you want an array of "words" as a result.
That's easy to arrange (note: code untouched by compiler's hands):
#include <vector>
#include <string>
#include <sstream>
#include <stdexcept>
bool throwX( char const s[] ) { throw std::runtime_error( s ); }
typedef std::vector<std::string> StringVector;
std::string lineFromUser()
{
std::string line;
std::getline( cin, line )
|| throwX( "lineFromUser failed: std::getline failed" );
return line;
}
void getWordsOf( std::string const& s, StringVector& result )
{
std::istringstream stream( s );
std::string word;
StringVector v;
while( stream >> word )
{
v.push_back( word );
}
result.swap( v );
}
StringVector wordsOf( std::string const& s )
{
StringVector result;
getWordsOf( s, result );
return result;
}
// Some call, like
StringVector const words = wordsOf( lineFromUser() );
Again, this is off the cuff code, please just correct any syntax erors.
Cheers & hth.,