【问题标题】:Use just regexp to split a string into a 'tuple' of filename and extension?仅使用正则表达式将字符串拆分为文件名和扩展名的“元组”?
【发布时间】:2022-01-02 23:49:30
【问题描述】:

我知道get file extensions with JavaScript 有更简单的方法,但部分是为了练习我的正则表达式技能,我想尝试使用正则表达式将文件名分成两个字符串,在最后一个点之前和之后(. 字符) .

这是我目前所拥有的

const myRegex = /^((?:[^.]+(?:\.)*)+?)(\w+)?$/
const [filename1, extension1] = 'foo.baz.bing.bong'.match(myRegex);
// filename1 = 'foo.baz.bing.'
// extension1 = 'bong'
const [filename, extension] = 'one.two'.match(myRegex);
// filename2 = 'one.'
// extension2 = 'two'
const [filename, extension] = 'noextension'.match(myRegex);
// filename2 = 'noextension'
// extension2 = ''

我尝试使用否定前瞻来表示“仅匹配文字”。如果后面跟着一个以这样结尾的单词,则将(?:\.)* 更改为(?:\.(?=\w+.))*

/^((?:[^.]+(?:\.(?=(\w+\.))))*)(\w+)$/gm

但我想仅使用正则表达式来排除最后一个时期,并且最好在初始组中匹配“noextension”,我该如何仅使用正则表达式来做到这一点?

这是我的正则表达式临时文件:https://regex101.com/r/RTPRNU/1

【问题讨论】:

标签: javascript regex regex-lookarounds capture-group


【解决方案1】:

对于第一个捕获组,您可以从 1 个或多个单词字符开始匹配。然后选择性地重复 . 和 1 个或多个单词字符。

然后您可以使用与. 匹配的可选非捕获组,并在组 2 中捕获 1 个或多个单词字符。

由于第二个非捕获组是可选的,所以第一个重复应该是贪婪的。

^(\w+(?:\.\w+)*?)(?:\.(\w+))?$

模式匹配

  • ^ 字符串开始
  • ( 捕获第 1 组
    • \w+(?:\.\w+)*? 匹配 1+ 个单词字符,并可选择重复 . 和 1+ 个单词字符
  • )关闭第一组
  • (?:非捕获组整体匹配
    • \.(\w+) 匹配 . 并在捕获 group 2 中捕获 1+ 个单词字符
  • )?关闭非捕获组并使其可选
  • $字符串结束

Regex demo

const regex = /^(\w+(?:\.\w+)*?)(?:\.(\w+))?$/;
[
  "foo.baz.bing.bong",
  "one.two",
  "noextension"
].forEach(s => {
  const m = s.match(regex);
  if (m) {
    console.log(m[1]);
    console.log(m[2]);
    console.log("----");
  }
});

在 cmets 中发布的另一个选项 @Wiktor Stribiżew 是使用非贪心点来匹配文件名的任何字符:

^(.*?)(?:\.(\w+))?$

Regex demo

【讨论】:

    【解决方案2】:

    如果你真的想使用正则表达式,我建议使用两个正则表达式:

    // example with 'foo.baz.bing.bong'
    
    const firstString = /^.+(?=\.\w+)./g // match 'foo.baz.bing.' 
    const secondString = /\w+$/g   // match 'bong'

    【讨论】:

    • 你能做一个完整的 sn-p 来测试你的正则表达式吗?
    【解决方案3】:

    不环顾四周,更明确和准确的东西怎么样......

    1. 命名组变体.../^(?<noextension>\w+)$|(?<filename>\w+(?:\.\w+)*)\.(?<extension>\w+)$/

    2. 没有命名组.../^(\w+)$|(\w+(?:\.\w+)*)\.(\w+)$/

    刚刚显示的两个变体都可以缩短为 2 个捕获组,而不是上述变体的 3 个捕获组,在我看来,这使得正则表达式更易于使用,但可读性较差...

    1. 命名组变体.../(?<filename>\w+(?:\.\w+)*?)(?:\.(?<extension>\w+))?$/

    2. 没有命名组.../(\w+(?:\.\w+)*?)(?:\.(\w+))?$/

    const testData = [
      'foo.baz.bing.bong',
      'one.two',
      'noextension',
    ];
    // https://regex101.com/r/RTPRNU/5
    const regXTwoNamedFileNameCaptures = /(?<filename>\w+(?:\.\w+)*?)(?:\.(?<extension>\w+))?$/;
    // https://regex101.com/r/RTPRNU/4
    const regXTwoFileNameCaptures = /(\w+(?:\.\w+)*?)(?:\.(\w+))?$/;
    
    // https://regex101.com/r/RTPRNU/3
    const regXThreeNamedFileNameCaptures = /^(?<noextension>\w+)$|(?<filename>\w+(?:\.\w+)*)\.(?<extension>\w+)$/
    // https://regex101.com/r/RTPRNU/3
    const regXThreeFileNameCaptures = /^(\w+)$|(\w+(?:\.\w+)*)\.(\w+)$/
    
    console.log(
      'based on 2 named file name captures ...\n',
      testData, ' =>',
      testData.map(str =>
        regXTwoNamedFileNameCaptures.exec(str)?.groups ?? {}
      )
    );
    console.log(
      'based on 2 unnamed file name captures ...\n',
      testData, ' =>',
      testData.map(str => {
        const [
          match,
          filename,
          extension,
        ] = str.match(regXTwoFileNameCaptures) ?? [];
      //] = regXTwoFileNameCaptures.exec(str) ?? [];
    
        return {
          filename,
          extension,
        }
      })
    );
    
    console.log(
      'based on 3 named file name captures ...\n',
      testData, ' =>',
      testData.map(str => {
        const {
          filename = '',
          extension = '',
          noextension = '',
        } = regXThreeNamedFileNameCaptures.exec(str)?.groups ?? {};
    
        return {
          filename: filename || noextension,
          extension,
        }
      })
    );
    console.log(
      'based on 3 unnamed file name captures ...\n',
      testData, ' =>',
      testData.map(str => {
        const [
          match,
          noextension = '',
          filename = '',
          extension = '',
        ] = str.match(regXThreeFileNameCaptures) ?? [];
      //] = regXThreeFileNameCaptures.exec(str) ?? [];
    
        return {
          filename: filename || noextension,
          extension,
        }
      })
    );
    .as-console-wrapper { min-height: 100%!important; top: 0; }

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-05
      • 1970-01-01
      • 2010-09-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多