How to imitate std::string's find_first_not_of using strspn - c++

I'm trying to create a custom string class similar to std::string.
And I'm having a trouble implementing 'find_first_not_of'.
Here's my test code
#include <iostream>
class String {
private:
char *m_data;
int m_length;
char *alloc(int size);
int length() const {return m_length;}
int size() const {return m_length;}
const char *c_str() const {return m_data;}
public:
String(const char *str=0);
int find_first_not_of(const char *str);
static const int npos;
};
const int String::npos = -1;
char * String::alloc(int size)
{
char * str = new char[size+1];
return str;
}
String::String(const char *str)
{
if (!str) str = "";
m_length = static_cast<int>(strlen(str));
m_data = alloc(m_length);
strcpy(m_data, str);
}
int String::find_first_not_of(const char *str)
{
size_t len = strspn(c_str(), str);
if (len == 0)
return -1;
else
return len;
}
int main(int argc, const char * argv[]) {
String A = "123";
std::string B = "123";
if (A.find_first_not_of("0123456789") == -1)
std::cout << "A is digit" << std::endl;
else
std::cout << "A is not digit" << std::endl;
if (B.find_first_not_of("0123456789") == -1)
std::cout << "B is digit" << std::endl;
else
std::cout << "B is not digit" << std::endl;
return 0;
}
And this is the result I see if I run the code.
A is not digit
B is digit
Program ended with exit code: 0
Can someone please point me what I'm missing?
Thanks!

You are confusing your String::find_first_not_of with std::string::find_first_not_of. They are different functions that have different functionality.
I really don't understand what String::find_first_not_of needs to do, but here is what each of them returns (one the length of the string and the other one the position):
std::string::find_first_if_not (from here):
The position of the first character that does not match.
If no such characters are found, the function returns string::npos.
strspn (from here):
The length of the initial portion of str1 containing only characters that appear in str2.
Therefore, if all of the characters in str1 are in str2, the function returns the length of the entire str1 string, and if the first character in str1 is not in str2, the function returns zero.
So even the inner working of the functions are different.
You should be able to follow based on this info.

This one worked just like std::string's find_first_not_of.
int String::find_first_not_of(const char *str, int pos)
{
const int len = static_cast<int>(strspn(c_str() + pos, str));
if (len + pos == m_length)
return -1; //npos
else
return len + pos;
}
#Garmekain's explanation was really helpful. Thank you.

Related

Why does my char* copier return different things?

Writing a simple string copier and testing it in the main() fucntion. What's odd is that sometimes the program returns
"HelloHello"
like it should, but maybe every third time I run it, the program prints out:
"Hello!Hello!▌▌▌▌▌▌▌▌▌▌▒"UòB╚"
Why is the tail of garbage data only sometimes being added to the end of my second string?
#include <iostream>
using namespace std;
int strlength(const char* c)
{
int size = 0;
while (*c) {
++c;
++size;
}
return size;
}
char* mystrdup(const char* c)
{
int size = strlength(c);
char* result = new char;
copy(c, c + size, result);
return result;
}
void print_array(const char* c)
{
int size = strlength(c);
while (*c) {
cout << *c;
++c;
}
}
int main()
{
char test[] = "Hello!";
char* res = mystrdup(test);
print_array(test);
print_array(res);
}
The program has undefined behavior because you are allocating not enough memory for the result string.
char* mystrdup(const char* c)
{
int size = strlength(c);
char* result = new char;
^^^^^^^^^^^^^^^^^^^^^^^
copy(c, c + size, result);
return result;
}
Moreover you are not copying the terminating zero to the result string.
At least the two functions strlength and mystrdup can look the following way
size_t strlength( const char *s )
{
size_t size = 0;
while ( s[size] ) ++size;
return size;
}
char * mystrdup( const char *s )
{
size_t size = strlength( s ) + 1;
char *result = new char[size];
copy( s, s + size, result );
return result;
}
Of course instead of the standard algorithm std::copy you could use the standard C function strcpy declared in the header <cstring>.
strcpy( result, s );
And do not forget to delete the allocated array.
char* res = mystrdup(test);
//…
delete [] res;
Pay attention to that the function print_array does not use the variable size. There is no need to output a C-string character by character.
The function could be defined like
std::ostream & print_array( const char *s, std::ostream &os = std::cout )
{
return os << s;
}
And at last the identifier c is usually used with single objects of the type char. If you deal with a string then it is better to use the identifier s.
You have multiple bugs in your code. You allocate wrong memory (char instead of char array). You don't delete the memory. Stop using C-string and use std::string
#include <iostream>
#include <string>
using std::cout;
void print_array(const char* c)
{
while (*c) {
cout << *c;
++c;
}
}
int main()
{
std::string = "Hello!";
std::string res = test;
print_array(test.c_str());
print_array(res.c_str());
}
In strcpy you need to create a char size.
char* mystrdup(const char* c)
{
int size = strlength(c);
char* result = new char[size];
copy(c, c + size, result);
return result;
}

