Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Is there an easy way to count the number of occurrences of wildcards in a QString object?

Is there an easy way to count the number of occurrences of wildcards in a QString object?

Scheduled Pinned Locked Moved Solved General and Desktop
15 Posts 3 Posters 1.8k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • R Offline
    R Offline
    Robert Hairgrove
    wrote on last edited by
    #1

    Actually, I would like to know the DISTINCT count of wildcards (%1, %2, etc.) in a string passed as a parameter to a function which does the replacements.

    I couldn't find anything in the API documentation for QString for doing this. I could write it myself using the QString::count() function repeatedly, but it seems like something that should be built into the API.

    Christian EhrlicherC JonBJ 2 Replies Last reply
    0
    • R Robert Hairgrove

      Actually, I would like to know the DISTINCT count of wildcards (%1, %2, etc.) in a string passed as a parameter to a function which does the replacements.

      I couldn't find anything in the API documentation for QString for doing this. I could write it myself using the QString::count() function repeatedly, but it seems like something that should be built into the API.

      Christian EhrlicherC Offline
      Christian EhrlicherC Offline
      Christian Ehrlicher
      Lifetime Qt Champion
      wrote on last edited by
      #2

      @Robert-Hairgrove said in Is there an easy way to count the number of occurrences of wildcards in a QString object?:

      but it seems like something that should be built into the API.

      Why? Where should this be needed?

      Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
      Visit the Qt Academy at https://academy.qt.io/catalog

      1 Reply Last reply
      1
      • R Robert Hairgrove

        Actually, I would like to know the DISTINCT count of wildcards (%1, %2, etc.) in a string passed as a parameter to a function which does the replacements.

        I couldn't find anything in the API documentation for QString for doing this. I could write it myself using the QString::count() function repeatedly, but it seems like something that should be built into the API.

        JonBJ Online
        JonBJ Online
        JonB
        wrote on last edited by
        #3

        @Robert-Hairgrove said in Is there an easy way to count the number of occurrences of wildcards in a QString object?:

        DISTINCT count of wildcards (%1, %2, etc.)

        Since you pointedly say DISTINCT (I presume your string can contain e.g. %2 multiple times) you cannot just search for %s. You will want to indexOf() the string to find each % and act on the next character. I wouldn't call QString::count() 9 times if it were me, but up to you.

        1 Reply Last reply
        0
        • R Offline
          R Offline
          Robert Hairgrove
          wrote on last edited by
          #4

          @Christian-Ehrlicher and @JonB : Thank you for the replies. I assume this means there is no built-in function, so I will try using a regular expression and count the matches.

          So far, this seems to work well enough as a pattern to capture any combination of 1 or 2 digits preceded by a single % character. If there are more than three digits following the token, only the first two are matched (QString::arg() can handle between 1 and 99 distinct wildcards according to the documentation, but I will certainly not need that many):

          (^[^%]?%\d{1,2})|([^%]%\d{1,2})
          

          Of course, the backslashes will need to be escaped, but it seems to work OK when tested on the site https://regex101.com. I can eliminate duplicates when I iterate over the matches.

          Can anyone come up with an example where this might break?

          JonBJ 1 Reply Last reply
          0
          • R Robert Hairgrove

            @Christian-Ehrlicher and @JonB : Thank you for the replies. I assume this means there is no built-in function, so I will try using a regular expression and count the matches.

            So far, this seems to work well enough as a pattern to capture any combination of 1 or 2 digits preceded by a single % character. If there are more than three digits following the token, only the first two are matched (QString::arg() can handle between 1 and 99 distinct wildcards according to the documentation, but I will certainly not need that many):

            (^[^%]?%\d{1,2})|([^%]%\d{1,2})
            

            Of course, the backslashes will need to be escaped, but it seems to work OK when tested on the site https://regex101.com. I can eliminate duplicates when I iterate over the matches.

            Can anyone come up with an example where this might break?

            JonBJ Online
            JonBJ Online
            JonB
            wrote on last edited by
            #5

            @Robert-Hairgrove
            Personally like I wrote I think I would scan with indexOf(), seems simpler to me than reg ex. May be personal preference!

            I think yours will go wrong on %%%1 etc.? Which is why I think it's trickier with a reg ex than your own code?

            R 1 Reply Last reply
            0
            • JonBJ JonB

              @Robert-Hairgrove
              Personally like I wrote I think I would scan with indexOf(), seems simpler to me than reg ex. May be personal preference!

              I think yours will go wrong on %%%1 etc.? Which is why I think it's trickier with a reg ex than your own code?

              R Offline
              R Offline
              Robert Hairgrove
              wrote on last edited by
              #6

              @JonB No, I tried this on the regex101 site. It was indeed a little tricky to get that part right, but here is the breakdown of the pattern:

              1st capturing group: Looks at the beginning of the string for any character not equal to %, or none, followed by exactly one % followed by 1 or 2 digits;

              2nd (alternate) capture group: Looks for a sequence which MUST begin with a non-% character followed by exactly one % followed by 1 or 2 digits.

              If the string begins with "%1" it will match the first group, but "%%1" does not match anywhere. Anything past the beginning of the string must match the second capture group, and this requires a % preceded by something else and followed by 1 or 2 digits, whereas the non-% character at the beginning of the string is optional (due to the "?" token).

              JonBJ 1 Reply Last reply
              0
              • R Robert Hairgrove

                @JonB No, I tried this on the regex101 site. It was indeed a little tricky to get that part right, but here is the breakdown of the pattern:

                1st capturing group: Looks at the beginning of the string for any character not equal to %, or none, followed by exactly one % followed by 1 or 2 digits;

                2nd (alternate) capture group: Looks for a sequence which MUST begin with a non-% character followed by exactly one % followed by 1 or 2 digits.

                If the string begins with "%1" it will match the first group, but "%%1" does not match anywhere. Anything past the beginning of the string must match the second capture group, and this requires a % preceded by something else and followed by 1 or 2 digits, whereas the non-% character at the beginning of the string is optional (due to the "?" token).

                JonBJ Online
                JonBJ Online
                JonB
                wrote on last edited by JonB
                #7

                @Robert-Hairgrove
                I don't understand what you are saying. I suggested what will not work:

                %%%1
                

                somewhere/anywhere (beginning/middle/end). That is 3 percent characters. It expands to a literal % followed by your %1 substitution. In my head your reg ex will fail to recognise the %1 because it will see it as a %%1 and so reject the match. Effectively you do not deal with %%s correctly.

                R 1 Reply Last reply
                0
                • JonBJ JonB

                  @Robert-Hairgrove
                  I don't understand what you are saying. I suggested what will not work:

                  %%%1
                  

                  somewhere/anywhere (beginning/middle/end). That is 3 percent characters. It expands to a literal % followed by your %1 substitution. In my head your reg ex will fail to recognise the %1 because it will see it as a %%1 and so reject the match. Effectively you do not deal with %%s correctly.

                  R Offline
                  R Offline
                  Robert Hairgrove
                  wrote on last edited by Robert Hairgrove
                  #8

                  @JonB Hmmm ... I think you might be right about this. I was probably confusing the SQL LIKE syntax where "%" is also used as a wildcard, and to search for a literal "%" string, one must write it twice, i.e.: WHERE something LIKE '%%1' would match the string '%1' and WHERE something LIKE '%1' would match 'a1', 'b1' etc. So I was deliberately trying NOT to match the wildcard if it had two or more % tokens.

                  In that case, the pattern would be much simpler: "%\d{1,2}" which would match anywhere in the string.

                  JonBJ 1 Reply Last reply
                  0
                  • R Robert Hairgrove

                    @JonB Hmmm ... I think you might be right about this. I was probably confusing the SQL LIKE syntax where "%" is also used as a wildcard, and to search for a literal "%" string, one must write it twice, i.e.: WHERE something LIKE '%%1' would match the string '%1' and WHERE something LIKE '%1' would match 'a1', 'b1' etc. So I was deliberately trying NOT to match the wildcard if it had two or more % tokens.

                    In that case, the pattern would be much simpler: "%\d{1,2}" which would match anywhere in the string.

                    JonBJ Online
                    JonBJ Online
                    JonB
                    wrote on last edited by JonB
                    #9

                    @Robert-Hairgrove
                    Sorry, but again I think you are misunderstanding.

                    (Oh, btw, it may not be documented, but I believe Qt does do %% => % for arguments, and I have used that.)

                    If you went for %\d{1,2} that would indeed incorrectly cause %%1 to be treated as %1 substitution. You are correct that you do need to deal with %1 versus %%1, as you tried earlier. But I believe you will go wrong on %%%1, you will see it as %%1 and thereby not thing it is a %1 but it is!

                    Try abc %1 %%2 %%%3 %%%%4 def with whatever reg ex you choose. I have said I would probably not use reg exs here for this reason, I would just do it via indexOf('%', ...) and deal with the following characters (%, digits, something else) myself. But if you want to use reg ex here you'll have to deal with this issue!

                    R 1 Reply Last reply
                    0
                    • Christian EhrlicherC Offline
                      Christian EhrlicherC Offline
                      Christian Ehrlicher
                      Lifetime Qt Champion
                      wrote on last edited by
                      #10

                      I still don't understand the use-case for this though...
                      If you want to see what Qt is doing search for findArgEscapes() in qstring.cpp

                      Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
                      Visit the Qt Academy at https://academy.qt.io/catalog

                      R 1 Reply Last reply
                      0
                      • Christian EhrlicherC Christian Ehrlicher

                        I still don't understand the use-case for this though...
                        If you want to see what Qt is doing search for findArgEscapes() in qstring.cpp

                        R Offline
                        R Offline
                        Robert Hairgrove
                        wrote on last edited by
                        #11

                        @Christian-Ehrlicher The use case would be like this: A function receives a pattern (a string with some wildcards) from some other place and needs to fill in the wildcards within the function, returning the completed string. My function would like to assert that the input has the expected number of wildcards before trying to do replacements.

                        1 Reply Last reply
                        0
                        • JonBJ JonB

                          @Robert-Hairgrove
                          Sorry, but again I think you are misunderstanding.

                          (Oh, btw, it may not be documented, but I believe Qt does do %% => % for arguments, and I have used that.)

                          If you went for %\d{1,2} that would indeed incorrectly cause %%1 to be treated as %1 substitution. You are correct that you do need to deal with %1 versus %%1, as you tried earlier. But I believe you will go wrong on %%%1, you will see it as %%1 and thereby not thing it is a %1 but it is!

                          Try abc %1 %%2 %%%3 %%%%4 def with whatever reg ex you choose. I have said I would probably not use reg exs here for this reason, I would just do it via indexOf('%', ...) and deal with the following characters (%, digits, something else) myself. But if you want to use reg ex here you'll have to deal with this issue!

                          R Offline
                          R Offline
                          Robert Hairgrove
                          wrote on last edited by Robert Hairgrove
                          #12

                          @JonB I looked at the source code, as @Christian-Ehrlicher suggested, and it appears that QString("%%%1").arg("xyz") would result in the string "%%xyz", although I didn't try it yet.

                          JonBJ 1 Reply Last reply
                          0
                          • R Robert Hairgrove

                            @JonB I looked at the source code, as @Christian-Ehrlicher suggested, and it appears that QString("%%%1").arg("xyz") would result in the string "%%xyz", although I didn't try it yet.

                            JonBJ Online
                            JonBJ Online
                            JonB
                            wrote on last edited by JonB
                            #13

                            @Robert-Hairgrove

                            QString("%%%1").arg("xyz") would result in the string "%%xyz"

                            I would expect it to produce %xyz. But in any case the issue is I expect your code to miss the %1 in this case.

                            R 1 Reply Last reply
                            0
                            • JonBJ JonB

                              @Robert-Hairgrove

                              QString("%%%1").arg("xyz") would result in the string "%%xyz"

                              I would expect it to produce %xyz. But in any case the issue is I expect your code to miss the %1 in this case.

                              R Offline
                              R Offline
                              Robert Hairgrove
                              wrote on last edited by Robert Hairgrove
                              #14

                              @JonB Finally got around to setting up a little test app (with Qt 5.15.5 on Linux Ubuntu 18.04).
                              This is what it prints:

                              Original:  	Testing %%%1
                              Result:  	Testing %%xyz
                              

                              main.cpp

                              #include <QString>
                              #include <string>
                              #include <iostream>
                              
                              const QString s("Testing %%%1");
                              
                              int main()
                              {
                                const QString rep("xyz");
                                const QString res = s.arg(rep);
                                std::cout
                                    << "Original:  \t" << s.toStdString()
                                    << "\nResult:  \t" << res.toStdString()
                                    << std::endl;
                                return 0;
                              }
                              

                              .pro file:

                              QT -= gui
                              
                              CONFIG += c++11 console
                              CONFIG -= app_bundle
                              
                              # You can make your code fail to compile if it uses deprecated APIs.
                              # In order to do so, uncomment the following line.
                              #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
                              
                              SOURCES += \
                                      main.cpp
                              
                              # Default rules for deployment.
                              qnx: target.path = /tmp/$${TARGET}/bin
                              else: unix:!android: target.path = /opt/$${TARGET}/bin
                              !isEmpty(target.path): INSTALLS += target
                              
                              1 Reply Last reply
                              0
                              • R Offline
                                R Offline
                                Robert Hairgrove
                                wrote on last edited by
                                #15

                                Changed the regexp now to this:

                                %[1-9][0-9]?
                                

                                Just in case someone writes leading zeroes, i.e. "ABC %01 XYZ" instead of "ABC %1 XYZ". The question mark after the second brackets group is necessary for matching either single digits or two-digit numbers.

                                1 Reply Last reply
                                0

                                • Login

                                • Login or register to search.
                                • First post
                                  Last post
                                0
                                • Categories
                                • Recent
                                • Tags
                                • Popular
                                • Users
                                • Groups
                                • Search
                                • Get Qt Extensions
                                • Unsolved