0

I tried making a little program that checks if a string is a substring of another. I think got the logic down correctly, but i encountered a problem concerning data types. I declared 2 strings and gave them a value instantly and they worked fine, but the other one, the checker variable, works in a weird way. It can not be printed out like a regular string using cout but it only prints the first letter. And functions like .length() do not work on it, the error says

|error: request for member 'length' in 'checker', which is of non-class type 'std::__cxx11::string

Code:

#include <iostream>
#include <string.h>
#include <string>
using namespace std;

int main()
{
    string text = "abcde";
    string subtext = "bcde";
    //string specialChars = "\\*";
    string checker[subtext.length()];
    int counter = 0;

    for(int i = 0; i < 5; i++){
        if (subtext[counter] == text[i]){
            checker[counter] = text[i];
            counter++;
        }

    }
    cout << text << " " << checker.length() << endl;
    for (int i = 0; i < 4; i++){
        cout << checker[i];
    }
    return 0;
}

Bonus question ^_^: Is the logic behind the program good enough, or is there a more efficient way of solving it.

thanks a lot.

TommyGun
  • 25
  • 4
  • Tip: `using namespace std;` is a bad habit to get into and if you can stop now you might avoid a whole lot of headaches in the future. The `std::` prefix is there for a reason: It avoids conflict with your own classes, structures and variables especially when you’re importing a lot of header files which may define a lot more things than they superficially appear to. – tadman Oct 27 '20 at 18:51
  • 1
    Note that [variable-length arrays](https://en.wikipedia.org/wiki/Variable-length_array) are not part of standard C++. Please try to avoid it in favor of `std::vector` (which will as noted in an answer also solve your problem). – Some programmer dude Oct 27 '20 at 19:17

2 Answers2

1

checker is not a std::string, it's an array of std::string, and as a primitive type, does not have a .length() function.1

If you want that, you'll need to use this instead:

std::vector<std::string> checker;

Where then you can add as many as you see fit:

checker.push_back(text[i]);

And later:

cout << text << " " << checker.size() << endl;

1 Not everything in the C++ Standard Library has a length() function anyway, it's conventionally called size(). std::string just has an alias, but it's an exception.

tadman
  • 208,517
  • 23
  • 234
  • 262
  • 2
    My suspicion is the asker really wants `string checker(' ', subtext.length());` and this is just a typo (and errors descending from the typo while trying to make the code compile). – user4581301 Oct 27 '20 at 18:54
  • @user4581301 A very good point. Worth adding as a separate answer? – tadman Oct 27 '20 at 18:55
  • 1
    I was commenting and about to close as typo when I saw your answer pop up and decided to stop and wait for more input. – user4581301 Oct 27 '20 at 18:59
  • The expression `string checker[subtext.length()];` is not valid C++, as VLA are supported in C99 and sometimes as an extension in C++. Visual Studio doesn't compile it. – prapin Oct 27 '20 at 18:59
  • I know std stands for standard library, but what is std::vector. And what are the <> signs used for in this context. I am willing to read abt it so i fully understand it. – TommyGun Oct 27 '20 at 18:59
  • 1
    If you're learning C++ and don't know that yet, time to get a reference which covers [templates](https://en.cppreference.com/w/cpp/language/templates). They're extremely important to know, and if you don't, then 90% of the Standard Library will be off-limits and useless, which is a huge impediment. A template is a C++ "generic" of sorts. C++ is not something you can intuit your way through, so a good reference book is absolutely critical to understanding it. – tadman Oct 27 '20 at 19:01
  • I dropped a bug above. `string checker(' ', subtext.length());` should be `string checker(subtext.length(), ' ');` – user4581301 Oct 27 '20 at 19:02
  • So, if i'm not mistaken, the string class takes 2 arguments, size and content. But why did the first 2 string variables work fine and not the checker variable? – TommyGun Oct 27 '20 at 19:06
  • 1
    In C++ `X y[n]` creates a C array of type `X` with `n` entries. That `[n]` part doesn't even hit the constructor, it means *create `n` of*. You want `X y(...)` where the `...` part *is* passed to the constructor. – tadman Oct 27 '20 at 19:08
  • So, I must dig into templates and the ::std prefix for the next few weeks i guess. – TommyGun Oct 27 '20 at 19:14
  • The `std::` prefix isn't the hard part, it just means "don't slap `using namespace std` on your code and instead embrace prefixing with `std::`" so that's easy. The harder part is understanding how templates work. Getting up to speed on something simple like `std::vector` doesn't take long, that template is very forgiving, but writing your own templates can be challenging, so don't be discouraged. – tadman Oct 27 '20 at 19:17
  • What are some sources where i can learn about templates. Are youtube videos enough? If not, what is the best place to learn about them in your opinion? – TommyGun Oct 27 '20 at 19:20
  • YouTube videos might work for a "quick introduction" but they're no substitute for a solid reference. I found [*The C++ Programming Language*](https://www.stroustrup.com/4th.html) to be very thorough, it's written by the originator of C++, and was essential for my understanding of C++. There's also a [large book list](https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list) here worth checking out. – tadman Oct 27 '20 at 19:23
0
string checker[subtext.length()]; 

is an array of subtext.length() empty std::strings. Not what you want to have if you indeed want a string of the shared character. Which you don't. More on that after the post-mortem.

string checker(subtext.length(), ' '); 

Will give you a single std::string of the requested length using spaces as place-holder characters. This is also the wrong thing to do because now you have to deal with left-over unused spaces still containing spaces.

What you need in order to correct the code you currently have is a single empty std::string and a change to the loop that builds it.

#include <iostream>
#include <string>

int main()
{
    std::string text = "abcde";
    std::string subtext = "bcde";
    std::string checker;

    int counter = 0;

    for(std::string::size_type i = 0; // unlikely to happen, but int may not be
                                      // big enough. size_type is guaranteed
        i < text.length(); // avoid magic numbers. Use the length of the string
        i++){
        if (subtext[counter] == text[i]){
            checker.push_back(text[i]); //add to checker
            counter++;
        }

    }
    std::cout << text << " " << checker.length() << std::endl
              << checker << std::endl;
    return 0;
}

That's it for the post-mortem. The code works as I believe the asker intends. Unfortunately it doesn't detect substrings. For a string to be a substring not only do all of the letters have to match and be in the same order, they also need to be contiguous. The way it is built, checker can't guarantee this.

The easiest way to find a substring is

#include <iostream>
#include <string>

int main()
{
    std::string text = "abcde";
    std::string subtext = "bcde";

    if (text.find(subtext) != std::string::npos)
    {
        std::cout << "We Got One!";
    }
    return 0;
}

find looks for the exact subtext inside text and returns the location of subtext inside text. If subtext is not found, find returns an impossible index, npos.

If you have to search the hard way, as in the assignment won't all of you to call a library function to do the dirty work, there are many approaches with Knuth-Morris-Pratt being relatively easy to implement, pretty darn efficient, and extremely well documented.

user4581301
  • 33,082
  • 7
  • 33
  • 54