Program to check if one string is contained cyclic in the other one

doing some exrecises for upcoming test. a bit stuck in this one.
"Write a program that asks the user for two strings and checks and prints a message if the second string is contained cyclic in the first string. The cyclic containment means that either the second string appears normally within the first string or the second string appears so that its prefix appears at the end of the first string and the continuation appears at the beginning of the first string".
You can assume that the strings contain only lowercase letters.
String functions are only allowed are : strlen, strcpy, strcmp, strcat
for example:
String A: itisaniceday
String B: sanic
Is a regular occurrence
String A: itisaniceday
String B: dayit
It's a cyclic occurence.
what I did so far:
#include <iostream>
#include <string.h>
using namespace std;
#define Max 128
int isCyclic(char* str1, char* str2);
int main()
{
char* str1 = new char[Max];
char* str2 = new char[Max];
cout << "Please enter two strings:" << endl;
cin >> str1 >> str2;
cout << isCyclic(str1, str2) << endl;
delete[] str1;
delete[] str2;
}
int isCyclic(char* str1, char* str2)
{
int s1 = strlen(str1);
int s2 = strlen(str2);
if (s1!=s2) // if string size is diffrent - they are not contained cyclic
{
return 0;
}
}
You will need two loops, first one over string 1 which is our starting point in string 1 for comparison and second one over string 2 which will be matched to string 1 in a cyclic way. If we reach the end of string 1 and still some characters are left in string 2 then cycle through string 1 starting from index 0.
#include <stdio.h>
#include <iostream>
#include <string.h>
// Should be avoided in general. Use scope resolution instead.
using namespace std;
char* isCyclic(char* s1, char* s2){
int s1_size = strlen(s1);
int s2_size = strlen(s2);
// s1 must contain s2
if(s2_size > s1_size)
return "No Occurence";
for(int i = 0; i < s1_size; i++){
int current = i;
// Boolean to track if we are currently cycling through s1
bool inCycle = false;
int j = 0;
for(; j < s2_size; j++, current++){
// character wise comparision
if(s2[j] != s1[current])
break;
if(! inCycle){
// start from first. Note that we are setting current = -1.
// as we will be incrementing it in the for loop.
if(current == s1_size - 1 && j < s2_size - 1){
current = -1;
inCycle = true;
}
}
}
if(j == s2_size){
if(inCycle)
return "cyclic";
else
return "regular";
}
}
return "No Occurence";
}
int main()
{
printf("Hello World\n");
char* s1 = "itisaniceday";
char* s2 = "dayitis";
cout<<"Occurence Type: "<<isCyclic(s1, s2)<<endl;
return 0;
}
Here is a solution for the second part of the problem (cyclic part). I simply go through all of the characters in the first string and checked if they are the beginning of a cyclic appearance of the second string.
To check that I used % (the modolu operation) if you don't know what it dose then you really need to learn it now.
Also I used bool instead of int because numbers are confusing (and cursed).
#include <iostream>
#include <string.h>
using namespace std;
#define Max 128
bool isCyclic(char* str1, char* str2);
bool isCyclic(char* str1, char* str2,int start);
int main()
{
char* str1 = new char[Max];
char* str2 = new char[Max];
cout << "Please enter two strings:" << endl;
cin >> str1 >> str2;
cout << isCyclic(str1, str2) << endl;
delete[] str1;
delete[] str2;
}
bool isCyclic(char* str1, char* str2) {
for(int i = 0; i < strlen(str1); i++) {
if(str1[i] == str2[0] && isCyclic(str1,str2,i)) {
return true;
}
}
return false;
}
bool isCyclic(char* str1, char* str2,int start)
{
int containingStrLen = strlen(str1);
for(int i = 0; i < strlen(str2); i++) {
if(str1[(start + i)%containingStrLen] != str2[i]) {
return false;
}
}
return true;
}
There are some things missing in this code still:
1) The first part of the problem (it can easily be derived from this code).
2) Some size validation such as making sure that str1 is bigger then str2 before using is cyclic. And that the strings are smaller then Max (I assume).
3) A proper result print.
Good luck in your exam :)
There's a simple trick : if you duplicate the string's prefix at its own end, the problem becomes a straight substring search as a cyclic match would be recomposed at the end. It also handles the corner case where the substring loops back on itself, such as "looploop" inside of "loop".
So here's how you'd do it in broken C-ish dialect:
bool containsCyclic(char const *string, char const *substring) {
std::size_t const stringLen = std::strlen(string);
std::size_t const substringLen = std::strlen(substring);
// Too long a substring wouldn't fit in the string
if(substringLen > 2 * stringLen)
return false;
// Concatenate `string` with its own substring-long prefix
char *const loopedString = new char[stringLen + substringLen + 1];
std::strcpy(loopedString, string);
{ // Partial reimplementation of std::strncpy(loopedString, string, substringLen)
char const *src = string;
char *dest = loopedString + stringLen;
for(std::size_t count = 0; count < substringLen; ++count)
*dest++ = *src++;
*dest = '\0';
}
{ // Partial and naïve reimplementation of std::strstr(loopedString, substring)
for(char const *start = loopedString; start < loopedString + stringLen; ++start) {
// Check if substring is present at this offset
char const *s1 = start;
char const *s2 = substring;
while(*s2 != '\0' && *s1 == *s2)
++s1, ++s2;
if(*s2 == '\0') {
// We found a complete match of substring inside loopedString
delete[] loopedString;
return true;
}
}
}
// No match found
delete[] loopedString;
return false;
}
And just for kicks, here it is in C++:
bool containsCyclicCpp(std::string const &string, std::string const &substring) {
std::string const loopedString = string + string.substr(0, substring.size());
return loopedString.find(substring) != std::string::npos;
}
See it live on Coliru (with tests!)

