[Solved]

Hello! I am currently having quite the problem while developing my software. I cannot seem to find a decent way to find all values that match the delimeters. for example, here is the data in the form of a string:

1
2
3
[Data]Important[/Data]
[Data]Copythis[/Data]
[Bob]Notimportant[/Bob]


This is just an example of the delimiters I would like to use. At first, I tried using regex, but I couldn’t seem to find a way to remove the tags themselves, and to get the values. Also, with regex, I had no way of looping through the results. I have attempted to use string::find, but I couldn’t seem to find a way to make it work. Any help is appreciated. Thanks, Drew.
Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <iostream>
#include <vector>
#include <sstream>
#include <string>
using namespace std;


// ADDS any appropriately delimited strings to an EXISTING collection
// Requires a PERFECT match (including case)
void delimited( string text, string before, string after, vector<string> &collection )
{                                                
   int p = 0, q;

   while ( true )
   {
      p = text.find( before, p );      if ( p == string::npos ) return;
      p += before.size();
      q = text.find( after, p );       if ( q == string::npos ) return;
      collection.push_back( text.substr( p, q - p ) );
   }
}



int main()
{
   stringstream fakeFile( "[Data]Important[/Data][Data]Copy this[/Data]\n"
                          "[Bob]Not important[/Bob]\n"
                          "[Bob]Leave till tomorrow[/Bob][Data]Ignore[/Data]" );

   vector<string> items;
   string line;

   while ( getline( fakeFile, line ) ) delimited( line, "[Data]", "[/Data]", items );

   cout << "Required items are:\n";
   for ( string s : items ) cout << s << '\n';
}


Required items are:
Important
Copy this
Ignore


- Must match the delimiters PRECISELY (including case) - easily fixed if necessary
- More efficient to pass longer strings by constant reference than by value, but trying to keep the argument list simple here.
Last edited on
> At first, I tried using regex, but I couldn’t seem to find a way to remove the tags themselves, and to get the values.

Create two captures; one for the tag and another for the value within the tags.
Use a backreference to match the ending tag.


> Also, with regex, I had no way of looping through the results.

With std::regex_iterator, we can iterate through the individual matches.


For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <iostream>
#include <string>
#include <regex>
#include <iomanip>

int main()
{
    const std::regex tagged_item_re( R"(\s*\[\s*([A-Za-z]\w*)\s*\](.+)\[\s*/\1\s*\]\s*)" ) ;

    {
        // match individual strings exactly, extract tag and associated data

        const std::string test_strings[]
        {
            "[Data]Important[/Data]",
            " [ Data ]Copy this[/Data]",
            "[Bob] Not important [ /Bob ]",
            "[Bob] this won't be matched [/Boo]",
            "[regex][A-Za-z][A-Za-z0-9]*[/regex]"
        };

        for( const auto& str : test_strings )
        {
            std::cout << "\nstring: " << std::quoted(str) << '\n' ;
            std::smatch match ;
            if( std::regex_match( str, match, tagged_item_re ) )
            {
                std::cout << "matched: " << std::quoted(match[1].str())
                          << " == " << std::quoted(match[2].str()) << '\n' ;
            }
            else std::cout << "*** error: not matched ***\n" ;
        }
    }

    std::cout << "\n--------------------------------------\n\n" ;

    {
        // extract all embedded tags and their associated data from a string

        const std::string str = "this is a [Data]string[/Data] containing "
                                "[count ]5[ /count ] [what]tagged items[/what]\n"
                                "embedded within it. [use]std::regex_iterator[/use] "
                                "to [action]extract[/action] the data within the tags.\n" ;

        std::cout << str << '\n' ;

        std::sregex_iterator iter( str.begin(), str.end(), tagged_item_re ), end ;
        for( ; iter != end ; ++iter )
        {
            const auto& match = *iter ;
            std::cout << std::quoted(match[1].str())
                      << " == " << std::quoted(match[2].str()) << '\n' ;
        }
    }
}

http://coliru.stacked-crooked.com/a/f6fcd25362d4752e
http://rextester.com/AVHXS57537
For some strange reason, I can't seem to get the above code to compile. I keep getting that quoted is not a member of namespace std? Also, A little confused about how I can store each result as a seperate string in an array of strings.
std::quoted is a C++14 feature.
What compiler do you use?
Topic archived. No new replies allowed.