Here is a problem I have , I have a vector of filenames and I want to check if they end by .jpg or by .png so I made some code with iterators and the STL, this i also for creating a std::map with those names as keys and with value a texture, so here is my code, that does a Segmentation Fault error at the line 11:
#include "TextureManager.h"
std::map<std::string,sf::Texture> createTextureArray(){
std::map<std::string,sf::Texture> textureArray;
std::vector<std::string>::iterator strIt;
std::string fName;
for(strIt = textureFileList().begin(); strIt != textureFileList().end(); strIt++){
sf::Texture texture;
fName = *strIt;
if(fName[fName.size()] == 'g' && fName[fName.size()-1] == 'p' && fName[fName.size()-2] == 'j' && fName[fName.size()-3] == '.'){
texture.loadFromFile(fName);
textureArray[fName] = texture;
}
else if(fName[fName.size()] == 'g' && fName[fName.size()-1] == 'n' && fName[fName.size()-2] == 'p' && fName[fName.size()-3] == '.'){
texture.loadFromFile(fName);
textureArray[fName] = texture;
}
}
return textureArray;
}
I think this is the only code needed to try to understand the problem , but if anyone wants more of this code here is the Github repo
This is not shown in your question, but textureFileList returns by value, which means that you get a copy of the std::vector<std::string> it returns. You're calling this function twice, once for begin() and then once for end(), which means you're calling those functions on different copies of the vector. Obviously the beginning of one copy has no relation to the end of another copy. Not only this, but those copies are being destroyed immediately afterwards, because they are temporaries. Instead, you should store a single copy and call begin and end on that:
std::vector<std::string> fileList = textureFileList();
for(strIt = fileList.begin(); strIt != fileList.end(); strIt++){
And fName[fName.size()] is always '\0'.
You should use
if(fName[fName.size()-1] == 'g' && fName[fName.size()-2] == 'p' && fName[fName.size()-3] == 'j' && fName[fName.size()-4] == '.'){
and
else if(fName[fName.size()-1] == 'g' && fName[fName.size()-2] == 'n' && fName[fName.size()-3] == 'p' && fName[fName.size()-4] == '.'){
I suppose that function textureFileList() returns an object of type std::vector<std::string> by reference.
The segmentation fault can occur due to accessing non-existent character of a string.
if(fName[fName.size()] == 'g' && fName[fName.size()-1] == 'p' && fName[fName.size()-2] == 'j' && fName[fName.size()-3] == '.'){
There is no such character of the string with index fName.size(). The valid range of indexes is 0, size() -1 provided that size() is not equal to zero.
You should use another approach. You have to find the period using member function rfind() and then compare the substring with a given extension.
Here is an example
#include <iostream>
#include <string>
int main()
{
std::string s( "SomeFile.jpg" );
std::string::size_type n = s.rfind( '.' );
if ( n != std::string::npos && s.substr( n ) == ".jpg" )
{
std::cout << "It is a JPEG file" << std::endl;
}
return 0;
}
The output is
It is a JPEG file
Your for loop could be written the following way
for ( const std::string &fName : textureFileList() )
{
const char *ext[] = { "jpg", "png" };
std::string::size_type n = s.rfind( '.' );
if ( n != std::string::npos &&
( fName.substr( n + 1 ) == ext[0] || fName.substr( n + 1 ) == ext[1] ) )
{
sf::Texture texture;
texture.loadFromFile( fName );
textureArray[fName] = texture;
}
}
If you need to erase the extension then you can write simply
fName.erase( n );
In this case fName has to be defined as a non-const reference in the for statement.
Related
To check at an index whether it's a consonant or a vowel , and having a bit of a problem with the writing of the logic in syntactical form?
Tried running it but the count variable wasn't incrementing.
if (s[i]!= ('a' || 'e' || 'i' || 'o' || 'u') && s[i+1] == ('a' || 'e' || 'i' || 'o' || 'u'))
It keeps giving 0 , i.e , the initialised value as the output.
Write a function. For example:
bool isvowel( char c ) {
return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u';
}
Then you can simply say:
if ( !isvowel( s[i] ) && isvowel( s[i+1] ) ) {
// do something
}
Write a separate function that checks whether a given character is a vowel. For example
#include <iostream>
#include <string>
#include <cstring>
#include <cctype>
bool is_vowel( char c )
{
const char *vowels = "aeiou";
return c != '\0' && std::strchr( vowels, std::tolower( ( unsigned char )c ) );
}
int main( void )
{
std::string s( "Hi" );
if ( not is_vowel( s[0] ) && is_vowel( s[1] ) ) std::cout << s << '\n';
}
The program output is
Hi
I'm not getting errors, but the output is incorrect. I'm not sure what I'm doing wrong. I can only use functions from string library.
#include <iostream>
#include <string>
#include <cstdlib>
using namespace std;
int main() {
string message, pig_message;
getline(cin, message);
unsigned int x = message.find_first_of("aeiou");
if (message[x] == 'a' || 'e' || 'i' || 'o' || 'u' ) {
pig_message = message + "yay";
cout << pig_message;
}
else if (!(message[x] == 'a' || 'e' || 'i' || 'o' || 'u' )) {
pig_message = message.substr(1) + message[0] + "ay";
cout << pig_message;
}
system("pause");
return 0;
}
The first if statement is always true. You should change it to
if (message[x] == 'a' || message[x] == 'e' || message[x] == 'i' || message[x] == 'o' || message[x] == 'u' ) {
Also, you could change the else if (...) { line to just
else {
if you want it to be executed every time the first if statement is not true.
Your comparison statement is incorrect.
Make sure your function is actually iterating through the letters, and that you're concatenating the strings correctly.
So:
unsigned int x = message.find_first_of("aeiou"); // Returns the first match
if(message[x] == 'a' || message[x] == 'e'...) // Currently your code reads as only checking for a.
Think of it as IF message[x] = a, IF e, IF i
vs
if message[x] = a, IF message[x] = i
What does your code do after it finds a match?
pig_message = message + 'yay' would add "yay" to the whole message string.
It would then print it out and move on, without doing anything to the other vowels.
I'm new to C++ myself but that's how I've understood your code.
It might be better to go through the whole input string one letter at a time in a for loop with your if else statements to add the strings inside the loop.
I search the most concise and efficient way to find the first printf format sequence (conversion specification) in a C++ string (I cannot use std::regex as they are not yet implement in most in compilers).
So the problem is to write an optimized function that will return the beginning of the first printf-format sequence pos and its length n from an input string str:
inline void detect(const std::string& str, int& pos, int& n);
For example, for:
%d -> pos = 0 and n = 2
the answer is: %05d -> pos = 15 and n = 4
the answer is: %% %4.2f haha -> pos = 18 and n = 5
How to do that (clever and tricky ways are welcome)?
Scan forward for %, then parse the content from there. There are some quirky ones, but not THAT bad (not sure you want to make it an inline tho').
General principle (I'm just typing as I go along, so probably not the BEST form of code ever written - and I haven't tried to compile it at all).
inline void detect(const std::string& str, int& pos, int& n)
{
std::string::size_type last_pos = 0;
for(;;)
{
last_pos = str.find('%', last_pos)
if (last_pos == std::string::npos)
break; // Not found anythin.
if (last_pos == str.length()-1)
break; // Found stray '%' at the end of the string.
char ch = str[last_pos+1];
if (ch == '%') // double percent -> escaped %. Go on for next.
{
last_pos += 2;
continue;
}
pos = last_pos;
do
{
if (isdigit(ch)) || ch == '.' || ch == '-' || ch == '*' ||
ch == '+' || ch == 'l' || ch == 'L' || ch == 'z' ||
ch == 'h' || ch == 't' || ch == 'j' || ch == ' ' ||
ch == '#' || ch == '\'')
{
last_pos++;
ch = str[last_pos+1];
}
else
{
// The below string may need appending to depending on version
// of printf.
if (string("AacdeEfFgGiopusxX").find(ch) != std::string::npos)
{
// Do something about invalid string?
}
n = last_pos - pos;
return;
}
} while (last_pos < str.length());
}
}
edit2: This bit is probably better written as:
if (isdigit(ch)) || ch == '.' || ch == '-' || ch == '*' ||
ch == '+' || ch == 'l' || ch == 'L' || ch == 'z' ||
ch == 'h' || ch == 't' || ch == 'j' || ch == ' ' ||
ch == '#' || ch == '\'') ...
if (string("0123456789.-*+lLzhtj #'").find(ch) != std::string::npos) ...
Now, that's your homework done. please report back with what grade you get.
Edit: It should be noted that some things that a regular printf will "reject" is accepted by the above code, e.g. "%.......5......6f", "%5.8d", "%-5-6d" or "%-----09---5555555555555555llllld". If you want the code to reject these sort of things, it's not a huge amount of extra work, just need a little bit of logic to check "have we seen this character before" in the "check for special characters or digit", and in most cases the special character should only be allowed once. And as the comment says, I may have missed a couple of valid format specifiers. It gets further trickier if you also need to cope with "this 'l' is not allowed with 'c'" or such rules. But if the input isn't "malicious" (e.g. you want to annotate where on which line there are format specifiers in a working C source file), the above should work reasonably well.
I'm doing some string manipulation, and am looping through a string with a string iterator, and under certain conditions insert a character into the string. Here is the code:
string * const Expression::process(char * const s)
{
if(s == NULL)
{
printf("(from Expression::process())\n > NULL data");
return NULL;
}
string *rtrn = new string(s);
string garbage;
//EDIT
rtrn->erase(remove(rtrn->begin(), rtrn->end(), ' '), rtrn->end());
for(string::iterator j = rtrn->begin(); (j+2) != rtrn->end(); j++)
{
if(Operator::isValid(&*j, garbage) != Operator::SYM && *(j+1) == '-' && (Operator::isValid(&(*(j+2)), garbage) != Operator::INVALID))
rtrn->replace(j+1, j+2, "+-");
}
rtrn->insert(rtrn->begin(), '(');
rtrn->append(")");
for(string::iterator k = rtrn->begin(); k+1 != rtrn->end(); k++)
{
if(*k == '-' && !Operator::isValidNum(*(k+1)))
rtrn->replace(k, k+1, "-1*");
if((Operator::isValid(&*(k+1), garbage) != Operator::INVALID && (Operator::isValid(&*(k+1), garbage) != Operator::SYM || *(k+1)=='(')) &&
(Operator::isValid(&*k, garbage) == Operator::VAR || Operator::isValidNum(*k) || *k==')') &&
!(Operator::isValid(&*k, garbage) == Operator::NUM && Operator::isValid(&*(k+1), garbage) == Operator::NUM))
{
if(Operator::isValid(&*k, garbage) == Operator::SYM)
{
if(opSymb::valid[garbage]->getArguments())
rtrn->insert(k+1, '*');
}
else
{
rtrn->insert(k+1, '*');
}
}
}
return rtrn;
}
When s is equal to "20x(5x+3)-6x(5x^2+11/2)", I get a runtime error at rtrn->insert(k+1, '*'); under the else statement when it gets to "5x^2" in the string. Basically, when it makes the 6th insertion, it crashes on me and complains about the iterator + operator being out of range. Although, when I'm debugging, it does pass the correct offset. And it does successfully insert the char into the string, but after the function executes, the iterator is pointing to corrupt data.
for(string::iterator i = rtrn->begin(); i != rtrn->end(); i++)
{
if(*i == ' ')
rtrn->erase(i);
}
There are errors in this and all code snippets like this: for loop can`t be used for deleting element from a container, becase erase() - invalidates all iterators related to the container,
I offer you to use while loop instead, here is a short example from another question I answered:
string::iterator it = input.begin();
while (it != input.end())
{
while( it != input.end() && isdigit(*it))
{
it = input.erase(it);
}
if (it != input.end())
++it;
}
So after research and help from you guys, it seems I have to refine my code so that any string functions such as erase, insert, or replace writes over the iterator passed to the function. So I need to change my code to something like this
for(string::iterator k = rtrn->begin(), m=k+1; m != rtrn->end(); k=m, m=k+1)
{
if(*k == '-' && !Operator::isValidNum(*m))
rtrn->replace(k, m, "-1*");
if((Operator::isValid(&*m, garbage) != Operator::INVALID && (Operator::isValid(&*m, garbage) != Operator::SYM || *m=='(')) &&
(Operator::isValid(&*k, garbage) == Operator::VAR || Operator::isValidNum(*k) || *k==')') &&
!(Operator::isValid(&*k, garbage) == Operator::NUM && Operator::isValid(&*m, garbage) == Operator::NUM))
{
if(Operator::isValid(&*k, garbage) == Operator::SYM)
{
if(opSymb::valid[garbage]->getArguments())
rtrn->insert(m, '*');
}
else
{
m=rtrn->insert(m, '*');
}
}
}
I'm working on a project in c++ (which I just started learning) and can't understand why this function is not working. I'm attempting to write a "Person" class with a variable first_name, and use a function set_first_name to set the name. Set_first_name needs to call a function(the one below) to check if the name has any numbers in it. The function always returns false, and I'm wondering why? Also, is this the best way to check for numbers, or is there a better way?
bool Person::contains_number(std::string c){ // checks if a string contains a number
if (c.find('0') == std::string::npos || c.find('1') == std::string::npos || c.find('2') == std::string::npos || c.find('3') == std::string::npos
|| c.find('4') == std::string::npos || c.find('5') == std::string::npos || c.find('6') == std::string::npos || c.find('7') == std::string::npos
|| c.find('8') == std::string::npos || c.find('9') == std::string::npos){// checks if it contains number
return false;
}
return true;
}
Change all your || to &&.
Better yet:
return std::find_if(s.begin(), s.end(), ::isdigit) != s.end();
Or, if you have it:
return std::any_of(s.begin(), s.end(), ::isdigit);
C++11:
#include <algorithm>
#include <cctype>
#include <string>
#include <iostream>
bool has_any_digits(const std::string& s)
{
return std::any_of(s.begin(), s.end(), ::isdigit);
}
int main()
{
std::string query("H311o, W0r1d!");
std::cout << query << ": has digits: "
<< std::boolalpha
<< has_any_digits(query)
<< std::endl;
return 1;
}
Output:
H311o, W0r1d!: has digits: true
It always returns false because your logic is backwards. You are using the || operator with == npos checks. If any one particular digit is missing from the string, == npos evaluates to true and || is satisfied, so you return false. You need to using != npos checks and then return true if any check evaluates to true:
bool Person::contains_number(const std::string &c)
{
if (c.find('0') != std::string::npos ||
c.find('1') != std::string::npos ||
c.find('2') != std::string::npos ||
c.find('3') != std::string::npos ||
c.find('4') != std::string::npos ||
c.find('5') != std::string::npos ||
c.find('6') != std::string::npos ||
c.find('7') != std::string::npos ||
c.find('8') != std::string::npos ||
c.find('9') != std::string::npos)
{
return true;
}
return false;
}
Or:
bool Person::contains_number(const std::string &c)
{
return (
c.find('0') != std::string::npos ||
c.find('1') != std::string::npos ||
c.find('2') != std::string::npos ||
c.find('3') != std::string::npos ||
c.find('4') != std::string::npos ||
c.find('5') != std::string::npos ||
c.find('6') != std::string::npos ||
c.find('7') != std::string::npos ||
c.find('8') != std::string::npos ||
c.find('9') != std::string::npos
);
}
A simplier solution is to use find_first_of() instead of find():
bool Person::contains_number(const std::string &c)
{
return (c.find_first_of("0123456789") != std::string::npos);
}
How to test if a string contains any digits in C++
This should do it!
if (std::string::npos != s.find_first_of("0123456789"))
{
std::cout << "digit(s)found!" << std::endl;
}
You are using || (or operator) to check several conditions in an if statement.
The or operator returns true (satisfies the condition) if one of the expression is true.
The or operator evaluates first the expression on its left: if that is true then it doesn't evaluate the expression on its right and returns true.
If the expression on the left is false then the expression on the right is evaluated and the result of it is returned as result of || operator
This is what happen in your function:
does c contains '0' ? if no (because std::string::npos in find() means not found) then return false
does c contains '1' ? if no then return false
...
So, replace the or operators with && (and operator).