需要帮助从 JavaScript 中的字符串中提取数字
Need help extracting numbers from string in JavaScript
我需要一个坚如磐石的 RegExp 来尝试解决 Raphael.js parseStringPath
处理关于弧形路径命令和可能的其他命令的一些问题(SnapSVG 也继承了这个问题)。你看,arcTo
路径命令接受 7 个坐标和设置,但由于极端优化,某些字符串可能格式不正确,浏览器不会标记它们,而是正确呈现它们。检查 Raphael.js demo here.
看看这个例子,我正在使用 Raphael.js 的 RegExp 和一个非常简单的例子,我自己的 RegExp 叫做 incorrectReg
,试图打破像 000
这样的字符串进入 [0
,0
,0
] 或 011
进入 [0
,1
,1
].
let spaces = "\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029",
pathValues = new RegExp(`(-?\d*\.?\d*(?:e[\-+]?\d+)?)[${spaces}]*,?[${spaces}]*`, `ig`),
incorectReg = new RegExp(`([${spaces}]*0(?=[a-z0-9])|([${spaces}]\0)*0(?=[a-z0-9]*))`, `ig`); // THIS ONE
function action(){
let input = document.getElementById('input'),
output = document.getElementById('output'),
pathValue = input.getAttribute('d'),
segments = pathValue.replace(/([a-z])/gi,'|').split('|').filter(x=>x.trim()),
pathArray = []
segments.map(x=>{
let pathCommand = x[0],
pathParams = x.replace(pathCommand,'').trim()
pathArray.push( [pathCommand].concat(
pathParams.replace(',',' ')
.replace(pathValues,' ')
.replace(incorectReg,' ')
.split(' '))
.filter(x=>x)
);
})
output.setAttribute('d',pathArray.map(x=>x.join(' ')).join(''))
console.table(pathArray)
}
svg {max-width:49%}
<button onclick="action()">Extract</button>
<hr>
<svg viewBox="0 0 16 16">
<path id="input" d="M2,0a2 2 0 00,-2 2a2 2 0 002 2a.5.5 0 011 0z" stroke="red" stroke-width="1px" fill="none"></path>
</svg>
<svg viewBox="0 0 16 16">
<path id="output" d="M0 0" stroke="green" stroke-width="1" fill="none"></path>
</svg>
正如您在浏览器控制台中看到的那样,我们已经解决了 000
组(这显然不是有效的数字、布尔值或任何特定的值),我们只需要解决 011
和 11
,其中所有这些组实际上都是一串布尔值。
因此,arcTo
路径命令同样适用于
arcTo -> ['A', rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y]
// str, float, float, float, boolean (0|1), boolean (0|1), float, float
我需要更好的 incorrectReg
RegExp 和解决方案的组合来正确处理主要 arcTo
和其他类似情况。欢迎任何建议。
谢谢
根据 OP 下面的讨论,我建议不要使用正则表达式,而是使用合适的解析器(或词法分析器或分词器或如何正确调用它)。
你可以
- 编写您自己的解析器(不错的练习)
- 使用现有的东西,例如我试过成功
svg-path-parser.
我什至不确定是否可以创建这样的“超级”正则表达式。无论如何,您可以在解析过程中使用“子”正则表达式:-)
为了清楚起见和为社区服务,我将 post 一个可行的解决方案,它可能会在将来对某人有所帮助。
不幸的是,incorrectReg
RegExp,无论好坏都无法工作,因为它也可以更改其他值(例如:M0,11
returns ["M",0,1,1]
提供的 RegExp TheFourthBird), so yea Jan,你是对的!
这是一个可行的解决方案,如果您愿意,请随时进行编辑或更加清晰。一旦我们就坚如磐石的解决方案达成一致,我将立即向 Raphael 提交 PR。
let spaces = "\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029",
pathValues = new RegExp(`(-?\d*\.?\d*(?:e[\-+]?\d+)?)[${spaces}]*,?[${spaces}]*`, `ig`),
incorrectReg = new RegExp(`(?<=[01${spaces}]+)([01])[${spaces}]*`, `g`); // FIXED ONE
function action(){
let input = document.getElementById('input'),
output = document.getElementById('output'),
pathValue = input.getAttribute('d'),
segments = pathValue.replace(/([a-z])/gi,'|').split('|').filter(x=>x.trim()),
pathArray = []
segments.map(x=>{
let pathCommand = x[0],
pathParams = x.replace(pathCommand,'').trim();
pathParams = pathParams.replace(',',' ')
.replace(pathValues,' ')
/* .replace(incorrectReg,' $& ') */
.split(' ').filter(x=>x);
if ( pathCommand.toLowerCase() === 'a' && pathParams.length < 7){
for (let i=0, ln = pathParams.length; i<ln; i++){
if ( (i === 3 || i === 4) && pathParams[i].length > 1 ) {
pathParams = pathParams.slice(0,i) // first part of array
.concat(pathParams[i][0]) // extract largeArcFlag OR sweepFlag
.concat(
pathParams[i].slice(1).replace(/(\-\d|\-\.\d|\.\d*(?=\.))/g,'|').split('|'), // get sweepFlag
pathParams.slice(i+1)) // continue after flags
.filter(x=>x) // remove added empty "space" items
ln = pathParams.length // update length
}
}
if (pathParams.length === 7) {
pathArray.push([pathCommand].concat(pathParams.splice(0, 7)));
} else {
throw Error(`arcTo requires 7 coordinates, only ${pathParams.length + ' given: ['+pathParams.join(',')}]`)
}
} else {
pathArray.push( [pathCommand].concat(pathParams) );
}
})
output.setAttribute('d',pathArray.map(x=>x.join(' ')).join(''))
// console.log(pathArray)
}
svg {max-width:49%}
<button onclick="action()">Extract</button>
<hr>
<svg viewBox="0 0 16 16">
<path id="input" d="M2 0a2 2 0 00-2 2v12a2 2 0 002 2h12a2 2 0 002-2V2a2 2 0 00-2-2H2zm7.5 11h-4a.5.5 0 01-.5-.5v-4a.5.5 0 011 0v2.793l4.146-4.147a.5.5 0 01.708.708L6.707 10H9.5a.5.5 0 010 1z" fill="red"></path>
</svg>
<svg viewBox="0 0 16 16">
<path id="output" d="M0 0" fill="green"></path>
</svg>
我需要一个坚如磐石的 RegExp 来尝试解决 Raphael.js parseStringPath
处理关于弧形路径命令和可能的其他命令的一些问题(SnapSVG 也继承了这个问题)。你看,arcTo
路径命令接受 7 个坐标和设置,但由于极端优化,某些字符串可能格式不正确,浏览器不会标记它们,而是正确呈现它们。检查 Raphael.js demo here.
看看这个例子,我正在使用 Raphael.js 的 RegExp 和一个非常简单的例子,我自己的 RegExp 叫做 incorrectReg
,试图打破像 000
这样的字符串进入 [0
,0
,0
] 或 011
进入 [0
,1
,1
].
let spaces = "\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029",
pathValues = new RegExp(`(-?\d*\.?\d*(?:e[\-+]?\d+)?)[${spaces}]*,?[${spaces}]*`, `ig`),
incorectReg = new RegExp(`([${spaces}]*0(?=[a-z0-9])|([${spaces}]\0)*0(?=[a-z0-9]*))`, `ig`); // THIS ONE
function action(){
let input = document.getElementById('input'),
output = document.getElementById('output'),
pathValue = input.getAttribute('d'),
segments = pathValue.replace(/([a-z])/gi,'|').split('|').filter(x=>x.trim()),
pathArray = []
segments.map(x=>{
let pathCommand = x[0],
pathParams = x.replace(pathCommand,'').trim()
pathArray.push( [pathCommand].concat(
pathParams.replace(',',' ')
.replace(pathValues,' ')
.replace(incorectReg,' ')
.split(' '))
.filter(x=>x)
);
})
output.setAttribute('d',pathArray.map(x=>x.join(' ')).join(''))
console.table(pathArray)
}
svg {max-width:49%}
<button onclick="action()">Extract</button>
<hr>
<svg viewBox="0 0 16 16">
<path id="input" d="M2,0a2 2 0 00,-2 2a2 2 0 002 2a.5.5 0 011 0z" stroke="red" stroke-width="1px" fill="none"></path>
</svg>
<svg viewBox="0 0 16 16">
<path id="output" d="M0 0" stroke="green" stroke-width="1" fill="none"></path>
</svg>
正如您在浏览器控制台中看到的那样,我们已经解决了 000
组(这显然不是有效的数字、布尔值或任何特定的值),我们只需要解决 011
和 11
,其中所有这些组实际上都是一串布尔值。
因此,arcTo
路径命令同样适用于
arcTo -> ['A', rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y]
// str, float, float, float, boolean (0|1), boolean (0|1), float, float
我需要更好的 incorrectReg
RegExp 和解决方案的组合来正确处理主要 arcTo
和其他类似情况。欢迎任何建议。
谢谢
根据 OP 下面的讨论,我建议不要使用正则表达式,而是使用合适的解析器(或词法分析器或分词器或如何正确调用它)。
你可以
- 编写您自己的解析器(不错的练习)
- 使用现有的东西,例如我试过成功 svg-path-parser.
我什至不确定是否可以创建这样的“超级”正则表达式。无论如何,您可以在解析过程中使用“子”正则表达式:-)
为了清楚起见和为社区服务,我将 post 一个可行的解决方案,它可能会在将来对某人有所帮助。
不幸的是,incorrectReg
RegExp,无论好坏都无法工作,因为它也可以更改其他值(例如:M0,11
returns ["M",0,1,1]
提供的 RegExp TheFourthBird), so yea Jan,你是对的!
这是一个可行的解决方案,如果您愿意,请随时进行编辑或更加清晰。一旦我们就坚如磐石的解决方案达成一致,我将立即向 Raphael 提交 PR。
let spaces = "\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029",
pathValues = new RegExp(`(-?\d*\.?\d*(?:e[\-+]?\d+)?)[${spaces}]*,?[${spaces}]*`, `ig`),
incorrectReg = new RegExp(`(?<=[01${spaces}]+)([01])[${spaces}]*`, `g`); // FIXED ONE
function action(){
let input = document.getElementById('input'),
output = document.getElementById('output'),
pathValue = input.getAttribute('d'),
segments = pathValue.replace(/([a-z])/gi,'|').split('|').filter(x=>x.trim()),
pathArray = []
segments.map(x=>{
let pathCommand = x[0],
pathParams = x.replace(pathCommand,'').trim();
pathParams = pathParams.replace(',',' ')
.replace(pathValues,' ')
/* .replace(incorrectReg,' $& ') */
.split(' ').filter(x=>x);
if ( pathCommand.toLowerCase() === 'a' && pathParams.length < 7){
for (let i=0, ln = pathParams.length; i<ln; i++){
if ( (i === 3 || i === 4) && pathParams[i].length > 1 ) {
pathParams = pathParams.slice(0,i) // first part of array
.concat(pathParams[i][0]) // extract largeArcFlag OR sweepFlag
.concat(
pathParams[i].slice(1).replace(/(\-\d|\-\.\d|\.\d*(?=\.))/g,'|').split('|'), // get sweepFlag
pathParams.slice(i+1)) // continue after flags
.filter(x=>x) // remove added empty "space" items
ln = pathParams.length // update length
}
}
if (pathParams.length === 7) {
pathArray.push([pathCommand].concat(pathParams.splice(0, 7)));
} else {
throw Error(`arcTo requires 7 coordinates, only ${pathParams.length + ' given: ['+pathParams.join(',')}]`)
}
} else {
pathArray.push( [pathCommand].concat(pathParams) );
}
})
output.setAttribute('d',pathArray.map(x=>x.join(' ')).join(''))
// console.log(pathArray)
}
svg {max-width:49%}
<button onclick="action()">Extract</button>
<hr>
<svg viewBox="0 0 16 16">
<path id="input" d="M2 0a2 2 0 00-2 2v12a2 2 0 002 2h12a2 2 0 002-2V2a2 2 0 00-2-2H2zm7.5 11h-4a.5.5 0 01-.5-.5v-4a.5.5 0 011 0v2.793l4.146-4.147a.5.5 0 01.708.708L6.707 10H9.5a.5.5 0 010 1z" fill="red"></path>
</svg>
<svg viewBox="0 0 16 16">
<path id="output" d="M0 0" fill="green"></path>
</svg>