正则表达式匹配一组中的 1 个或两个字符,并确保它们不同

regex to match 1 or two characters within a set, and ensure they're different

好的,这是我的难题:

我正在寻找使用 vanilla JavaScript 的 test()RegExp 原型的函数)来匹配字符串,以测试输入变量 inp

/{CONDITION}/.test(inp)

字符串必须满足以下条件:

示例:

't'   // Good
'B'   // Good
'Rc'  // Good
'bl'  // Good
'tb'  // bad
'mm'  // Bad
'cC'  // Bad
'BB'  // Bad
'Bob' // Bad
'5'   // Bad
'Ċ'   // Still Bad
'ß'   // Suspiciously Bad
''  // Now you're just screwing with me
'上'  // You know what? I don't care if this fails gracefully or not. ^&%* you.

我这里的objective是解析用户输入,表示垂直和水平位置('T'/'M'/'B'代表'Top'/'Middle'/'Bottom''L'/'C'/'R'代表'Left'/'Center'/'Right' , 分别)。应该允许用户在任何情况下以任何顺序传递两个分组的任何排列(或者只是一个,在这种情况下另一个被推断为默认值)。

我并不专注于使用正则表达式,但做类似(或同样迟钝)的事情似乎很笨拙:

  let errd  = false;
      set1  = 'TMB',
      set2  = 'LCR',
      sets  = set1 + set2;
  if(inp.length === 1 && sets.indexOf(inp) === -1) errd = true;
  else if(inp.length === 2){
      let inpArr = inp.split('');
      errd = (set1.indexOf(inpArr[0]) === set1.indexOf(inpArr[1]) === -1 || set2.indexOf(inpArr[0]) === set2.indexOf(inpArr[1]) === -1);
  }else errd = true;

所以我的问题是:真的没有比简单地吐出所需结果的每个排列更优雅的方式来处理这个问题了吗?

/^[SINGLE (S)]$|^[CASE A/B]$|^[CASE B/A]$/i

我的意思是,如果有三个,

/^[S]$|^[AB]$|^[AC]$|^[BC]$|^[BA]$|^[CA]$|^[CB]$|^[ABC]$|^[ACB]$|^[BAC]$|^[BCA]$|^[CAB]$|^[CBA]$/i

或者(大神帮帮我),四个有类似限制的角色?我是 RegEx 的新手,我想知道我是否遗漏了这里的核心原则。我有一个可行的解决方案(“/^[S]|[AB]|[BA]$/”版本),但这实际上是正确的吗?

EDIT

Thanks for the stellar, comprehensive answer, Sweeper!

(Here's the working code in context, in case it'll help someone else later):

orient: function(objQS, rPos='TL', offsetWidth=0, offsetHeight=0)  {
    try{

        // objQS accepts a QuerySelector string or an HTMLElement
        let obj = (typeof(objQS) === 'string') ? document.querySelector(objQS) : objQS;
        console.log('obj', obj, obj.getBoundingClientRect())
        if(null == obj || typeof(obj) !== 'object'){ throw('Invalid Target!'); }
        let objBRC = obj.getBoundingClientRect();

        // rPos accepts TL, T/TC, TR, ML, M/C/MC, MR, BL, B/BC, BR (case- and order-insensitive)
        if(!/^(?:[tmbrcl]|[tmb][rcl]|[rcl][tmb])$/i.test(rPos)){ throw('Invalid orientation specified!'); }

        // Accomodate single-character entry of 'm' or 'c', both taken to mean 'mc' ('m'iddle-'c'enter)
        if(/^[mc]$/i.test(rPos)) { rPos = 'mc'; } 

        // Set default orientation to top-left (tl/lt), meaning we have nothing to do for 't'op or 'l'eft
        let osT = objBRC.y + offsetHeight,                       // Note we add the user-set offsets to our bases
            osL = objBRC.x + offsetWidth;                        // so they carry though to the other options.
        if(/m/i.test(rPos))      { osT += (objBRC.height / 2); } // Adjust vertically for 'm'iddle (top + height/2)
        if(/b/i.test(rPos))      { osT += objBRC.height; }       // Adjust vertically for 'b'ottom (top + height)
        if(/c/i.test(rPos))      { osL += (objBRC.width / 2); }  // Adjust horizontally for 'c'enter (left + width/2)
        if(/r/i.test(rPos))      { osL += objBRC.width; }        // Adjust horizontally for 'r'ight (left + width)

        objBRC.offsetTop  = osT;
        objBRC.offsetLeft = osL;
        this.place(osL, osT);
        console.log('return', 'objBRC:', objBRC)
        return objBRC;
    }catch(e){
        console.group('ERROR DETAILS (Error in callout.orient)');
        console.error('Error details:\n  - ', e);
        console.groupEnd();
        return false;
    }
}

您的正则表达式可以大大缩短为:

/^(?:[tmbrcl]|[tmb][rcl]|[rcl][tmb])$/i

我认为这是一个足够好的解决方案。写得很清楚:

Between the start and end of the string, there are three options:

  • one of [tmbrcl]
  • one of [tmb] then one of [rcl]
  • one of [rcl] then one of [tmb]

您实际上并不需要所有这些 {1}

编辑:

我没想到你问的是多套的案例。在那种情况下,我认为你应该采用不同的方法。

一种方法是这样的:

  1. 每组有一个正则表达式:

    var r1 = /[abc]/i // notice the missing ^ and $ anchors
    var r2 = /[def]/i
    var r3 = /[ghi]/i
    
  2. 将它们全部放在一个数组中

    var regexes = [r1, r2, r3]
    
  3. 遍历数组并计算有多少正则表达式匹配字符串

  4. 与字符串匹配的正则表达式的数量应等于字符串的长度。

请注意,这假设您的集合不相交。