根据分隔符从字符串中提取子字符串

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

例如,解码后的字符串是

] d2 01 05000456013482 17 201200 10 00001 /: 21 0000000001

基于以下规则(无法获得正确的表格标记,从而附加图片),我试图从上面提到的字符串中提取子字符串。

在此处输入图像描述

我要提取的子字符串:

05000456013482(在分隔符01之后)

201200(在分隔符17之后)

00001(在分隔符10之后)

0000000001(在分隔符21之后)

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

现在有些怪癖:

1)分隔符10之后的字母数不固定。 因此,在上面给出的例子中,即使它是00001它甚至可以是001 。 类似地,分隔符21之后的字母数也不是固定的,并且它可以具有不同的长度。

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

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

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

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

] d2 01 05000456013482 17 201200 10 00001 /: 21 0000000001 – 注意:否/: 21组之后签字,因为它是EOL

] d2 17 201200 10 00001 /: 21 0000000001 /: 01 05000456013482 – 注意:10和21组都有/. 标志表示我们必须提取,直到该标志

] d2 10 00001 /: 21 0000000001 /: 01 05000456013482 17 201200 – 前两个长度各不相同,接下来两个长度固定。

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

PS – JS和jQuery帮助表示赞赏。

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

基本步骤是:

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

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

例如,由于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; } 
  

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

 ^\]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个日期字段。

正如我们所期望的那样,这三个片段以任何顺序排列,我将它们粘在了一起 暗示一个OR并期望这个大序列重复(最后用*表示0或更多,我们可以定义确切的最小值和最大值以获得更高的可靠性)

我不确定这是否适用于所有内容,因为您提供的测试字符串不包含实际值中的标识符…很可能会发生产品在日期之前的最佳日期是1月,因此其值将为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]); }