【问题标题】:Extracting substring from string based on delimiter基于分隔符从字符串中提取子字符串
【发布时间】:2018-02-05 17:08:58
【问题描述】:

我正在尝试从编码的二维条码中提取数据。提取部分工作正常,我可以在文本输入中获取值。

例如,解码后的字符串是

]d20105000456013482172012001000001/:210000000001

基于以下规则(无法获得正确的表格降价,因此附上图片),我正在尝试从上述字符串中提取子字符串。

我要提取的子串:

05000456013482(在分隔符01之后)

201200(在分隔符 17 之后)

00001(在分隔符 10 之后)

0000000001(在分隔符 21 之后)

P.S - > 原始字符串 (]d2) 中的前 3 个字符始终相同,因为它只是简单地表示解码方法。

现在有些怪癖:

1) 分隔符10 后的字母数不固定。因此,在上面给出的示例中,即使它是 00001,它也可能是 001。同样,分隔符21 之后的字母数量也不是固定的,它可以是不同的长度。

对于不同长度的分隔符,我添加了一个常量/: 来确定通过手持设备扫描后编码何时结束。

现在,我在分隔符 10 之后查找 /: 并提取字符串直到它到达 /: 或 EOL 并找到分隔符 21 并删除字符串直到它到达 /: 或 EOL

2) 分隔符0117 后的字母数量始终是固定的(分别为14 个字母和6 个字母),如表所示。

注意:分隔符的位置可能会改变。换句话说,编码的条形码可以以不同的顺序书写。

]d20105000456013482172012001000001/:210000000001 - 注意:否/: 21组后签名,因为它是EOL

]d2172012001000001/:210000000001/:0105000456013482 - 注意:两者都是 10和 21 组有/. 标志表示我们必须提取直到那个标志

]d21000001/:210000000001/:010500045601348217201200 - 前两个是长度不等,接下来的两个是固定长度。

我不是正则表达式方面的专家,到目前为止,我只尝试使用一些简单的模式,例如 (01)(\d*)(21)(\d*)(10)(\d*)(17)(\d*)$,这在给定的示例中不起作用,因为它会像前 2 个字符一样查找 10 个字符。此外,使用substring(x, x) 方法仅适用于固定长度字符串的情况,前提是我知道我必须提取字符串的哪些索引。

P.S - 感谢 JS 和 jQuery 帮助。

