This would be great to avoid copying. Is there anything in std or boost to do this?
std::string mystring = "HELLO "; //initial string
int len = mystring.size(); //get initial length
mystring.resize(100); //resize so it's big enough
char* buffer = &mystring[len-1]+1; //get pointer to "spare" space
std::cin.get(buffer , 100-len, '\n'); //read into buffer, until newline
mystring.resize(len + std::cin.gcount()); //shrink to correct size
Since there were no existing solutions, this is what I came up with:
istream& appendline(istream& is, string& str, char delim = '\n')
{
size_t size = str.size();
size_t capacity = str.capacity();
streamsize spaceRemaining = capacity - size;
if (spaceRemaining == 0)
{
capacity = max(static_cast<size_t>(8), capacity * 2);
spaceRemaining = capacity - size;
}
// give getline access to all of capacity
str.resize(capacity);
// get until delim or spaceRemaining is filled
is.getline(&str[size], spaceRemaining, delim);
// gcount includes the delimiter but not the null terminator
size_t newSize = size + is.gcount();
// is failbit set?
if (!is)
{
// if string ran out of space, expand and retry
if (is.gcount()+1 == spaceRemaining)
{
is.clear();
str.resize(newSize);
str.reserve(capacity * 2);
return appendline(is, str, delim);
}
}
else if (!is.eof())
--newSize;
// resize string to fit its contents
str.resize(newSize);
return is;
}
You can use this member function instead (doc):
istream& getline (char* s, streamsize n, char delim );
to read data, and then append that data to a string.
For example, you can wrap this functionality in your own defined function as:
std::istream & getline(std::istream &in, std::string & str, char delim)
{
char buf[1024];
in.getline(buf, 1024, delim);
str.append(buf, in.gcount());
return in;
}
std::string s = "initial string";
getline(std::cin, s, '\n'); //should append to s
Just use the global std::getline instead of the member method
stringstream s;
s << "line1\nline2";
string str;
while(std::getline(s, str)) cout << str;
output: line1line2
Related
well I would like to know how to change number by letter, I would like to replace the number 1 with :x:
Here's my code:
string stng;
printf("Enter with number:");
cin >> stng;
replace(stng.begin(), stng.end(), '1', 'x');
cout << stng << endl;
as you can see I'm using this to replace: replace(stng.begin(), stng.end(), '1', 'x');
but as soon as I can only change 1 for x, I want to replace for :x:
Maybe you can try something like this
string stng;
printf("Enter with number:");
cin >> stng;
replace(stng.begin(), stng.end(), '1', ":x:");
cout << stng << endl;
Here's what I use. It will take a std::string and replace all occurrences of the from input string to the to input string.
std::string replaceAll(const std::string & s, const std::string & from, const std::string & to)
{
string res(s);
string::size_type n1 = from.size();
string::size_type n2 = to.size();
string::size_type i = 0;
string::size_type j = 0;
while ((i = res.find(from, j)) != string::npos)
{
res.replace(i, n1, to);
j = i + n2;
}
return res;
}
You can split the string by delim '1' into tokens using this split function.
Then merge the string by ":x:" using the following function
std::string merge(const std::vector<std::string>& v, const std::string& glue)
{
std::string result;
if(v.empty()) { return result; }
result += v[0];
for(size_t i = 1; i != v.size() ; i++)
{
result += glue;
result += v[i];
}
return result;
}
std::string replace(const std::string& src, char delim, const std::string& glue)
{
return merge(split(src, delim), glue);
}
Live is here
You can do that better with the replace member function of std::string.
auto pos = stng.find("1"); // search for 1 in the string
if (pos!=stng.npos) // check if 1 is found
{
stng.replace(pos, 1, ":x:"); // replace ":x:" starting from 'pos' to 'pos+1'
}
And your job is done !!!
I basically have a txt file that looks like this...
High Score: 50
Player Name: Sam
Number Of Kills: 5
Map
Time
I want to store everything before the : or whitespace after Map and Time into one array and everything after in another. For both Map and Time, there is nothing after and so I want to store the whitespace as null.
So far, I have managed to read and store all this information into a temp array. However, it is separating that I am having trouble with. This is my code:
istream operator >> (istream &is, Player &player)
{
char **temp;
char **tempNew;
char lineInfo[200]
temp = new char*[5];
tempNew = new char*[5];
for (int i=0; i<5; i++)
{
temp[i] = new char[200];
is.getline(lineInfo, sizeof(lineInfo));
int length = strlen(lineInfo);
for (int z=0; z < length; z++)
{
if(lineInfo[z] == '= ' ){ //HOW DO I CHECK IF THERE IS NOTHING AFTER THE LAST CHAR
lineInfo [length - (z+1)] = lineInfo [length];
cout << lineInfo << endl;
strncpy(temp[i], lineInfo, sizeof(lineInfo));
}
else{
tempNew[i] = new char[200];
strncpy(tempNew[i], lineInfo, sizeof(lineInfo));
}
}
}
If what you need is to find ':'
#include <cstring>
and just
auto occurance = strstr(string, substring);
Documentation here.
if occurance is not a null ptr, then see if occurance is at the end of the line from get line. If not, your value is everything after that :
Much easier with std::string.
// Read high score
int high_score;
my_text_file.ignore(10000, ':');
cin >> high_score;
// Read player name
std::string player_name;
my_text_file.ignore(10000, ':');
std::getline(my_text_file, player_name);
// Remove spaces at beginning of string
std::string::size_type end_position;
end_position = player_name.find_first_not_of(" \t");
if (end_position != std::string::npos)
{
player_name.erase(0, end_position - 1);
}
// Read kills
unsigned int number_of_kills = 0;
my_text_file.ignore(':');
cin >> number_of_kills;
// Read "Map" line
my_text_file.ignore(10000, '\n');
std::string map_line_text;
std::getline(my_text_file, map_line_text);
// Read "Text" line
std::string text_line;
std::getline(my_text_file, text_line);
If you insist on using C-style strings (arrays of char), you will have to use more complex and less safe functionality. Look up the following functions:
fscanf, strchr, strcpy, sscanf
I am doing this
char *draw_line(int n, char ch)
{
char *line = new char[50];
for(int i = 0; i <= n; i++)
line[i] = ch;
return line;
}
and while calling the function I am writing this:
char *res_line = draw_line(50, '=');
cout<<*res_line;
but instead of getting = printed 50 times in the console window, it just show = sign one time. My main aim is to return the = or any character as many times I want and output
the same to a text file. That's it.
cout<<*res_line;
is printing one char because *res_line is char, not char*.
Write:
cout<<res_line;
But wait — that is not going to work either because res_line is NOT null-terminated.
Use std::string or std::vector<char> — avoid explicit memory allocation, use RAII idiom instead:
std::string draw_line(int n, char ch)
{
return {n, ch}; //C++11
}
So simple!
Or if you use std::vector:
std::vector<char> draw_line(int n, char ch)
{
return {n, ch}; //C++11
}
which is almost same.
In C++03, however, you've to write:
return std::string(n, ch); //in the first case
return std::vector<char>(n, ch); //in the second case
That is, invoke the constructor explicitly.
The valid code will look as
char* draw_line( int n, char ch )
{
char *ch2= new char[n + 1]();
std::memset( ch2, ch, n );
return ch2;
}
//...
char *ch50 = draw_line(50,'=');
cout << ch50;
//...
delete []ch50;
Take into account this statement
char *ch2= new char[n + 1]();
But it would be much better to write simply
std::cout << std::string( 50, '=' );
char* draw_line(int n, char ch)
{
char *ch2= (char *) malloc(sizeof(char)*(n+1)); // (n+1) here
for(int i=0;i<n;i++) // < instead of <=
ch2[i]=ch;
ch2[n] = 0; // terminator
return ch2;
}
char *ch50 = draw_line(50,'=');
cout<< ch50; // changed from *ch50 to ch50
ADDON: look at string-fill constructor
cout << string(50, '=');
Question
The problem is that i am trying to get user input using insertion operator and initialising the value thechars, to allocate the size to thechars i need the length of input, how do i get it?? And initialise in insertion operator.
Main problem is with insertion operator.
When i run the program it shows the segmentation fault,
plz help
class string1
{
private:
int len;
char *thechars;
//friend ostream& operator<<(ostream&,string1&);##
//friend istream& operator>>(istream&,string1&);##
public:
//string1() :len(0),thechars(NULL){}
string1()
{
thechars = new char[1];
thechars[0] = '\0';
len=0;
// cout << "\tDefault string constructor\n";
// ConstructorCount++;
}
};
// this is the insertion operator i use
istream& operator>>(istream& in, string1& tpr)
{
in >> tpr.thechars;
//tpr.thechars[i+1]='\0';
return in;
}
//this one is the extraction operator
ostream& operator<<(ostream& out,string1& prt)
{
for(int i=0;i<prt.len;i++)
out<<prt.thechars[i];
return out;
}
// main function##
string1 str;
cout << "enter first string" << endl;
cin >> str;
cout << str << endl;
If in is a file input stream, you can do the following:
in.seekg(0, ios::end);
length = in.tellg();
in.seekg(0, ios::beg);
The other option is reading the input stream char by char and double the size of thechars each time it's exhausted. First, introduce one more variable to store the currently allocated size of the buffer --- allocSize. After that update the constructor and operator<< as follows.
Constructor:
string1()
{
allocSize = 1; // initially allocated size
thechars = new char[allocSize];
thechars[0] = '\0';
len=0;
}
Input operator:
istream& operator>>(istream& in, string1& tpr)
{
char inp;
while (in.get(inp)) {
// end-of-input delimiter (change according to your needs)
if (inp == ' ')
break;
// if buffer is exhausted, reallocate it twice as large
if (tpr.len == tpr.allocSize - 1) {
tpr.allocSize *= 2;
char *newchars = new char[tpr.allocSize];
strcpy(newchars, tpr.thechars);
delete[] tpr.thechars;
tpr.thechars = newchars;
}
// store input char
tpr.thechars[tpr.len++] = inp;
tpr.thechars[tpr.len] = '\0';
}
}
But the best option is to use std::string as a type for thechars. Do you really need all this manual memory handling?
Instead of giving the in a char* give it a regular string. Then you can extract the data yourself.
istream& operator>>(istream& in, string1& tpr)
{
string temp;
in >> temp;
tpr.len = temp.length + 1;
tpr.thechars = new char[tpr.len];
tpr.thechars[temp.length] = '\0';
strcpy(tpr.thechars, &temp[0], tpr.len);
return in;
}
you wrote
in>> tpr.thechars; // where thechars="\0";
You allocated only one byte, but i guess you are input string with more bytes.
I think error here.
I have an array of chars and I need to extract subsets of this array and store them in std::strings. I am trying to split the array into lines, based on finding the \n character. What is the best way to approach this?
int size = 4096;
char* buffer = new char[size];
// ...Array gets filled
std::string line;
// Find the chars up to the next newline, and store them in "line"
ProcessLine(line);
Probably need some kind of interface like this:
std::string line = GetSubstring(char* src, int begin, int end);
I'd create the std::string as the first step, as splitting the result will be far easier.
int size = 4096;
char* buffer = new char[size];
// ... Array gets filled
// make sure it's null-terminated
std::string lines(buffer);
// Tokenize on '\n' and process individually
std::istringstream split(lines);
for (std::string line; std::getline(split, line, '\n'); ) {
ProcessLine(line);
}
You can use the std::string(const char *s, size_t n) constructor to build a std::string from the substring of a C string. The pointer you pass in can be to the middle of the C string; it doesn't need to be to the very first character.
If you need more than that, please update your question to detail exactly where your stumbling block is.
I didn't realize you only wanted to process each line one at a time, but just in case you need all the lines at once, you can also do this:
std::vector<std::string> lines;
char *s = buffer;
char *head = s;
while (*s) {
if (*s == '\n') { // Line break found
*s = '\0'; // Change it to a null character
lines.push_back(head); // Add this line to our vector
head = ++s;
} else s++; //
}
lines.push_back(head); // Add the last line
std::vector<std::string>::iterator it;
for (it = lines.begin(); it != lines.end(); it++) {
// You can process each line here if you want
ProcessLine(*it);
}
// Or you can process all the lines in a separate function:
ProcessLines(lines);
// Cleanup
lines.erase(lines.begin(), lines.end());
I've modified the buffer in place, and the vector.push_back() method generates std::string objects from each of the resulting C substrings automatically.
your best bet (best meaning easiest) is using strtok and convert the tokens to std::string via the constructor. (just note that pure strtok is not reentrant, for that you need to use the non standard strtok_r).
void ProcessTextBlock(char* str)
{
std::vector<std::string> v;
char* tok = strtok(str,"\n");
while(tok != NULL)
{
ProcessLine(std::string(tok));
tok = strtok(tok,"\n");
}
}
You can turn a substring of char* to std::string with a std::string's constructor:
template< class InputIterator >
basic_string( InputIterator first, InputIterator last, const Allocator& alloc = Allocator() );
Just do something like:
char *cstr = "abcd";
std::string str(cstr + 1, cstr + 3);
In that case str would be "bc".