The following Objective C++ routine, if I run it enough in XCode 7.1 on OS X 10.11, eventually crashes on the string append. The debugger shows me that it stops every time at the number 23 (trying to append the number 23). I imagine this has to do with memory allocation. What am I doing wrong?
The debugger opens the string class and jams on the return statement below. In the other debugger window it says (lldb), whatever that means.
template <class _CharT, class _Traits, class _Allocator>
inline _LIBCPP_INLINE_VISIBILITY
basic_string<_CharT, _Traits, _Allocator>&
basic_string<_CharT, _Traits, _Allocator>::append(const basic_string& __str)
{
return append(__str.data(), __str.size());
}
Here's my code I'm running that seems to cause the crash if I run the routine enough times. (This was only present during monkey testing, where I clicked on my Settings menu in my Objective C/C++ application enough times that it triggered the following function enough times to crash.)
std::string Minutes[] = {};
std::string s = "";
for (int i = 1; i<= 59; i++) {
s = std::to_string(i);
if (s.length() < 2) {
s = "0" + s;
}
s = ":" + s;
Minutes->append(s);
}
This might be a plain old C++ question, perhaps not an Objective C++ question. Or, perhaps this is an Apple bug?
Note that I ran an experiment with the following change, and it never crashed after 3 attempts of 100 times:
std::string Minutes[] = {};
std::string s = "";
for (int i = 1; i<= 59; i++) {
//s = std::to_string(i);
/*
if (s.length() < 2) {
s = "0" + s;
}
s = ":" + s;
*/
//[Minutes->append(s);
Minutes->append("01");
}
Also, the following patch of code also runs 3 times, up to 100 times, without an issue:
const std::string Days[] = {"Su","M","T","W","Th","F","Sa"};
std::string Hours[] = {};
for (int i = 1; i <= 12; i++) {
Hours->append(std::to_string(i));
}
If you intend to keep null string in Minutes, do as follows
std::string Minutes[] = {""};
Then Minutes->append(s);will append s to first null string inside the Minutes[] array.
UPdate:
First you have to create a dynamic array( std::vector) of string, in case if you don't know the size of the array as follows and use index to access the string from std::vector.
// implies that you used #include <string> and #include <vector>
std::vector<std::string> Minutes;
Minutes.push_back(s)
Related
I have a homework assignment in which I have to return the number of words in a string based on the number of blank spaces. Here is my code, I am not sure what is wrong with it but I keep getting error messages when I try to compile it.
string getWordCount(string sentence){
int count = 1;
for(int i = 1; i < sentence.length(); i++){
if (s[i]==' '){
count++;
}
}
return count;
}
The error messages are:
error: ‘s’ was not declared in this scope
if (s[i]==' '){
^
error: could not convert ‘count’ from ‘int’ to ‘std::__cxx11::string {aka std::__cxx11::basic_string<char>}’
return count;
^~~~~
To compile your code, you must ensure that return type (count is an int) is compatible with the declared return type of the function (you said it would return string). So instead of:
string getWordCount(string sentence)
declare your function as :
int getWordCount(string sentence)
Note also that you use s[i], but s is not declared. You probably mean sentence[i].
Consider also solving the different algorithmic errors mentionned in the comments (i.e. wrong result for an empty string, for a string with only blank, and depending on the exercise narative, for a string where several consecutive spaces are used between two words) to finish your homework and improve your skills.
You are passing in a variable named sentence but parsing on a variable named s. Also, count is an int but your return type is string. Try this:
int getWordCount (string sentence)
{
int count = 1;
for (int i = 0; i < sentence.length (); i++)
{
if (sentence[i] == ' ')
count++;
}
return count;
}
What they said.
Plus ...Avoid indexing with square brackets. Use range-based for loops. Be careful about trivial input (no words, or even an empty string, in this case). Do not assume that white space is only spaces, or that it only comprises one character. Input parameters that a function does not modify can be declared const-reference, to avoid making an un-needed copy. Use #include <algorithm> for common tasks.
SPOILER ALERT. Read no more until you after you have finished the assignment.
#include <cctype>
#include <string>
int get_word_count(const std::string &sentence)
{
int count = 0;
bool scanning_a_word = false;
for (auto ch : sentence) {
if (!std::isspace(ch)) {
if (!scanning_a_word) {
count += 1;
scanning_a_word = true;
}
} else {
scanning_a_word = false;
}
}
return count;
}
Bonus (and better) solution. The following does not use a state-variable (scanning_a_word). I call that kind of code "bread crumb programming". You leave bread crumbs along the trail to show where you've been. Sometimes birds eat the bread crumbs. Look Ma, no crumbs!
#include <algorithm>
int get_word_count(const std::string &sentence)
{
int count = 0;
auto next = sentence.begin();
const auto done = sentence.end();
while(done != next) {
next = std::find_if_not(next, done, std::isspace);
if (done != next) {
count += 1;
next = std::find_if(next, done, std::isspace);
}
};
return count;
}
Generally, when a variable is not declared within a scope you defined it inside something locally and it does not exist after that block of code (i.e. for loop) has finished executing. Or, you have not declared the variable at all.
error: ‘s’ was not declared in this scope
if (s[i]==' '){
^
Assuming you were trying to iterate through the parameter you passed in, which is a string called sentence, I either change the variable name to s of the parameter, copy the string into another string called s, or change s to sentence in the loop. The different variations are shown below:
// change parameter name to s
string getWordCount(string s)
{
int count = 1;
for(int i = 1; i < sentence.length(); i++)
{
if (s[i]==' ')
count++;
}
return count;
}
// change s to sentence inside the loop
string getWordCount(string sentence)
{
int count = 1;
for(int i = 1; i < sentence.length(); i++)
{
if (sentence[i]==' ')
count++;
}
return count;
}
// create a string s, and copy sentence into string s
string getWordCount(string sentence)
{
int count = 1;
string s = strcpy(s, sentence);
for(int i = 1; i < sentence.length(); i++)
{
if (s[i]==' ')
count++;
}
return count;
}
For your second error, generally this error occurs when there is an issue with casting a variable type to another type. In your case, the below error occurs because you are declaring a function will return a string, however, you are trying to return an int.
error: could not convert ‘count’ from ‘int’ to ‘std::__cxx11::string {aka std::__cxx11::basic_string<char>}’
return count;
^~~~~
To fix this error, simply change the return type to int. However, if you really want to return the count as a string for some reason, convert the integer to a string before returning. Both variations are shown below.
// Change return type to int to match count's type
int getWordCount(string sentence)
{
int count = 1;
...
...
return count;
}
// If you really need to send count back as a string for some reason, convert the int to string
string getWordCount(string sentence)
{
int count = 1;
...
...
return itoa (count);
}
Please note: this is only describing why your compilation errors are occurring and how to fix them so that you can continue on with your homework assignment. This is not describing what is wrong, logically, with your code.
I newly started C++ and it feels pretty wired while writing Java for a while. So, I have this array,
char values[][10] = {"miami", "seattle", "berlin"};
int rows = sizeof values / sizeof values[0];
This is this is the function where I would like to pass the value,
// a function to reverse the strings
void App::reverse(char *str) {
}
When I do the loop, I can't apparently pass the value there,
for (int i = 0; i < rows; ++i) {
// first character of the string
char *firstPtr = values[i];
reverse(firstPtr);
}
The line reverse(firstPtr) provides error which I don't understand. The error message says Too few arguments, expected 2.
What is the issue here? I apologize for any mistakes as writing the C++ for the first time and the pointer stuff feels strange.
UPDATE
This is the piece of code I would like to exexute,
void App::reverse(char* str) {
// get the first character of the string
char *ptrEnd = str;
char temp;
if (str){
while (*ptrEnd) {
ptrEnd++;
}
ptrEnd--;
// as long the first adddress is lesser than the end
while (str < ptrEnd) {
temp = *str;
*str++ = *ptrEnd;
*ptrEnd-- = temp;
}
}
}
There is too little information here to be sure, but it looks like you have
using namespace std;
Somewhere in your code. Don't do this! In this case, the standard library has a function reverse() in std that takes two parameters.
Furthermore, you have void App::reverse(char *str), but that cannot be seen from void myArray::reverse(char* str), so your own reverse() cannot be called as-is - you would need to do App::reverse() if the function is a class static.
I'm working with c++ and XE8. Given the following code:
String __fastcall RemoveCharsFromString(String &str, const String &c)
{
for(unsigned int i=0; i<c.Length(); ++i)
{
str.Delete(std::remove(str[0], str.LastChar(), c[i]), str.LastChar());
}
}
Received errors:
Using str.Delete(remove(str[0], str.LastChar(), c[i]), str.LastChar()); results in a
Cannot convert 'int' to 'const char *'
error inside the for loop.
Using str.Delete(std::remove(str[0], str.LastChar(), c[i]), str.LastChar()); results in a
Could not find a match for 'remove(wchar_t,wchar_t*,wchar_t)'
error inside the for loop.
Searching SO and the web, it's my understanding this error is usually received when code is written with single quotes when double quotes should have been used. I don't believe that scenario is applicable in this case.
The return type of String is Embarcadero's UnicodeString. Details can be found here: RAD Studio VCL Reference - UnicodeString Class
In C++Builder, String refers to System::String, which is an alias for System::UnicodeString in XE8.
There are a lot of mistakes in your code.
The System::String::Delete() method expects an index and a count as input, but that is not what you are trying to pass to it. You are expecting Delete() to work like the STL std::wstring::erase() method, and that is simply not the case.
You are not taking into account that System::String::operator[] is 1-based. It is not 0-based, like your code is assuming.
The System::String::LastChar() method returns a pointer to the last UTF-16 encoded Unicode character in the string. It does not return a pointer to the string's null terminator, like your code is assuming.
You are calling the STL std::remove() algorithm that takes a range of iterators as input, shifts all copies of the specified value to the end of the range, and then returns a new iterator to where the "removed" values have been moved to within the range (so they can be erase()'d from the container that owns the iterators). You cannot mix System::String::Delete() and std::remove() the way you are attempting to do. If you really want to use std::replace(), you need to use it more like this instead:
String __fastcall RemoveCharsFromString(String &str, const String &c)
{
for(int i = 1; i <= c.Length(); ++i)
{
const Char *start = str.c_str();
const Char* end = start + str.Length();
Char* ptr = std::replace(start, end, c[i]);
str.Delete(1 + std::distance(start, ptr), std::distance(ptr, end));
}
}
That being said, Embarcadero's RTL has its own System::Sysutils::StringReplace() function that you can use instead of std::replace():
#include <System.SysUtils.hpp>
String __fastcall RemoveCharsFromString(String &str, const String &c)
{
for(int i = 1; i <= c.Length(); ++i)
{
str = StringReplace(str, c[i], L"", TReplaceFlags() << rfReplaceAll);
}
}
Or, if you need to take UTF-16 surrogates into account in the c string (which std::remove() does not account for):
#include <System.SysUtils.hpp>
#include <System.Character.hpp>
String __fastcall RemoveCharsFromString(String &str, const String &c)
{
int i = 1;
while (i <= c.Length())
{
String chr;
if (IsSurrogatePair(c, i))
chr = c.SubString(i, 2);
else
chr = c.SubString(i, 1);
str = StringReplace(str, chr, L"", TReplaceFlags() << rfReplaceAll);
i += chr.Length();
}
}
I am having a bit of an issue with a current task of mine. Basically, I am given an XML file and am trying to parse it for key information. For example, some lines will be like this:
<IPAddress>123.45.67</IPAddress>
And I am to get the value of 123.45.67, nothing too bad at all. I was told not to use a XML parser and just parse it manually, which was pretty easy. However, I am having issues with the second part of the task. Basically, I am to make a class with certain member variables and declare them based on the values I parse. So let's say the class is called Something and there is a member variable called IPAddress. I am to then update the value of IPAddress to 123.45.67 so when someone calls Something.IPAddress in the main method, it returns 123.45.67. This was my initial attempt at it:
#include <iostream>
#include <fstream>
#include <string>
#include <sys/stat.h>
using namespace std;
class Something
{
public:
string location;
string IPAddress;
string theName;
int aValue;
//loop through the array from the method below
void fillContent(string* array)
{
for(int i = 0; i < array->size(); i++)
{
string line = array[i];
if((line.find("<") != std::string::npos) && (line.find(">")!= std::string::npos))
{
unsigned first = line.find("<");
unsigned last = line.find(">");
string strNew = line.substr (first + 1, last - first - 1); //this line will get the key, in this case, "IPAddress"
unsigned newfirst = line.find(">");
unsigned newlast = line.find_last_of("<");
string strNew2 = line.substr(newfirst + 1, newlast - newfirst - 1); //this line will get the value, in this case, "123.45.67"
if(strNew == "IPAddress")
{
IPAddress = strNew2; //set the member variable to the IP Address
}
}
}
}
//this method will create an array where each element is a line from the xml
void fillVariables()
{
string line;
ifstream myfile ("content.xml");
long num = //function that gets size that I didn't add to make code shorter!;
string *myArray;
myArray = new string[num];
string str1 = "";
string strNew2 = "";
int counter = 0;
if (myfile.is_open())
{
while ( getline (myfile,line) )
{
myArray[counter] = line;
counter++;
}
myfile.close();
}
fillContent(myArray);
}
};
int main(int argc, char* argv[])
{
Something local;
local.fillVariables();
cout << local.IPAddress<< endl; // should return "123.45.67"
return 0;
}
Now this does do what I want it to do, however, you can see I need the if statement. Assuming I have at least 20 of these member variables, having 20 if-statements would be annoying and just frowned upon. Is there any other way I could somehow access the member variables from the class? Sorry if my question was long, I just wanted to make sure everything that is needed to understand the question is provided! Please let me know if anything crucial that may not be there should be added.
Thanks a lot!
This may be considered bad style, but I usually just do:
// at the top of the 'fillContent' function
std::map<string, string*> varmap{
{"IPAddress", &IPAddress},
{"AnotherField", &AnotherField}
};
// If you're not using C++11, you can also try:
// std::map<string, string*> varmap;
// varmap["IPAddress"] = &IPAddress;
// varmap["AnotherField"] = &AnotherField;
// parsing code goes here
*varmap[strNew] = strNew2;
I am trying to grab sub-strings out of a larger string and I have got it to work in a small program but when I try to run it into the real program it just goes wrong. I am building off someone else s function and got it to work for my purpose, but cannot get it to work in the main program I need it in. I will limit the program down to where I think error is occurring.
Problem: I pass in same value into function findStats(std::string sString) but get different results.
Case I:
stats = findStats("^9dff9d[Attribute 0% Active Defense 0]\r^f2f3f2Mana: 1411 ^00ff00(+1975)\r^f2f3f2^9dff9d[Attribute 0% Active Mana 0]\r^f2f3f2^ffc000Fortify Level: 12/12\r^f2f3f2^006effIdentified Attribute: + 6% Crit Damage\rIdentified Attribute: + 6 Accuracy\r^f2f3f2^006eff^O053Sacrifice Elapse(6/8)\r^00ff00 ^O041Desollar's Shadow\rÌÌÌÌÌÌÌÌL«");
The above case will output correctly and stores \r offsets correctly.
Case II:
stats = findStats((std::string)((char*)&buffer));
Case II is the case I need to work and has the same value as above Case I at start of function findStats but offsets for \r Are not stored for w.e reason when sString has same value at start of function.
//Function that finds positioning of \r
void calc_z (std::string &s, std::vector<int> & z)
{
int len = s.size();
z.resize (len);
int l = 0, r = 0;
for (int i=1; i<len; ++i)
if (z[i-l]+i <= r)
z[i] = z[i-l];
else
{
l = i;
if (i > r) r = i;
for (z[i] = r-i; r<len; ++r, ++z[i])
if (s[r] != s[z[i]])
break;
--r;
}
}
std::vector<std::string> findStats(std::string sString){
//sString is exactly the same in value for both cases of stats at this point
int offSet = 0;
int sOffsets[100] = {};
std::vector<std::string> t1;
std::string main_string = sString;
std::string substring = "\r";
std::string working_string = substring + main_string;
std::vector<int> z;
calc_z(working_string, z);
for(int i = substring.size(); i < working_string.size(); ++i){
if(z[i] >=substring.size()){
sOffsets[offSet] = i;
offSet++;
}
}
.... code ....problem occurs right above offsets are not stored for \r
}
void main()
{
std::vector<std::string> stats;
std::string buffer[10];
...code...
...code to find string and store in buffer...
stats = findStats((std::string)((char*)&buffer));
//stats = findStats("^9dff9d[Attribute 0% Active Defense 0]\r^f2f3f2Mana: 1411 ^00ff00(+1975)\r^f2f3f2^9dff9d[Attribute 0% Active Mana 0]\r^f2f3f2^ffc000Fortify Level: 12/12\r^f2f3f2^006effIdentified Attribute: + 6% Crit Damage\rIdentified Attribute: + 6 Accuracy\r^f2f3f2^006eff^O053Sacrifice Elapse(6/8)\r^00ff00 ^O041Desollar's Shadow\rÌÌÌÌÌÌÌÌL«");
for( std::vector<std::string>::const_iterator i = stats.begin(); i != stats.end(); ++i)std::cout << *i << ' ' << std::endl;
std::cin.get();
}
This statement: (std::string)((char*)&buffer) does not do what you think it does.
std::vector is not a simple array.
If you take address of std::vector, that won't be the address of the first element within std::vector.
YOu can't just cast const char* or char* into std::string. You can, however, construct new std::string using provided const char* or char * c-style string. const char *str = asdf; std::string s = std::string(str);.
So, to summarize:
If you want to pass several strings at once in std::vector, pass the buffer by const reference
typedef std::vector<std::string> StringVector;
void test(const StringVector& v){
for (StringVector::const_iterator i = v.begin(); i != v.end(); i++)
std::cout << *i << std::endl;
}
...
StringVector strings;
test(strings);
If you want to WRITE something into std::vector, pass it by reference:
typedef std::vector<std::string> StringVector;
void test(const StringVector& out){
out.push_back("test");
}
...
StringVector strings;
test(strings);
If you want to pass a single string from, vector, just pass the element itself (by reference, const reference, or by value, depending on what you want to do with it), without casts.
typedef std::vector<std::string> StringVector;
void test(const std::string& s){
std::cout << s << std::endl;
}
...
StringVector strings;
strings.push_back("test");
test(strings[0]);
--edit--
IN addition to that:
std::vector<std::string> findStats(std::string sString){
//sString is exactly the same in value for both cases of stats at this point
int offSet = 0;
int sOffsets[100] = {};//<<here's a problem
Using array with fixed size in this case is a bad idea. Your array is small, and it WILL overflow on any string larger than 100 bytes, breaking/crashing your program. You can simply store results on std::vectro<std::string>, make vector of structs, or use std::map, depending on your goals.