-1

Does substr change the position where the find function starts searching ?

I have a char * named search_text containing the following text:

ABC_NAME = 'XYZSomeone' AND ABC_CLASS = 'XYZSomething'

I want to display the "ABC_NAME" value from that string.

Here is what I am doing:

std::cout << std::string(search_text).substr ( 12, std::string( search_text ).find ("'", 13 )-1) << std::endl;

My logic in the above in the substr is as follows:

  1. The ABC_NAME value always begins at the 12th character, so start the substring there.
  2. Do a find for the character ' (single quotation mark) from the 13th character onwards, starting from the 13th character (the second argument of the find() function). The resulting number will be the outer bound of the substr.

However, my code prints out the following:

XYZSomeone' AND ABC_C

However, when I try to display the value of the find() function directly, I do get the correct number for the location of the second ' (single quotation mark)

std::cout << std::string( search_text ).find ("'", 13 ) << std::endl;

This prints out:

22

So why is it that the substr is not finding the value of 22 as its second argument ?

didjek
  • 393
  • 5
  • 16
  • You might find it easier to work through this by breaking this into separate statements and store the intermediate results into new variables. – crashmstr Jun 07 '19 at 13:02
  • 1
    "_Do a find for the character ' <...> The resulting number will be the outer bound of the substr._" No, it wouldn't. Second argument of [`std::substr`](https://en.cppreference.com/w/cpp/string/basic_string/substr) is the length of substring, not the outer bound. – Algirdas Preidžius Jun 07 '19 at 13:04
  • You are right. I was assuming the second parameter was the outer bound. If it is the length, then that explains what is happening. Thanks. If you want to put your response in the "Answer" section, go ahead, otherwise I will do it. – didjek Jun 07 '19 at 13:12
  • Possible duplicate of [substr() method in C++](https://stackoverflow.com/questions/31465277/substr-method-in-c) – Thomas Sablik Jun 07 '19 at 13:47

1 Answers1

0

It's a rather simple matter to evaluate your expression by hand, seeing how you already verified the result of find:

std::string(search_text).substr ( 12, std::string( search_text ).find ("'", 13 )-1)
std::string("ABC_NAME = 'XYZSomeone' AND ABC_CLASS = 'XYZSomething'").substr ( 12, 22-1)

Now check the documentation for substr: "Returns a substring [pos, pos+count)". The character at position 12 is the 'X' for the name portion, and the character at position 12+21 = 33 is the 'L' from the class portion. So we expect the substring starting at that 'X' and going up to just before that 'L', which is "XYZSomeone' AND ABC_C". Check.

(It is understandable to forget whether substr takes a length or a position at which to end. Different languages do disagree on this. Hence the link to the documentation.)

Unsolicited commentary

Trying to do so much in one line makes your code harder to read and harder to debug. In this case, it also hurts performance. There is no need to convert search_text to a std::string twice.

std::string search_string{search_text};
std::size_t found = search_string.find('\'', 12);
if ( found != std::string::npos )
    found -= 12;
std::cout << search_string.substr(12, found) << std::endl;

This cuts the number of times a string is constructed (hence the times the string data is copied) from three to two.

If you are using C++17, you can improve the performance even more by constructing no strings. Just use std::string_view instead of std::string. For this scenario, it has the same member functions taking the same parameters; all you have to change is the type of search_string. This puts the performance on par with C code.

Even better: since string views are so cheap to create, you could even write your code – without a performance hit – so that it doesn't matter whether substr takes a length or takes the past-the-end position.

std::string_view search_string{search_text};
std::string_view ltrimmed = search_string.substr(12);
std::size_t found = ltrimmed.find('\'');
std::cout << ltrimmed.substr(0, found) << std::endl;

Constructive laziness FTW!

JaMiT
  • 14,422
  • 4
  • 15
  • 31