copy string with offsett C++

i need help to copy string with offset and invert
how i implement this function ?
void copyString(char *input, int offset, int length, bool invert, char *output, int output_offset)
input : input string
offset : starting position to be copied.
length : length of substring of input to be copied.
invert : the result would be inverted if true.
output : result string.
output_offset : starting position of output to receive the copied string
example
st ="Hello World";
st2="My Name is Fatima";
copyString(st,6,5,true,st2,11) -> st2 = "My Name is dlrow";
i have succes with copystring function without offset and invert..
this my function
void stringcpy(const char* src,char* dest)
{
while(*src != '\0')
{
*dest++ = *src++;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
const char str[] = "fatima";
char str2[sizeof(str)] ;
const char* s = str;
char* s2 = str2;
stringcpy(s, s2);
cout<<str2<<"\n";
cout<<str<<"\n";
system("pause");
}
anyone can help ?

C++ strange function behavior

I've been working in C++ a bit lately and only using a small subset of the language (I'd call it C with classes) so I've been messing around trying to learn about some of the other features of the language. To this end I was going to write a simple JSON parser, and almost immediately hit a road block I cannot decipher. Here's the code:
//json.hpp
#include <cstring>
namespace JSON
{
const char WS[] = {0x20,0x09,0x0A,0x0D};
const char CONTROL[] = {0x5B,0x7B,0x5D,0x7D,0x3A,0x2C};
bool is_whitespace(char c) {
if (strchr(WS, c) != nullptr)
return true;
return false;
}
bool is_control_char(char c) {
if (strchr(CONTROL, c) != nullptr)
return true;
return false;
}
}
And here's main.cpp:
#include <iostream>
#include "json.hpp"
using namespace std;
int main(int argc, char **argv) {
for(int i=0; i < 127; i++) {
if(JSON::is_whitespace((char) i)) {
cout << (char) i << " is whitespace." << endl;
}
if(JSON::is_control_char((char) i)) {
cout << (char) i << " is a control char." << endl;
}
}
return 0;
}
I'm just trying to check if a char is a valid whitespace or a valid control character in JSON.
is whitespace.
is a control char.
is whitespace.
is whitespace.
is whitespace.
is whitespace.
, is whitespace.
, is a control char.
: is whitespace.
: is a control char.
[ is whitespace.
[ is a control char.
] is whitespace.
] is a control char.
{ is whitespace.
{ is a control char.
} is whitespace.
} is a control char.
I've been staring for awhile now. I don't even know what search term to put into Google to describe this error (or feature?)... any explanations would be greatly appreciated.
If you read the requirements on strchr
const char* strchr( const char* str, int ch );
str - pointer to the null-terminated byte string to be analyzed
Whereas you are passing in:
const char WS[] = {0x20,0x09,0x0A,0x0D};
const char CONTROL[] = {0x5B,0x7B,0x5D,0x7D,0x3A,0x2C};
Neither of those is a null-terminated byte string. You could manually add a 0:
const char WS[] = {0x20,0x09,0x0A,0x0D, 0x0};
const char CONTROL[] = {0x5B,0x7B,0x5D,0x7D,0x3A,0x2C, 0x0};
Or, better yet, not actually rely on that behavior:
template <size_t N>
bool contains(const char (&arr)[N], char c) {
return std::find(arr, arr+N, c) != (arr+N);
}
bool is_whitespace(char c) { return contains(WS, c); }
bool is_control_char(char c) { return contains(CONTROL, c); }
In C++11:
template <size_t N>
bool contains(const char (&arr)[N], char c) {
return std::find(std::begin(arr), std::end(arr), c) !=
std::end(arr);
}