【问题讨论】:

    标签: javascript jquery regex string substring


    【解决方案1】:

    虽然您可以尝试制作一个非常复杂的正则表达式来执行此操作,但逐步解析字符串会更具可读性和可维护性。

    基本步骤是:

    1. 删除解码方法字符 (]d2)。
    2. 从第 1 步的结果中拆分出前两个字符。
    3. 使用它来选择提取数据的方法
    4. 从字符串中删除并保存该数据,转到第 2 步重复直到用完字符串。

    现在,由于您有一个 AI/数据结构表,您可以使用多种方法来提取不同形式的数据

    比如AI: 01, 11, 15, 17都是定长的,你可以用字符串的slice方法加上长度

    str.slice(0,14); //for 01
    str.slice(0,6);  //for 11 15 17
    

    虽然像 AI 21 这样的变量会是这样的

    var fnc1 = "/:";
    var fnc1Index = str.indexOf(fnc1);
    str.slice(0,fnc1Index);
    

    演示

    var dataNames = {
      '01': 'GTIN',
      '10': 'batchNumber',
      '11': 'prodDate',
      '15': 'bestDate',
      '17': 'expireDate',
      '21': 'serialNumber'
    };
    
    var input = document.querySelector("input");
    document.querySelector("button").addEventListener("click",function(){
      var str = input.value;
      console.log( parseGS1(str) );
    });
    
    function parseGS1(str) {
      var fnc1 = "/:";
      var data = {};
      
      //remove ]d2
      str = str.slice(3);
      while (str.length) {
        //get the AI identifier: 01,10,11 etc
        let aiIdent = str.slice(0, 2);
        //get the name we want to use for the data object
        let dataName = dataNames[aiIdent];
        //update the string
        str = str.slice(2);
    
        switch (aiIdent) {
          case "01":
            data[dataName] = str.slice(0, 14);
            str = str.slice(14);
            break;
          case "10":
          case "21":
            let fnc1Index = str.indexOf(fnc1);
            //eol or fnc1 cases
            if(fnc1Index==-1){
              data[dataName] = str.slice(0);
              str = "";
            } else {
              data[dataName] = str.slice(0, fnc1Index);
              str = str.slice(fnc1Index + 2);
            }
            break;
          case "11":
          case "15":
          case "17":
            data[dataName] = str.slice(0, 6);
            str = str.slice(6);
          break;
          default:
            console.log("unexpected ident encountered:",aiIndent);
            return false;
            break;
        }
      }
      return data;
    }
    <input><button>Parse</button>

    【讨论】:

    • 我会检查并报告。
    • 嗨,这个例子工作正常。但是,有没有更简单的方法可以简单地控制台记录各个提取的值。因此,在每个 break 语句之后,我可以获得提取的值。我所追求的是将这些值保存在一个变量中,我可以在我的代码中进一步传递。一个例子小提琴jsfiddle.net/o7uwvrnp/4
    【解决方案2】:

    好的,这是我对此的看法。我创建了一个匹配所有可能模式的正则表达式。这样所有部分都被正确分割,剩下的就是使用前两位数字来了解它的含义。

    ^\]d2(?:((?:10|21)[a-zA-Z0-9]{1,20}(?:\/:|$))|(01[0-9]{14})|((?:11|15|17)[0-9]{6}))*
    

    我建议您将其复制到 regex101.com 以读取完整的描述符并针对不同的可能结果进行测试。

    有3个主要部分:

    ((?:10|21)[a-zA-Z0-9]{1,20}(?:\/:|$))
    

    对从 10 和 21 开始的部分进行测试。它会查找 1 到 20 次之间的字母数字实体。它应该以EOL/: 结尾

    (01[0-9]{14})
    

    查找 GTIN,非常简单。

    ((?:11|15|17)[0-9]{6})
    

    查找 3 个日期字段。

    由于我们希望这 3 个片段可以按任意顺序排列,因此我将它们粘在一起 |暗示 OR 并期望这个大序列重复(* 在末尾表示 0 或更多,我们可以定义确切的最小值和最大值以提高可靠性)

    我不确定这是否适用于所有内容,因为您提供的测试字符串不包含实际值中的标识符...很可能产品的最佳使用日期是在一月份,因此其中会有一个 01价值。但是强制正则表达式以这种方式执行应该可以避免其中的一些问题。

    编辑:捕获组仅捕获最后一次出现,因此我们需要拆分它们的定义:

    ^\]d2(?:(21[a-zA-Z0-9]{1,20}(?:\/:|$))|(10[a-zA-Z0-9]{1,20}(?:\/:|$))|(01[0-9]{14})|(11[0-9]{6})|(15[0-9]{6})|(17[0-9]{6}))*
    

    再次编辑:Javascript 似乎让我们有些头疼...我不确定处理它的正确方法,但这里有一个可以工作的示例代码。

    var str = "]d20105000456013482172012001000001/:210000000001";
    var r = new RegExp("(21[a-zA-Z0-9]{1,20}(?:\/:|$))|(10[a-zA-Z0-9]{1,20}(?:\/:|$))|(01[0-9]{14})|(11[0-9]{6})|(15[0-9]{6})|(17[0-9]{6})", "g");
    var i = 0;
    while ((match = r.exec(str)) != null) {
      console.log(match[0]);
    }

    不过,我对结果并不满意。可能会有更好的解决方案。

    【讨论】:

    • 你错过了第 4 组,分隔符为 `10' 吗?
    • 是的,我正在调查它,只是发现它没有被看到,无法弄清楚为什么刚才......
    • 它只适用于 php 风格吗?我应该如何更改它以在 regex101.com 上使用 javascript 风格? regex101.com/r/0c1mnR/1
    • 我不确定为什么 javascript 风格不起作用...我正在尝试不同的东西。很抱歉没有给出完整的答案
    猜你喜欢
    • 1970-01-01
    • 2017-10-06
    • 2015-08-01
    • 2021-09-28
    • 2014-01-05
    • 1970-01-01
    • 2011-11-28
    • 2011-07-21
    相关资源
    最近更新 更多