From a text file, I read several values like
const float vLowCut = cfg.get<float>("LowCut");
const float vLowCut1 = cfg.get<float>("LowCut1");
...
How would it be possible to append those in a const char array ie like
const char * CutList[2] = {"value"+vLowCut, "value2"+vLowCut2)
Of course the above line does not work, just wanted to demonstrate what I want to have.
thanks
It would be difficult to use const char * here, since you'd need to create an array for it to point to. A C++ idiom would be more convenient:
std::string CutList[2] = {
"value" + std::to_string(vLowCut),
"value2" + std::to_string(vLowCut2)
};
Us the to_string() function like this:
std::string CutList = {"value: "+ std::to_string(vLowCut), " value2: " + std::to_string(vLowCut2))
Have you considered using std::string? You can do that with std::to_string:
std::string CutList[2] = {"value" + std::to_string(vLowCut),
"value2" + std::to_string(vLowCut2));
If you want to use explicit format specifiers, you may use std::stringstream instead i.e. like this:
#include <iostream>
#include <string>
#include <iomanip>
auto my_float_to_string =
[](float f){ std::stringstream ss;
ss << std::setprecision(3) << f;
return ss.str(); };
std::string CutList[2] = {"value" + my_float_to_string(vLowCut),
"value2" + my_float_to_string(vLowCut2));
You may use c_str() to convert it back to const char*:
strstr(CutList[0].c_str());
But do not use c_str() as a permanent storage, it will be destroyed once std::string object was destroyed. I.e.:
void f(int a) {
const char* s = nullptr;
if(a == 10) {
s = std::string("aaaa").c_str(); }
// Here s may point to invalid data because
// corresponding std::string object has been destroyed
}
Related
I'm a newbie in C++ learning the language and playing around. I wrote a piece of code which behavior I don't understand. Could someone explain why the code below prints out random junk and not the first character of the first string in the list (that is a).
#include <iostream>
#include <vector>
#include <string>
#include <cstdlib>
#include <ctime>
#include <climits>
#include <stdio.h>
char* str2char(std::string str)
{
char cset[str.size()+1]; // +1 for the null character
for(int i = 0; i < str.size(); i++)
{
cset[i] = str[i];
}
cset[str.size()] = '\0';
return cset;
}
int main (int argc, char * const argv[]) {
std::vector< std::string > ladontakadet;
ladontakadet.push_back("aabcbbca");
ladontakadet.push_back("abcdabcd");
ladontakadet.push_back("cbbdcdaa");
ladontakadet.push_back("aadcbdca");
ladontakadet.push_back("cccbaaab");
ladontakadet.push_back("dabccbaa");
ladontakadet.push_back("ccbdcbad");
ladontakadet.push_back("bdcbccad");
ladontakadet.push_back("ddcadccb");
ladontakadet.push_back("baccddaa");
std::string v = ladontakadet.at(0);
char *r;
r = str2char(v);
std::cout << r[0] << std::endl;
return 0;
}
Why is my returning garbage, when I'm expecting it to output a?
Thnx for any help!
P.S. The output of this code is random. It doesn't always print the same character..:S
It's because you return a pointer to a local variable, a local variable that goes out of scope when the function returns.
You are already using std::string for the argument, use it instead of the array and the return pointer.
If your aim is to pass the content of a std::string to a function modifying the content of a char*:
#include <iostream>
#include <vector>
void f(char* s) {
s[0] = 'H';
}
std::vector<char> to_vector(const std::string& s) {
return std::vector<char>(s.c_str(), s.c_str() + s.size() + 1);
}
int main(void)
{
std::string s = "_ello";
std::vector<char> t = to_vector(s);
f(t.data());
std::cout << t.data() << std::endl;
}
Your function is returning garbage because you're returning the address of a local variable which goes out of scope after your function returns. It should probably look like this:
char* str2char(const std::string &str)
{
char *const cset = new char[str.size() + 1]; // +1 for the null character
strcpy(cset, str.c_str());
return cset;
}
You will need to delete your variable r by doing delete[] r;. Ideally though you wouldn't be using raw pointers, and you would use std::string for everything, or wrap the char * in a std::unique_ptr.
I have a function in my code called buildPacket that takes some parameters, and converts them into a char* and adds them together using a std::vector<char> and at the end returns the result as a char*. The problem is that after I convert the vector to a char* all characters become a weird character.
I tried using other ways of converting the vector to a char*, like with using reinterpret_cast<char*>. When I print the contents of the vector from inside the function, I get the expected result so the problem is with the conversion.
The function's code:
char* buildPacket (int code, std::string data)
{
char* codeBytes = CAST_TO_BYTES(code);
std::vector<char> packetBytes(codeBytes, codeBytes + sizeof(char));
size_t dataLength = data.size() + 1;
char* dataLengthBytes = CAST_TO_BYTES(dataLength);
packetBytes.insert(packetBytes.end(), dataLengthBytes, dataLengthBytes + sizeof(int));
const char* dataBytes = data.c_str();
packetBytes.insert(packetBytes.end(), dataBytes, dataBytes + dataLength);
return &packetBytes[0];
}
The CAST_TO_BYTES macro:
#define CAST_TO_BYTES(OBJ) static_cast<char*>(static_cast<void*>(&OBJ));
The intent of the function is to take the input and build a packet out of it to send through a socket later on, the packet's format consists of a 1-byte long code, 4-byte long data length and data with variable length.
The input I gave it is code = 101 and data = "{\"password\":\"123456\",\"username\":\"test\"}"
This is the result I am getting when printing the characters: ▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌
EDIT: Thanks for all the help, I've returned a vector<char> at the end as suggested and took a different approach in converting to values to a char*.
You're returning a pointer to something inside of a local variable. You should change your code to have your vector<char> alive outside of your buildPacket function (such as by returning it instead of the char*).
You might try this solution. I thing using STL makes it more clearer what you are trying to achieve. There was also an undefined reference in your code, that could lead to unpredictable crashes.
#include <iostream>
#include <string>
#include <vector>
#include <iterator>
// Better return std::vector<char>
char* buildPacket(int code, const std::string& data)
{
auto result = data;
result.append(1, static_cast<char>(code));
char* ret = new char[data.size() + 2];
ret[data.size() + 1] = '\0';
std::copy(result.begin(), result.end(), ret);
return ret;
}
std::vector<char> buildPacketStl(int code, const std::string& data)
{
std::vector<char> ret;
std::copy(data.begin(), data.end(), std::back_inserter(ret));
ret.push_back(static_cast<char>(code));
return ret;
}
int main() {
std::cout << buildPacket(65, "test") << std::endl;; // 65 -> A
auto stl= buildPacketStl(65, "test"); // 65 -> A
std::copy(stl.begin(), stl.end(), std::ostream_iterator<char>(std::cout, ""));
std::cout << std::endl;
}
I'm new to C++ strings. Is there a way to concatenate more than two strings at once? Or I have to concat two strings at the time? My concern is that it probably needs to allocate memory on each operation, instead of only once for the final result.
Remember something like that in java, wonder if there is a method for that somewhere in std. Actually, std::stringstream might be it, but I don't know how exactly it works.
You can reserve the required space beforehand, avoiding reallocations.
std::string s0{/*...*/}, s1{/*...*/}, s2{/*...*/};
std::string sink;
sink.reserve(s0.size() + s1.size() + s2.size() + 1);
sink += s0;
sink += s1;
sink += s2;
You can make this nicer with a variadic string_cat function. Here's a C++17 implementation:
template <typename... Strings>
std::string string_cat(Strings&&... strings)
{
std::string result;
result.reserve((strings.size() + ...) + 1);
((result += std::forward<Strings>(strings)), ...);
return result;
}
Usage:
using namespace std::literals;
auto res = string_cat("a"s, "b"s, "c"s);
live example on wandbox
Yes, to avoid expensive memory waste use the stringstream
std::stringstream might be it, but I don't know how exactly it works.
this is how it works: include the sstream, create an object, append to it as much as you need using the stream operator << get the result as string calling the function str
example:
#include <sstream>
std::stringstream mySS;
mySS << "Hello" << " World!" << "end!!" << " Foo";
and when you are done
std::string stringResult = mySS.str();
You are correct that each concatenation could require an allocation. Consider an expression like this, where each variable is a string:
a = b + c + d + e;
This requires three concatenation operations and three temporaries, each of which requires a new allocation.
A simple solution is to use std::ostringstream, which should not require as many reallocations:
std::ostringstream ss;
ss << b << c << d << e;
a = ss.str();
However, if we are concatenating only strings, we can do one better, and allocate exactly the right size of string (C++11-compatible implementation):
std::size_t total_string_size()
{
return 1;
}
template <typename... T>
std::size_t total_string_size(std::string const &s, T const & ...tail)
{
return s.size() + total_string_size(tail...);
}
void concat_strings_impl(std::string &) { }
template <typename... T>
void concat_strings_impl(std::string &out, std::string const &s, T const & ...tail)
{
out += s;
concat_strings_impl(out, tail...);
}
template <typename... T>
void concat_strings(std::string &out, T const & ...strings)
{
out.clear();
out.reserve(total_string_size(strings...));
concat_strings_impl(out, strings...);
}
Now we can call concat_strings(a, b, c, d, e) to perform the equivalent of a = b + c + d + e; in a single reallocation. (Demo)
The only way to avoid multiple allocations is to tell the string how big it needs to become before concatenating.
For example, to join together all the strings stored in a vector (like a list):
std::string join(std::vector<std::string> const& v)
{
std::string r; // return string
// add up all the string sizes
std::size_t size = 0;
for(auto const& s: v)
size += s.size();
// reserve that much space all at once (one allocation)
r.reserve(size);
// now do the concatenations
for(auto const& s: v)
r += s;
return r;
}
You can concatenate multiple strings - no problem:
std::string hello = "Hello";
std::string cruel = "cruel";
std::string world = "world";
std::string result = hello + " " + cruel + " " + world;
Results in result holding the string "Hello cruel world".
If you are using the c-style strings (ie char*), then you can allocate once and cat a number of times. The strcat function takes a pointer to a destination address as a first argument. Thus if you make your destination string large enough, there will only be one alloc. Thus
char* dest = new char[100];
dest[0] = 0; //Zero length string to start
strcat(dest, str1);
strcat(dest, str2);
strcat(dest, str3);
If on the other hand you use std::string then + can be chained string1 + string2 + string3.
I am trying to make a function that takes a constant reference of a string as input and returns the string after each character of the string is rotated 1 place to the right. Using references and pointers still confuses me and I am not sure how to obtain the string from the constant reference.
string rotate(const string &str){
string *uno = &str;
string dos = rotate(uno.rbegin(), uno.rbegin() + 1, uno.rend());
return dos;}
This is what I have got so far but it does not compile. Any tips on how to properly get the string from the constant reference will be appreciated.
You can't perform the rotation in-place without violating the const contract on the parameter, so you should copy the input and return a new string:
string rotate(const string &str){
string uno = str;
rotate(uno.rbegin(), uno.rbegin() + 1, uno.rend());
return uno;
}
Another reasonable option would be to use std::rotate_copy
The line
string* uno = string &str;
makes no sense. I think you mean
string* uno = const_cast<string*>(&str);
You might consider this rotate:
// rotate last char to front
std::string rotate(const std::string& str)
{
return(str[str.size()-1] +
str.substr(0,str.size()-1));
}
// 'abcdefghijklmnopqrstuvwxyz'
// 'zabcdefghijklmnopqrstuvwxy'
You could pass in a string to receive the rotated string, thus avoiding return by value copy.
I passed the string in by pointer, as its clearer at the call site that it's intended to be altered, but it could easily be passed by reference if preferred.
#include <string>
#include <iostream>
#include <algorithm>
void rotate(std::string const& str, std::string* out)
{
*out = str;
std::rotate(out->rbegin(), out->rbegin() + 1, out->rend());
}
int main(int, char**)
{
std::string out;
std::string x = "1234567";
std::cout << x << '\n';
::rotate(x, &out);
std::cout << out << '\n';
}
I would like to generate consecutive C++ strings like e.g. in cameras: IMG001, IMG002 etc. being able to indicate the prefix and the string length.
I have found a solution where I can generate random strings from concrete character set: link
But I cannot find the thing I want to achieve.
A possible solution:
#include <iostream>
#include <string>
#include <sstream>
#include <iomanip>
std::string make_string(const std::string& a_prefix,
size_t a_suffix,
size_t a_max_length)
{
std::ostringstream result;
result << a_prefix <<
std::setfill('0') <<
std::setw(a_max_length - a_prefix.length()) <<
a_suffix;
return result.str();
}
int main()
{
for (size_t i = 0; i < 100; i++)
{
std::cout << make_string("IMG", i, 6) << "\n";
}
return 0;
}
See online demo at http://ideone.com/HZWmtI.
Something like this would work
#include <string>
#include <iomanip>
#include <sstream>
std::string GetNextNumber( int &lastNum )
{
std::stringstream ss;
ss << "IMG";
ss << std::setfill('0') << std::setw(3) << lastNum++;
return ss.str();
}
int main()
{
int x = 1;
std::string s = GetNextNumber( x );
s = GetNextNumber( x );
return 0;
}
You can call GetNextNumber repeatedly with an int reference to generate new image numbers. You can always use sprintf but it won't be the c++ way :)
const int max_size = 7 + 1; // maximum size of the name plus one
char buf[max_size];
for (int i = 0 ; i < 1000; ++i) {
sprintf(buf, "IMG%.04d", i);
printf("The next name is %s\n", buf);
}
char * seq_gen(char * prefix) {
static int counter;
char * result;
sprintf(result, "%s%03d", prefix, counter++);
return result;
}
This would print your prefix with 3 digit padding string. If you want a lengthy string, all you have to do is provide the prefix as much as needed and change the %03d in the above code to whatever length of digit padding you want.
Well, the idea is rather simple. Just store the current number and increment it each time new string is generated. You can implement it to model an iterator to reduce the fluff in using it (you can then use standard algorithms with it). Using Boost.Iterator (it should work with any string type, too):
#include <boost/iterator/iterator_facade.hpp>
#include <sstream>
#include <iomanip>
// can't come up with a better name
template <typename StringT, typename OrdT>
struct ordinal_id_generator : boost::iterator_facade<
ordinal_id_generator<StringT, OrdT>, StringT,
boost::forward_traversal_tag, StringT
> {
ordinal_id_generator(
const StringT& prefix = StringT(),
typename StringT::size_type suffix_length = 5, OrdT initial = 0
) : prefix(prefix), suffix_length(suffix_length), ordinal(initial)
{}
private:
StringT prefix;
typename StringT::size_type suffix_length;
OrdT ordinal;
friend class boost::iterator_core_access;
void increment() {
++ordinal;
}
bool equal(const ordinal_id_generator& other) const {
return (
ordinal == other.ordinal
&& prefix == other.prefix
&& suffix_length == other.suffix_length
);
}
StringT dereference() const {
std::basic_ostringstream<typename StringT::value_type> ss;
ss << prefix << std::setfill('0')
<< std::setw(suffix_length) << ordinal;
return ss.str();
}
};
And example code:
#include <string>
#include <iostream>
#include <iterator>
#include <algorithm>
typedef ordinal_id_generator<std::string, unsigned> generator;
int main() {
std::ostream_iterator<std::string> out(std::cout, "\n");
std::copy_n(generator("IMG"), 5, out);
// can even behave as a range
std::copy(generator("foo", 1, 2), generator("foo", 1, 4), out);
return 0;
}
Take a look at the standard library's string streams. Have an integer that you increment, and insert into the string stream after every increment. To control the string length, there's the concept of fill characters, and the width() member function.
You have many ways of doing that.
The generic one would be to, like the link that you showed, have an array of possible characters. Then after each iteration, you start from right-most character, increment it (that is, change it to the next one in the possible characters list) and if it overflowed, set it to the first one (index 0) and go the one on the left. This is exactly like incrementing a number in base, say 62.
In your specific example, you are better off with creating the string from another string and a number.
If you like *printf, you can write a string with "IMG%04d" and have the parameter go from 0 to whatever.
If you like stringstream, you can similarly do so.
What exactly do you mean by consecutive strings ?
Since you've mentioned that you're using C++ strings, try using the .string::append method.
string str, str2;
str.append("A");
str.append(str2);
Lookup http://www.cplusplus.com/reference/string/string/append/ for more overloaded calls of the append function.
it's pseudo code. you'll understand what i mean :D
int counter = 0, retval;
do
{
char filename[MAX_PATH];
sprintf(filename, "IMG00%d", counter++);
if(retval = CreateFile(...))
//ok, return
}while(!retval);
You have to keep a counter that is increased everytime you get a new name. This counter has to be saved when your application is ends, and loaded when you application starts.
Could be something like this:
class NameGenerator
{
public:
NameGenerator()
: m_counter(0)
{
// Code to load the counter from a file
}
~NameGenerator()
{
// Code to save the counter to a file
}
std::string get_next_name()
{
// Combine your preferred prefix with your counter
// Increase the counter
// Return the string
}
private:
int m_counter;
}
NameGenerator my_name_generator;
Then use it like this:
std::string my_name = my_name_generator.get_next_name();