EXC_BAD_ACCESS occurred when assign char with a poitner

EXC_BAD_ACCESS occurred in *str++ = *end;. What's wrong with this?
#include <iostream>
using namespace std;
int main() {
char *hello = "abcdefgh";
char c = 'c';
char *str = hello;
//printf("%s",str);
char * end = str;
char tmp;
if (str) {
while (*end) {
++end;
}
--end;
while (str < end) {
tmp = *str;
printf("hello:%s str:%c, end:%c\n", hello, *str, *end);
*str++ = *end;
*end-- = tmp;
}
}
return 0;
}
It is undefined behavior to attempt to alter a string literal. Change your code to the equivalent below, and you will see the issue:
const char *hello = "abcdefgh";
const char *str = hello;
const char * end = str;
So do you see why the line *str++ = *end; had a problem? You're attempting to write to a const area, and you can't do that.
If you want an even simpler example:
int main()
{
char *str = "abc";
str[0] = 'x';
}
Don't be surprised if this simple program produces a crash or segmentation fault when the
str[0] = 'x';
line is executed.
Unfortunately, string-literals did not have to be declared as const char* in C, and C++ brought this syntax over. So even though it looks like you are not using const, you are.
If you want the code to actually work, declare a writeable buffer, i.e. an array of char:
char hello[] = "abcdefgh";
char str[100];
strcpy(str, hello);
char end[100];
strcpy(end, str);
It seems like you're trying to reverse the string. It also looks like you're overcomplicating things.
C-style strings declared on the stack have to be declared as const char *, which means you can't change the characters as they are constant.
In C++ we use strings, and string iterators:
#include <iostream>
#include <string>
using std::string;
using std::reverse;
using std::swap;
using std::cout;
using std::endl;
int main(int argc, const char * argv[])
{
string hello("abcdefgh");
reverse(hello.begin(), hello.end());
cout << hello << endl;
return 0;
}
Manually:
static void reverse(std::string & str)
{
string::size_type b = 0, e = str.length() - 1, c = e / 2;
while(b <= c)
{
swap(str[b++], str[e--]);
}
}
Recursively:
static void reverse_helper(std::string & str,
string::size_type b,
string::size_type e)
{
if(b >= e)
return;
swap(str[b], str[e]);
reverse_helper(str, ++b, --e);
}
static void reverse(std::string & str)
{
reverse_helper(str, 0, str.length() - 1);
}