0

I have an application where grammar school teachers can place an answer box on a page after a question. The answer box is configured by the teacher with an answer line that specifies acceptable answers to the question. I don't expect them to give me a valid regular expression for the answer so I let them write the answer in a simplified form, where a '*' represents 0 or more of anything and ',' separates multiple acceptable answers. So an answer line that contained

                          *cup,glass

would accept 'teacup' , 'coffee cup' , 'cup' or 'glass' but not 'cup holder'.

Is there a way I can map the answer line they provide to a single regex that I can compare the student's answer with to give me a true or false answer, i.e., it's an acceptable answer to the question, or it isn't?

Thanks

Steve
  • 4,534
  • 9
  • 52
  • 110

3 Answers3

2

The language isn't specified in the question as I write this - the exact form of the answer will depend heavily on that. Let's assume JavaScript, as most of the poster's tags seem JavaScript-related.

function toRegexp(e) {
    return new RegExp(
        "^(?:"+
            e.split(/,/).map(
                function(x){ 
                    return x.replace(/([.?+^$[\]\\(){}|-])/g, "\\$1").replace(/\*/g,".*");
                }
            ).join("|")+
        ")$", "i");
}

(With thanks to this answer for the bit that escapes the special characters.)

Community
  • 1
  • 1
pobrelkey
  • 5,853
  • 20
  • 29
  • very nice indeed, suggest adding start and end of line anchors though so that the match (`*cup`) fails entirely for `Preoccupied` instead of matching `Preoccup` - bah, edited before I even commented – OGHaza Nov 14 '13 at 11:37
  • Could someone explain the ?: above? Thanks – Steve Nov 15 '13 at 00:36
  • `(?: )` is a [non-capturing group](http://stackoverflow.com/questions/3512471/non-capturing-group) - basically a way to wrap part of a regexp in parentheses without those parentheses forming a _capturing group_ which would populate `RegExp.$1` etc. – pobrelkey Nov 15 '13 at 00:40
1

I'm not sure what language you are doing this in but given an input string e.g.*cup,glass

  • add ^( to the start
  • add )$ to the end
  • replace all * with .*
  • replace all , with |

Giving ^(.*cup|glass)$.

All of those steps should be pretty trivial in any language.

OGHaza
  • 4,795
  • 7
  • 23
  • 29
0

Thanks for all your input. The solution I arrived at is http://jsfiddle.net/76zXf/12/. This seems to do everything I asked for, plus allow any number of spaces before and after the correct answer, and allow "+" and "-" in the answer. (So an Answer Line of "3,+3,-3" works fine for the question "What is the square root of 9?") I was able to do it with a simpler build function than proprelkey posted:

 $('#answerLine').change(function() {
     s = this.value.replace(/\*/,'.*'); // change all "*" for ".*"
     s = s.replace(/\+/,'\\+'); // change all "+" for "\+*"
     s = s.replace(/\-/,'\\-'); // change all "-" for "\-*"
     a1 = s.split(/,/); // get individual terms into an array
     a2 = a1.map(  // for each  term . . .
                  function(x){ 
                     exp = '^\\s*' + x + '\\s*$'; // build complete reg expression
                     return( exp );     // return this expression to array
                   }
                );
      re = RegExp( a2.join("|"),'i');    // our final, complete regExp

I hope I'm not missing any important cases. Thanks again.

Steve
  • 4,534
  • 9
  • 52
  • 110
  • Your function doesn't escape special characters other than `+` - so any answer involving decimal points or dollar values may pose a problem. – pobrelkey Nov 15 '13 at 15:15