这个函数可以用正则表达式重写吗?
Can this function be rewritten with a regex?
我想重新格式化并验证用户是否提供了有效的比利时企业编号。因为输入可以是下面所有的例子:
- BE 0123.321.123
- BE0123.321.123
- BE0123 321 123
- 0123.321.123
- 123.321.123
- 123321123
我编写了一个函数来验证输入并将其重新格式化为 'display' 版本 (BE 0123.123.123) 和 'code' 版本 (123123123)。这个函数看起来像这样。
formatAndValidateEnterpriseNumber = enterpriseNumber => {
if(enterpriseNumber === undefined || !enterpriseNumber || (enterpriseNumber || '').length < 3) return { isValid: false, error: 'Please fill in your enterprise number' };
//Remove space, dots, ...
enterpriseNumber = enterpriseNumber.toUpperCase();
enterpriseNumber = enterpriseNumber.replace(/[. ,:-]+/g, '');
//Check for double country code
const reDouble = /^[a-zA-Z]{4}/;
if (reDouble.test(enterpriseNumber)) enterpriseNumber = enterpriseNumber.substring(2);
if (enterpriseNumber.length < 9 || enterpriseNumber.length > 12) return { isValid: false, error: 'The length of the provided number is incorrect' };
//Check country code
const reBE = /^[a-zA-Z]{2}/;
if (reBE.test(enterpriseNumber)) {
//Check if country code = BE
if (enterpriseNumber.slice(0, 2) !== 'BE') return { isValid: false, error: 'Please fill in a Belgian enterprise number' };
// Remove country code
else enterpriseNumber = enterpriseNumber.substring(2);
}
//Check if first digit is 0
if (enterpriseNumber.length === 10 && enterpriseNumber.startsWith('0')) enterpriseNumber = enterpriseNumber.substring(1);
//Check if enterpriseNumber is valid with modulo test
if (parseInt(97 - (enterpriseNumber.slice(0, 7) % 97), 10) !== parseInt(enterpriseNumber.slice(7, 9), 10))
return { isValid: false, error: 'The provided number is invalid'}
return {
isValid: true,
enterpriseNumber: enterpriseNumber,
displayEnterpriseNumber: `BE 0${enterpriseNumber.substring(0, 3)}.${enterpriseNumber.substring(3, 6)}.${enterpriseNumber.substring(6, 9)}`
};
};
我认为这很混乱,我想知道是否可以通过 one/two 重新格式化和验证用户输入的正则表达式测试来改进?
第二个问题:有时对于帐户或信用卡号,输入字段中已经有那些下划线和线 (-),并且在输入时重新格式化数字。这种方法叫什么?可以针对比利时企业编号等特定事物执行此方法吗?
是的,你可以:
^(?:BE)?\s*[0-1]?(\d[. ]*){9}$
这个正则表达式应该可以做到!
This source (in Dutch) states what an enterprise number is for Belgium:
It has the country code: BE
followed by a 0
or 1
and then followed by 9 digits.
https://regex101.com/r/4SRHxi/4
解释:
^
:字符串必须以给定的正则表达式 开头
(?:BE)?
: 查找带有 BE 但 ?
表示它匹配零或
一次 - ?:
表示找到但不捕获
\s*
:搜索匹配零次或无限次的space
[0-1]?
: 检查 1 的零是否出现零次或一次
((\d[. ]*){9})
:检查剩余字符串后是否有 9 位数字,与填充了多少个点或 spaces 无关。每次迭代都被捕获为第一个捕获组。这在我们稍后更换时变得很重要。
$
: 字符串必须结束
这将检查输入是否有效。
编辑成code
版本很简单:
«code».replace(/^(?:BE)?\s*[0-1]?((\d[. ]*){9})$/g, function(){
return arguments[1].replace(/\D/g, "");
});
g
或全局修饰符将确保删除所有不需要的字符。通过使用其中包含替换的函数来替换所有非数字字符。此函数将输出我们想要的结果。
document.querySelector("pre").textContent.split("\n").forEach(function(element){
if (element.match(/^(?:BE)?\s*[0-1]?(\d[. ]*){9}$/))
{
console.log(element.replace(/^(?:BE)?\s*[0-1]?((\d[. ]*){9})$/g, function(){
return arguments[1].replace(/\D/g, "");
}));
}
else
{
console.log(`REJECTED: ${element}`);
}
});
<pre>
BE 0123.321.123
BE0123.321.123
BE0123 321 123
BE 0123 321 123
BE 01 23 32 11 23
BE 0123 32 11 23
1123.321.123
123.321.123
123321123
AAA3434343A
BE 1233 445 4545 442
</pre>
现在很容易将字符串重建为正确的用户友好方式:
document.querySelector("pre").textContent.split("\n").forEach(function(element) {
if (element.match(/^(?:BE)?\s*[0-1]?((\d[. ]*){9})$/)) {
var stripped = element.replace(/^(?:BE)?\s*[0-1]?((\d[. ]*){9})$/g, function(){
return arguments[1].replace(/\D/g, "");
});
//with the modulo check from your code added back in.
if (97 - (parseInt(stripped.slice(0, 7), 10) % 97) == parseInt(stripped.slice(7, 9), 10)) {
//use a literal string
//use substring to put the dots between the sections of three numbers.
var humanReadable = `BE 0${stripped.substring(0,3)}.${stripped.substring(3,6)}.${stripped.substring(6,9)}`;
console.log(`CODE: ${stripped}`, `UI: ${humanReadable}`);
}
}
});
<pre>
BE 0123.321.123
BE0123.321.123
BE0123 321 123
0123.321.123
123.321.123
123321123
844256524
</pre>
第二个问题
是的,这是可以做到的,但是它需要您为它编写自己的代码。
简单版:
document.querySelector("div.enterprisenumber > input").addEventListener("keydown", function(e) {
let value = this.value;
//prevent the input from going back to 0
if ( (value.length == 0 && (e.key == "Backspace" || e.key == "Delete"))) {
e.preventDefault();
return false;
}
}, true);
document.querySelector("div.enterprisenumber > input").addEventListener("keyup", function(e) {
//reset to a value without dots
let value = this.value.replace(/\./g, "");
//strip the leading zero
const valueWithout = value;
//calculate how much iterations we need of a groups of three digits.
const i = Math.floor(valueWithout.length / 3);
let newValue = "";
//check if backspace or delete are used to make sure the dot can be deleted.
if (valueWithout.length < 9 && !(e.key == "Backspace" || e.key == "Delete")) {
//only fire when higher than zero
if (i > 0) {
let t;
//t is the index
for (t = 0; t < i; t++) {
//slice the correct portion of the string and append a dot, unless we are at the end of the groups
newValue += valueWithout.slice(t * 3, t * 3 + 3) + (t == 2 ? "" : ".");
}
//append the remainder that is not a group of three.
newValue += valueWithout.slice((t) * 3);
} else {
//return the value as is.
newValue = value;
}
//set the new value to the input.
this.value = newValue;
}
}, true);
document.querySelector("div.enterprisenumber > input").addEventListener("blur", function(e) {
let passed = false;
if (this.value.match(/^(?:BE)?\s*[0-1]?((\d[. ]*){9})$/))
{
const value = this.value.replace(/\./g, "");
//with modulo check
if (97 - (parseInt(value.slice(0,7), 10) % 97) == value.slice(7, 9))
{
passed = true;
}
}
document.querySelector(".enterprisenumber").classList[(passed ? "remove" : "add")]("error");
});
//if focus then focus input
document.querySelector("div.enterprisenumber").addEventListener("click", function(e) {
if (e.target && e.target.nodeName != "SELECT")
{
this.querySelector("input").focus();
}
});
* {
box-sizing: border-box;
font-family: tahoma;
font-size: 10pt;
}
div.enterprisenumber {
border: 1px solid #747474;
width: 300px;
padding: 0px;
display: grid;
grid-template-columns: 25px 40px auto;
border-radius: 10px;
}
div.enterprisenumber.error {
border: 1px solid #ff0000;
}
div.enterprisenumber>span {
grid-column: 1;
border: 0px;
padding: 5px;
background: linear-gradient(to right, rgba(0,0,0, 0.8) 33%, rgba(255,243,54, 0.8) 33%, rgba(255, 243, 54, 0.8) 66%, rgba(255, 15, 33, 0.8) 66%, rgba(255, 15, 33, 0.8) 100%);
color: #ffffff;
font-weight: bold;
text-shadow: 1px 1px #000000;
border-radius: 10px 10px 10px 10px;
}
div.enterprisenumber>select {
grid-column: 2;
border: 0px;
padding: 5px;
}
div.enterprisenumber>input {
grid-column: 3;
border: 0px;
padding: 5px;
border-radius: 0px 10px 10px 0px;
}
Enter: 844256524
<div class="enterprisenumber">
<span>BE</span><select><option value="0">0</option><option value="1">1</option><input value="" maxlength="11" />
</div>
对于您的示例字符串,您可以匹配:
^(?:BE\s?)?[01]?(\d{3}([. ])\d{3}\d{3}|\d{9})$
那将匹配
^
字符串开头
(?:BE\s?)?
可选 BE 后跟可选 whitespace char
[01]?
可选0或1
(
捕获组
\d{3}
匹配3个数字
([. ])
在组中捕获 space 或数字以用作反向引用
\d{3}\d{3}
匹配3位数字,点或space(\2是反向引用)和3位数字
|
或
\d{9}
匹配9位数字
)
关闭捕获组
$
字符串结束
然后在替换中使用第一个捕获组并将 space 或点替换为空字符串。
let pattern = /^(?:BE\s?)?[01]?(\d{3}([. ])\d{3}\d{3}|\d{9})$/;
let strings = [
"BE 0123.321.123",
"BE0123.321.123",
"BE0123 321 123",
"0123.321.123",
"123.321.123",
"123321123",
];
strings = strings.map(x => x.replace(pattern, function(m, g) {
let enterpriseNumber = g.replace(/[. ]/g, "");
return `BE 0${enterpriseNumber.substring(0, 3)}.${enterpriseNumber.substring(3, 6)}.${enterpriseNumber.substring(6, 9)}`
}));
console.log(strings);
这里是 BE ____.___.___
输入样式的实现。该模式将保持不变,因此输入将保证具有 "BE" 前缀、space 和两个点。然后验证可以集中在完整性和模测试上。
请注意,输入要求第一组有 4 个数字,其中第一个数字必须是 0 或 1。
const ent = document.getElementById("ent");
const out = document.getElementById("isvalid");
function format() {
const re = /^\D*[2-9]+|\D+/g;
const [i, j] = [this.selectionStart, this.selectionEnd].map(i => {
i = this.value.slice(0, i).replace(re, "").length;
return i + 3 + (i >= 4 + format.backspace) + (i >= 7 + format.backspace);
});
this.value = "BE " + this.value.replace(re, "").padEnd(10, "_")
.replace(/(....)(...)(...).*/, "..");
this.setSelectionRange(i, j);
format.backspace = false;
out.textContent = validate(this.value) ? "is valid" : "is invalid";
}
function validate(num) {
return /^BE [01](\d{3}\.){2}\d{3}$/.test(num)
&& 97 - num.replace(/\D/g, "").slice(0, 8) % 97 === +num.slice(-2);
}
ent.addEventListener("input", format);
ent.addEventListener("keydown", (e) => format.backspace = e.key == "Backspace");
Belgian enterprise number: <input id="ent" value="BE ____.___.___">
<span id="isvalid"></span>
我想重新格式化并验证用户是否提供了有效的比利时企业编号。因为输入可以是下面所有的例子:
- BE 0123.321.123
- BE0123.321.123
- BE0123 321 123
- 0123.321.123
- 123.321.123
- 123321123
我编写了一个函数来验证输入并将其重新格式化为 'display' 版本 (BE 0123.123.123) 和 'code' 版本 (123123123)。这个函数看起来像这样。
formatAndValidateEnterpriseNumber = enterpriseNumber => {
if(enterpriseNumber === undefined || !enterpriseNumber || (enterpriseNumber || '').length < 3) return { isValid: false, error: 'Please fill in your enterprise number' };
//Remove space, dots, ...
enterpriseNumber = enterpriseNumber.toUpperCase();
enterpriseNumber = enterpriseNumber.replace(/[. ,:-]+/g, '');
//Check for double country code
const reDouble = /^[a-zA-Z]{4}/;
if (reDouble.test(enterpriseNumber)) enterpriseNumber = enterpriseNumber.substring(2);
if (enterpriseNumber.length < 9 || enterpriseNumber.length > 12) return { isValid: false, error: 'The length of the provided number is incorrect' };
//Check country code
const reBE = /^[a-zA-Z]{2}/;
if (reBE.test(enterpriseNumber)) {
//Check if country code = BE
if (enterpriseNumber.slice(0, 2) !== 'BE') return { isValid: false, error: 'Please fill in a Belgian enterprise number' };
// Remove country code
else enterpriseNumber = enterpriseNumber.substring(2);
}
//Check if first digit is 0
if (enterpriseNumber.length === 10 && enterpriseNumber.startsWith('0')) enterpriseNumber = enterpriseNumber.substring(1);
//Check if enterpriseNumber is valid with modulo test
if (parseInt(97 - (enterpriseNumber.slice(0, 7) % 97), 10) !== parseInt(enterpriseNumber.slice(7, 9), 10))
return { isValid: false, error: 'The provided number is invalid'}
return {
isValid: true,
enterpriseNumber: enterpriseNumber,
displayEnterpriseNumber: `BE 0${enterpriseNumber.substring(0, 3)}.${enterpriseNumber.substring(3, 6)}.${enterpriseNumber.substring(6, 9)}`
};
};
我认为这很混乱,我想知道是否可以通过 one/two 重新格式化和验证用户输入的正则表达式测试来改进?
第二个问题:有时对于帐户或信用卡号,输入字段中已经有那些下划线和线 (-),并且在输入时重新格式化数字。这种方法叫什么?可以针对比利时企业编号等特定事物执行此方法吗?
是的,你可以:
^(?:BE)?\s*[0-1]?(\d[. ]*){9}$
这个正则表达式应该可以做到!
This source (in Dutch) states what an enterprise number is for Belgium:
It has the country code:
BE
followed by a0
or1
and then followed by 9 digits.
https://regex101.com/r/4SRHxi/4
解释:
^
:字符串必须以给定的正则表达式 开头
(?:BE)?
: 查找带有 BE 但?
表示它匹配零或 一次 -?:
表示找到但不捕获\s*
:搜索匹配零次或无限次的space[0-1]?
: 检查 1 的零是否出现零次或一次((\d[. ]*){9})
:检查剩余字符串后是否有 9 位数字,与填充了多少个点或 spaces 无关。每次迭代都被捕获为第一个捕获组。这在我们稍后更换时变得很重要。$
: 字符串必须结束
这将检查输入是否有效。
编辑成code
版本很简单:
«code».replace(/^(?:BE)?\s*[0-1]?((\d[. ]*){9})$/g, function(){
return arguments[1].replace(/\D/g, "");
});
g
或全局修饰符将确保删除所有不需要的字符。通过使用其中包含替换的函数来替换所有非数字字符。此函数将输出我们想要的结果。
document.querySelector("pre").textContent.split("\n").forEach(function(element){
if (element.match(/^(?:BE)?\s*[0-1]?(\d[. ]*){9}$/))
{
console.log(element.replace(/^(?:BE)?\s*[0-1]?((\d[. ]*){9})$/g, function(){
return arguments[1].replace(/\D/g, "");
}));
}
else
{
console.log(`REJECTED: ${element}`);
}
});
<pre>
BE 0123.321.123
BE0123.321.123
BE0123 321 123
BE 0123 321 123
BE 01 23 32 11 23
BE 0123 32 11 23
1123.321.123
123.321.123
123321123
AAA3434343A
BE 1233 445 4545 442
</pre>
现在很容易将字符串重建为正确的用户友好方式:
document.querySelector("pre").textContent.split("\n").forEach(function(element) {
if (element.match(/^(?:BE)?\s*[0-1]?((\d[. ]*){9})$/)) {
var stripped = element.replace(/^(?:BE)?\s*[0-1]?((\d[. ]*){9})$/g, function(){
return arguments[1].replace(/\D/g, "");
});
//with the modulo check from your code added back in.
if (97 - (parseInt(stripped.slice(0, 7), 10) % 97) == parseInt(stripped.slice(7, 9), 10)) {
//use a literal string
//use substring to put the dots between the sections of three numbers.
var humanReadable = `BE 0${stripped.substring(0,3)}.${stripped.substring(3,6)}.${stripped.substring(6,9)}`;
console.log(`CODE: ${stripped}`, `UI: ${humanReadable}`);
}
}
});
<pre>
BE 0123.321.123
BE0123.321.123
BE0123 321 123
0123.321.123
123.321.123
123321123
844256524
</pre>
第二个问题 是的,这是可以做到的,但是它需要您为它编写自己的代码。
简单版:
document.querySelector("div.enterprisenumber > input").addEventListener("keydown", function(e) {
let value = this.value;
//prevent the input from going back to 0
if ( (value.length == 0 && (e.key == "Backspace" || e.key == "Delete"))) {
e.preventDefault();
return false;
}
}, true);
document.querySelector("div.enterprisenumber > input").addEventListener("keyup", function(e) {
//reset to a value without dots
let value = this.value.replace(/\./g, "");
//strip the leading zero
const valueWithout = value;
//calculate how much iterations we need of a groups of three digits.
const i = Math.floor(valueWithout.length / 3);
let newValue = "";
//check if backspace or delete are used to make sure the dot can be deleted.
if (valueWithout.length < 9 && !(e.key == "Backspace" || e.key == "Delete")) {
//only fire when higher than zero
if (i > 0) {
let t;
//t is the index
for (t = 0; t < i; t++) {
//slice the correct portion of the string and append a dot, unless we are at the end of the groups
newValue += valueWithout.slice(t * 3, t * 3 + 3) + (t == 2 ? "" : ".");
}
//append the remainder that is not a group of three.
newValue += valueWithout.slice((t) * 3);
} else {
//return the value as is.
newValue = value;
}
//set the new value to the input.
this.value = newValue;
}
}, true);
document.querySelector("div.enterprisenumber > input").addEventListener("blur", function(e) {
let passed = false;
if (this.value.match(/^(?:BE)?\s*[0-1]?((\d[. ]*){9})$/))
{
const value = this.value.replace(/\./g, "");
//with modulo check
if (97 - (parseInt(value.slice(0,7), 10) % 97) == value.slice(7, 9))
{
passed = true;
}
}
document.querySelector(".enterprisenumber").classList[(passed ? "remove" : "add")]("error");
});
//if focus then focus input
document.querySelector("div.enterprisenumber").addEventListener("click", function(e) {
if (e.target && e.target.nodeName != "SELECT")
{
this.querySelector("input").focus();
}
});
* {
box-sizing: border-box;
font-family: tahoma;
font-size: 10pt;
}
div.enterprisenumber {
border: 1px solid #747474;
width: 300px;
padding: 0px;
display: grid;
grid-template-columns: 25px 40px auto;
border-radius: 10px;
}
div.enterprisenumber.error {
border: 1px solid #ff0000;
}
div.enterprisenumber>span {
grid-column: 1;
border: 0px;
padding: 5px;
background: linear-gradient(to right, rgba(0,0,0, 0.8) 33%, rgba(255,243,54, 0.8) 33%, rgba(255, 243, 54, 0.8) 66%, rgba(255, 15, 33, 0.8) 66%, rgba(255, 15, 33, 0.8) 100%);
color: #ffffff;
font-weight: bold;
text-shadow: 1px 1px #000000;
border-radius: 10px 10px 10px 10px;
}
div.enterprisenumber>select {
grid-column: 2;
border: 0px;
padding: 5px;
}
div.enterprisenumber>input {
grid-column: 3;
border: 0px;
padding: 5px;
border-radius: 0px 10px 10px 0px;
}
Enter: 844256524
<div class="enterprisenumber">
<span>BE</span><select><option value="0">0</option><option value="1">1</option><input value="" maxlength="11" />
</div>
对于您的示例字符串,您可以匹配:
^(?:BE\s?)?[01]?(\d{3}([. ])\d{3}\d{3}|\d{9})$
那将匹配
^
字符串开头(?:BE\s?)?
可选 BE 后跟可选 whitespace char[01]?
可选0或1(
捕获组\d{3}
匹配3个数字([. ])
在组中捕获 space 或数字以用作反向引用\d{3}\d{3}
匹配3位数字,点或space(\2是反向引用)和3位数字|
或\d{9}
匹配9位数字
)
关闭捕获组$
字符串结束
然后在替换中使用第一个捕获组并将 space 或点替换为空字符串。
let pattern = /^(?:BE\s?)?[01]?(\d{3}([. ])\d{3}\d{3}|\d{9})$/;
let strings = [
"BE 0123.321.123",
"BE0123.321.123",
"BE0123 321 123",
"0123.321.123",
"123.321.123",
"123321123",
];
strings = strings.map(x => x.replace(pattern, function(m, g) {
let enterpriseNumber = g.replace(/[. ]/g, "");
return `BE 0${enterpriseNumber.substring(0, 3)}.${enterpriseNumber.substring(3, 6)}.${enterpriseNumber.substring(6, 9)}`
}));
console.log(strings);
这里是 BE ____.___.___
输入样式的实现。该模式将保持不变,因此输入将保证具有 "BE" 前缀、space 和两个点。然后验证可以集中在完整性和模测试上。
请注意,输入要求第一组有 4 个数字,其中第一个数字必须是 0 或 1。
const ent = document.getElementById("ent");
const out = document.getElementById("isvalid");
function format() {
const re = /^\D*[2-9]+|\D+/g;
const [i, j] = [this.selectionStart, this.selectionEnd].map(i => {
i = this.value.slice(0, i).replace(re, "").length;
return i + 3 + (i >= 4 + format.backspace) + (i >= 7 + format.backspace);
});
this.value = "BE " + this.value.replace(re, "").padEnd(10, "_")
.replace(/(....)(...)(...).*/, "..");
this.setSelectionRange(i, j);
format.backspace = false;
out.textContent = validate(this.value) ? "is valid" : "is invalid";
}
function validate(num) {
return /^BE [01](\d{3}\.){2}\d{3}$/.test(num)
&& 97 - num.replace(/\D/g, "").slice(0, 8) % 97 === +num.slice(-2);
}
ent.addEventListener("input", format);
ent.addEventListener("keydown", (e) => format.backspace = e.key == "Backspace");
Belgian enterprise number: <input id="ent" value="BE ____.___.___">
<span id="isvalid"></span>