I have been implementing a factory for a component based game engine recently. I am deserializing objects by reading in from a file what component they need and what to initialize them with. It works except for when I try to read in a property longer than 15 characters. At 15 characters, it reads it in perfectly, anything longer and I get "ε■ε■ε■ε■ε■ε■ε■ε■ε" as output.
I am using std::string to store these lines of text.
Example:
JunkComponent2 test "1234567890123456" test2 "123456789012345"
With this the value of test becomes garbage, while test2 stays perfectly intact.
Any idea's what might be going on?
char line[1024];
while (file.getline(line, 1024))
{
std::vector<std::string> words;
std::string word;
int j = 0;
for (unsigned i = 0; line[i] != '\0' && i < 1024; ++i)
{
if (line[i] == ' ' && j > 0 && line[i - 1] != '\\')
{
words.push_back(word);
j = 0;
word = "";
}
else
{
++j;
word += line[i];
}
}
words.push_back(word);
// std::cout << (*Parts)["JunkComponent"]->GetName() << std::endl;
Component* c = (*Parts)[words[0]]->clone(words);
object->AddComponent(words[0], c);
for (std::list<Member*>::iterator it = members.begin(); it != members.end(); ++it)
{
for (unsigned i = 0; i < words.size(); ++i)
{
if ((*it)->GetName() == words[i])
{
if (words[i + 1][0] == '\"')
{
std::vector<char> chars;
chars.push_back('\"');
chars.push_back('\\');
for (unsigned int n = 0; n < chars.size(); ++n)\
{
words[i + 1].erase(std::remove(words[i + 1].begin(), words[i + 1].end(), chars[n]), words[i + 1].end());
}
Container((*it)->GetMeta(), GET_MEMBER(data.GetData(), (*it)->GetOffset()), (*it)->GetName()).SetValue<std::string>(words[i + 1]);
}
else
{
Container((*it)->GetMeta(), GET_MEMBER(data.GetData(), (*it)->GetOffset()), (*it)->GetName()).SetValue<int>(std::stoi(words[i + i]));
}
++i;
break;
}
}
}
}
GET_MEMBER Macro expands to:
#define GET_MEMBER(P, OFFSET) ((void *)(((char *)(P)) + (OFFSET)))
SetValue Function: (data is a void*)
template <typename T>
void SetValue(T data_)
{
memcpy(data, &data_, sizeof(T));
}
I'll take a stab having just eyed your code. GET_MEMBER is really nasty and I think that's where your problem is. It seems to rely on std::string being convertible to char*, which it is not. Why does your code work with strings shorter than 15? Well that's more than likely because std::string on most popular implementations actually contains a special case for strings where it keeps an internal buffer of length 16 ( last element \0 ) to avoid dynamic memory allocation. When the string is larger than 15 this buffer is uninitialized because it isn't used. The correct way to access the string is by using operator[].
Related
The goal of the program is to take a string like "kcck" and delete the consecutive duplicates. It should first iterate through the string and delete cc leaving kk; then go through again and delete kk; then return "empty" since there are no characters left in the string.
Another example, "aabggtcc" should return "bt".
int i;
int j = i+1;
string deduplicate(string input) {
for(i=0; i<input.length(); ++i) {
while(j <input.length()) {
if(input[i] == input[j]) {
input.erase(i);
input.erase(j);
}
else if (input[i] != input[j]) {
++i; ++j;
}
if(input[i] == '\0') {
cout<<"empty";
}
}
}
return 0;
}
int main () {
cout<<deduplicate("aabg")<<endl;
cout<<deduplicate("ag")<<endl;
cout<<deduplicate("btaabb")<<endl;
return 0;
}
When I run the code it gives me:
libc++abi.dylib: terminating with uncaught exception of type std::out_of_range: basic_string
There are couple of issues with your snippet,
deduplicate function is returning zero(0) all the time
j is initialized in global scope and is never reset for new string
As you erase std::string::length() is calculated on new string hence your index i and j are won't point to same laocation.
Here is the snippet with rectified error,
string deduplicate(string input) {
int i = 0;
int j = 0;
while (i < input.length()) {
j = i + 1;
bool isRepeated = false;
while (j < input.length()) {
if (input[i] == input[j]) {
input.erase(j,1);
--j; //as string length is reduced by 1
isRepeated = true;
}
++j;
}
if (isRepeated) {
input.erase(i,1); //remove first letter as well
--i;//sting length is reduced by one
}
++i;
}
return input;
}
int main() {
std::cout << deduplicate("aabg") << endl;
std::cout << deduplicate("ag") << endl;
std::cout << deduplicate("btaabb") << endl;
return 0;
}
output:
bg
ag
t
which can be even simplified as,
std::string deduplicate(std::string input) {
std::string s ="";
for (auto c : input) //loop through all char
{
int f = 0;
for (auto c1 : input)
{
if (c1 == c)
{
f++; //increment if char is found
}
}
if (f == 1)//append char only if it present ones
s += c;
}
return s;
}
You are decreasing the size of the string whenever you call string.erase() that's why the variable i eventually exceeds the "current" string size input.length() in the while loop, and you get an error std::out_of_range: basic_string when you try to access input[i] in the if and else if conditions of the loop.
Try to manually go through the loop on the string on which you got the error and you will see that i has gone out of bound (i.e. i >= input.length()) in the while loop
With C++11 and on, instead of iterating over each character and making the comparison manually, you can use std::basic_string::find_first_not_of to look forward from a position in the string and find the first character not of the current character. If the position returned by .find_first_not_of is more than 1 from the current position, you can use .erase to erase that number characters. If the return is 1, then just increment your current position and repeat.
To operate on all duplicates characters in the modified string, you simply wrap it all in an outer-loop, keep a copy of the string before before entering the inner-loop to remove duplicate characters, and compare if the modified string is equal to your copy or the .length() is zero for your exit condition.
You can do something similar to the following:
#include <iostream>
#include <string>
int main (void) {
std::string s;
while (getline (std::cin, s)) {
std::string current;
do {
size_t pos = 0;
current = s;
while (pos < s.length()) {
size_t duplicates = s.find_first_not_of (s.at(pos), pos);
if (duplicates != std::string::npos && duplicates > pos + 1)
s.erase(s.begin() + pos, s.begin() + duplicates);
else if (duplicates == std::string::npos &&
(s.end() - s.begin() - pos) > 1)
s.erase(s.begin() + pos, s.end());
else
pos++;
}
} while (current != s && s.length());
std::cout << "'" << s << "'\n";
}
}
Example Use/Output
$ echo "kcck" | ./bin/ddcpp
''
$ echo "aabggtcc" | ./bin/ddcpp
'bt'
$ echo "aabg" | ./bin/ddcpp
'bg'
$ echo "ag" | ./bin/ddcpp
'ag'
$ echo "btaabb" | ./bin/ddcpp
'bt'
There are a number of ways to approach the problem and as long as they are reasonably efficient there isn't any one right/wrong way. If you have a modern C++ compiler, letting some of the built-in container functions handle the work is generally a bit more robust than reinventing it on your own. Look things over and let me know if you have questions.
I need to insert symbol '+' into string after its each five symbol.
st - the member of class String of type string
int i = 1;
int original_size = st.size;
int count = 0;
int j;
for (j = 0; j < st.size; j++)
{
if (i % 5)
count++;
}
while (st.size < original_size + count)
{
if (i % 5)
{
st.insert(i + 1, 1, '+');
st.size++;
}
i++;
}
return st;
I got an error in this part of code. I think it is connected with conditions of of the while-cycle. Can you help me please how to do this right?
If I've understood you correctly then you want to insert a '+' character every 5 chars in the original string. One way to do this would be to create a temporary string and then reassign the original string:
std::string st("A test string with some chars");
std::string temp;
for (int i = 1; i <= st.size(); ++i)
{
temp += st[i - 1];
if (i % 5 == 0)
{
temp += '+';
}
}
st = temp;
You'll notice I've started the loop at 1, this is to avoid the '+' being inserted on the first iteration (0%5==0).
#AlexB's answer shows how to generate a new string with the resulting text.
That said, if your problem is to perform in-place insertions your code should look similar to this:
std::string st{ "abcdefghijk" };
for(auto i = 4; i != st.size(); i += 5)
st.insert(i+1, 1, '+'); // insert 1 character = '+' at position i
assert(st == "abcde+fghij+k");
std::string InsertEveryNSymbols(const std::string & st, size_t n, char c)
{
const size_t size(st.size());
std::string result;
result.reserve(size + size / n);
for (size_t i(0); i != size; ++i)
{
result.push_back(st[i]);
if (i % n == n - 1)
result.push_back(c);
}
return result;
}
You don't need a loop to calculate the length of the resulting string. It's going to be simply size + size / 5. And doing multiple inserts makes it a quadratic-complexity algorithm when you can just as easily keep it linear.
Nothing no one else has done, but eliminates the string resizing and the modulus and takes advantage of a few new and fun language features.
std::string temp(st.length() + st.length()/5, '\0');
// preallocate string to eliminate need for resizing.
auto loc = temp.begin(); // iterator for temp string
size_t count = 0;
for (char ch: st) // iterate through source string
{
*loc++ = ch;
if (--count == 0) // decrement and test for zero much faster than
// modulus and test for zero
{
*loc++ = '+';
count = 5; // even with this assignment
}
}
st = temp;
I have a C++ function that splits a char array into multiple char arrays when it encounters a delimiter. For some reason, when saving the third split array the program just crashes and sometimes returns an std::bad_alloc exception.
char ** explode(const char * arr, const char delim) {
int start, end, curr=0, count=1;
char ** strings;
//Iegūst explodēto stringu skaitu
for (int i = 0; arr[i] != 0; i++) {
if (arr[i] == delim && i != 0 && arr[i+1] != 0 && arr[i+1] != delim ) { //Nav pirmais, nav pēdējais, nav pa labi vēlviens delimiters
count++;
}
}
strings = new char*[count];
start = 0;
for (int i = 0; arr[i] != 0; i++) {
if (arr[i] == delim || arr[i+1] == 0) {
if (arr[i] == delim) {
end = i;
} else {
end = i+1;
}
if (end-start < 1) {
start++;
} else {
copystring(arr,strings[curr++],start,end-start);
start = i+1;
}
}
}
for (int i = 0; i < count; i++) {
cout << strings[i] << endl;
}
return strings;
}
//Pārkopē daļu no pirmā char masīva uz otru, no START pozīcijas, līdz GARUMS garumā
void copystring(const char * from, char *& to, const int start, const int garums) {
int curr=0;
if (garums < 1 || start > charlen(from)) {
return;
}
to = new char[garums];
for (int i = start; i < start+garums && from[i] != 0; i++) {
to[curr++] = from[i];
}
to[curr] = 0;
}
It's hard to tell because it doesn't really tell me at which line everything goes wrong, but I think it happens at
to = new char[garums];
I've tried debugging this line within CodeBlocks, but for some reason when using breakpoints and tracking the variables the applications works fine and executes correctly. It only crashes when running it normally, without debugging...
Also note, that I can't use strings or pretty much any library except fstream and iostream.
EDIT: I tried changing the new char[garums] part to new char[100] and it magically started working. The problem is that I then changed it to new char[10] in which case everything still worked. I even outputted the saved text to the console and it saved everything properly. How could it have saved big words in a char array that is 10 character long (the words I'm testing are longer than 10 characters)? When I changed it to new char[1] however it started crashing again, but again only after the 3rd loop iteration. So it somehow saved the first 2 words in a 1 character long array?
EDIT2: And now it magically started working even with new char[garums]. Something is really wrong here, anyone have any ideas?
The bug you refer to in your question likely crops up when trying to use the pointer
to pointer being returned from the explode function.
Some pointers ; If you have to write C code, don't use a mishmash of C/C++,
Use the library functions rather than re-inventing the wheel (strncpy in copystring)
Your word count was off because you didn't take into account the word between
the last delimiter and EOL
Below are some minor changes to your code as a complete example :
#include <stdio.h>
#include <strings.h>
void copystring(const char *from, char **to, const int numchars)
{
if (numchars > 0) {
*to = new char[numchars];
strncpy(*to, from, numchars) ;
(*to)[numchars] = '\0' ;
}
}
char **explode(const char * buffer, const char delim)
{
int count = 0 ;
if (strlen(buffer) > 0) {
int inword = 0 ;
int idx = 0 ;
do {
if (buffer[idx] == delim || buffer[idx] == '\0') {
if (inword == 1) {
count++ ;
inword = 0 ;
}
} else {
inword = 1 ;
}
} while (buffer[idx++] != 0) ;
}
int start = 0;
int end = 0 ;
int curr = 0 ;
int idx = 0 ;
char **values = new char*[count+1];
do {
if (buffer[idx] == delim || buffer[idx] == '\0') {
end = idx;
if (end-start > 0) {
copystring(&buffer[start], &values[curr++], end - start) ;
}
start = ++end ;
}
} while (buffer[idx++] != 0) ;
values[curr] = NULL ;
for (int idx = 0; idx < count; idx++) {
fprintf(stdout, "'%s'\n", values[idx]) ;
}
return values;
}
int main(int argc, char *argv[])
{
char inputstr[] = "The, quick, brown, fox, jumped, over,,, ,,, ,,,,, ,,,, the, lazy, dog's, back" ;
char **values = explode(inputstr, ',') ;
while (*values != NULL) {
fprintf(stdout, "%s\n" , *values) ;
*values++ ;
}
return (0) ;
}
Since I don't know what input data you have I will have to guess:
Here you allocate your pointer array but please note that all the pointers are uninitialized.
strings = new char*[count]
then when you parse the code you use a variable curr which you let run freely so it is not certain that all strings[] have been set to some value or whether curr lands on a number larger than count.
If I were you I would put in a check to make sure that:
a) curr does not exceed count
b) that if curr lands on a value less than count, set the rest of the pointers to nullptr
This has probably to do with to being of type char*& instead of type char*.
On the other hand, I never programmed C++ like this (are you sure that this is not C?). Using explicit memory management (like ´new´) is as good as playing playing russian roulette.
Here is a more standard C++ way of doing this:
#include <vector>
#include <string>
#include <iostream>
std::vector<std::string> splitString(std::string& str, char c) {
std::vector<std::string> substrings;
while(true) {
unsigned pos = str.find(c);
substrings.push_back(str.substr(0,pos));
if(pos == std::string::npos) break;
str = str.substr(pos+1);
}
return substrings;
}
int main()
{
char c = '*';
std::string str = "Some*string*that we need to split*properly*";
std::vector<std::string> result = splitString(str,c);
for(unsigned i = 0; i < result.size(); ++i) {
std::cout << i << ": " << result[i] << "\n";
}
}
Output:
0: Some
1: string
2: that we need to split
3: properly
4:
Hi everyone I'm working on a function to manipulating any string in this following manner.
"abc" -> "cab"
"abcd" -> "dacb"
"abcdef" -> "faebdc"
"divergenta" -> "adtinveerg"
... and so on.
This is the code I've come up with so far. I think it does the job but I think the code and solution is kind of ugly and I'm not sure if it's fail proof and if it is working properly for every given case. I would highly appreciate any input on this code or any examples on how you would write this function. I beg you to bear in mind that I'm very much a n00b so don't go too hard on me.
string transformer(string input) {
string temp;
int n = 0;
int m = (input.length() -1);
for( int i = 0; i < input.length(); i++) {
temp += input[m];
if (input[m] == input[n]) {
break;
}
else {
temp += input[n];
}
n += 1;
m -= 1;
if ( temp.length() == input.length() ) {
break;
}
}
return temp; }
You have three problems.
Try it with "abbba". If the result isn't what you want, then this conditional:
if (input[m] == input[n]) {
break;
}
is just plain wrong.
Look at the other conditional:
if ( temp.length() == input.length() ) {
break;
}
You're adding two characters at a time to temp. What if input has odd length?
Suppose that works correctly. Consider the loop:
for( int i = 0; i < input.length(); i++) {
...
if ( temp.length() == input.length() ) {
break;
}
}
That loop will never terminate in the for statement. You might as well do it this way:
while( temp.length() < input.length() ) {
...
}
Once that's all working correctly, you should look into iterators.
This function just walks two indices toward the center until they meet or pass each other. The last if block handles the case of an odd length input string. It works for all your test cases on ideone.com
std::string transformer(const std::string& input)
{
std::string temp;
int i = 0;
int j = input.length() - 1;
while (i < j) {
temp += input[j--];
temp += input[i++];
}
if (i == j) {
temp += input[i];
}
return temp;
}
std::string transformer(const std::string& input) {
std::string res(input.length(), '0');
for (int i = 0; i < input.length(); ++i) {
res[i] = input[ i % 2 == 0 ? input.length() - (i/2) - 1 : (i/2) ];
}
return res;
}
Unfortunately if (input[m] == input[n]) will make sure that if the first and last characters are the same, it immediately quits after the first character is processed.
I'd do this with std::string::iterator and std::string::reverse_iterator:
auto it = input.begin();
auto rit = input.rbegin();
std::string temp;
for (size_t i = 0; i < input.length()/2; ++i) {
temp += *rit++;
temp += *it++;
}
The logic for handling empty and odd-length input is left for you to do, shouldn't be too hard. (Input of length 1 is also a special case)
I would use pointers instead of indexes to do this.
So you have a pointer the reading the edges and you keep swapping them with each iteration.
It will also make it faster.
I think this should work, but I can't remember how to make an array of const char pointers. Can anyone help me with that step?
string transformer(string input) {
std::string temp;
const char *front, *back;
for (*front = input.c_str(), *back = front + input.length() - 1; front < back ; front++, back--) {
temp += *back;
temp += *front;
}
if (front == back)
temp += *front;
return temp;
}
(using the same method as #Blastfurnace, but skipping unnecessary indexes.)
I created a program in C++ that remove commas (,) from a given integer. i.e. 2,00,00 would return 20000. I am not using any new space. Here is the program I created:
void removeCommas(string& str1, int len)
{
int j = 0;
for (int i = 0; i < len; i++)
{
if (str1[i] == ',')
{
continue;
}
else
{
str1[j] = str1[i];
j++;
}
}
str1[j] = '\0';
}
void main()
{
string str1;
getline(cin, str1);
int i = str1.length();
removeCommas(str1, i);
cout << "the new string " << str1 << endl;
}
Here is the result I get:
Input : 2,000,00
String length =8
Output = 200000 0
Length = 8
My question is that why does it show the length has 8 in output and shows the rest of string when I did put a null character. It should show output as 200000 and length has 6.
Let the standard library do the work for you:
#include <algorithm>
str1.erase(std::remove(str1.begin(), str1.end(), ','), str1.end());
If you don't want to modify the original string, that's easy too:
std::string str2(str1.size(), '0');
str2.erase(std::remove_copy(str1.begin(), str1.end(), str2.begin(), ','), str2.end());
You need to do a resize instead at the end.
Contrary to popular belief an std::string CAN contain binary data including 0s. An std::string 's .size() is not related to the string containing a NULL termination.
std::string s("\0\0", 2);
assert(s.size() == 2);
The answer is probably that std::strings aren't NUL-terminated. Instead of setting the end+1'th character to '\0', you should use str.resize(new_length);.
Edit: Also consider that, if your source string has no commas in it, then your '\0' will be written one past the end of the string (which will probably just happen to work, but is incorrect).
The std::srting does not terminate with \0, you are mixing this with char* in C. So you should use resize.
The solution has already been posted by Fred L.
In a "procedural fashion" (without "algorithm")
your program would look like:
void removeStuff(string& str, char character)
{
size_t pos;
while( (pos=str.find(character)) != string::npos )
str.erase(pos, 1);
}
void main()
{
string str1;
getline(cin, str1);
removeStuff(str1, ',');
cout<<"the new string "<<str1<<endl;
}
then.
Regards
rbo
EDIT / Addendum:
In order to adress some efficiency concerns of readers,
I tried to come up with the fastest solution possible.
Of course, this should kick in on string sizes over
about 10^5 characters with some characters to-be-removed
included:
void fastRemoveStuff(string& str, char character)
{
size_t len = str.length();
char *t, *buffer = new char[len];
const char *p, *q;
t = buffer, p = q = str.data();
while( p=(const char*)memchr(q, character, len-(p-q)) ) {
memcpy(t, q, p-q);
t += p-q, q = p+1;
}
if( q-str.data() != len ) {
size_t tail = len - (q-str.data());
memcpy(t, q, tail);
t += tail;
}
str.assign(buffer, t-buffer);
delete [] buffer;
}
void main()
{
string str1 = "56,4,44,55,5,55"; // should be large, 10^6 is good
// getline(cin, str1);
cout<<"the old string " << str1 << endl;
fastRemoveStuff(str1, ',');
cout<<"the new string " << str1 << endl;
}
My own procedural version:
#include <string>
#include <cassert>
using namespace std;
string Remove( const string & s, char c ) {
string r;
r.reserve( s.size() );
for ( unsigned int i = 0; i < s.size(); i++ ) {
if ( s[i] != c ) {
r += s[i];
}
}
return r;
}
int main() {
assert( Remove( "Foo,Bar,Zod", ',' ) == "FooBarZod" );
}
Here is the program:
void main()
{
int i ;
char n[20] ;
clrscr() ;
printf("Enter a number. ") ;
gets(n) ;
printf("Number without comma is:") ;
for(i=0 ; n[i]!='\0' ; i++)
if(n[i] != ',')
putchar(n[i]) ;
getch();
}
For detailed description you can refer this blog: http://tutorialsschool.com/c-programming/c-programs/remove-comma-from-string.php
The same has been discussed in this post: How to remove commas from a string in C
Well, if youre planing to read from a file using c++. I found a method, while I dont think thats the best method though, but after I came to these forums to search for help before, I think its time to contribute with my effort aswell.
Look, here is the catch, what I'm going to present you is part of the source code of the map editor Im building on right now, that map editor obviously has the purpose to create maps for a 2D RPG game, the same style as the classic Pokemon games for example. But this code was more towards the development of the world map editor.
`int strStartPos = 0;
int strSize = 0;
int arrayPointInfoDepth = 0;
for (int x = 0; x < (m_wMapWidth / (TileSize / 2)); x++) {
for (int y = 0; y < (m_wMapHeight / (TileSize / 2)); y++) {
if (ss >> str) {
for (int strIterator = 0; strIterator < str.length(); strIterator++) {
if (str[strIterator] == ',') {`
Here we need to define the size of the string we want to extract after the previous comma and before the next comma
`strSize = strIterator - strStartPos;`
And here, we do the actual transformation, we give to the vector that is a 3D vector btw the string we want to extract at that moment
`m_wMapPointInfo[x][y][arrayPointInfoDepth] = str.substr(strStartPos, strSize);`
And here, we just define that starting position for the next small piece of the string we want to extract, so the +1 means that after the comma we just passed
strStartPos = strIterator + 1;
Here, well since my vector has only 6 postions that is defined by WorldMapPointInfos we need to increment the third dimension of the array and finally do a check point where if the info has arrived the number 6 then break the loop
arrayPointInfoDepth++;
if (arrayPointInfoDepth == WorldMapPointInfos) {
strStartPos = 0;
arrayPointInfoDepth = 0;
break;
}
}
}
}
}
}
Either way on my code, think abt that the vector is just a string, thats all you need to know, hope this helps though :/
Full view:
int strStartPos = 0;
int strSize = 0;
int arrayPointInfoDepth = 0;
for (int x = 0; x < (m_wMapWidth / (TileSize / 2)); x++) {
for (int y = 0; y < (m_wMapHeight / (TileSize / 2)); y++) {
if (ss >> str) {
for (int strIterator = 0; strIterator < str.length(); strIterator++) {
if (str[strIterator] == ',') {
strSize = strIterator - strStartPos;
m_wMapPointInfo[x][y][arrayPointInfoDepth] = str.substr(strStartPos, strSize);
strStartPos = strIterator + 1;
arrayPointInfoDepth++;
if (arrayPointInfoDepth == WorldMapPointInfos) {
strStartPos = 0;
arrayPointInfoDepth = 0;
break;
}
}
}
}
}
}