正则表达式以匹配逗号分隔的主机名

RegExp to match comma separated hostnames

本文关键字:分隔 主机 正则表达式      更新时间:2023-09-26

问题

我正在尝试使用 JavaScript 验证<textarea>的内容,所以我创建了一个 validate() 函数,该函数返回truefalse文本区域内的文本是否有效。

文本区域只能包含逗号分隔的主机名。主机名的意思是类似于 subdomain.domain.com ,所以它基本上是一些点分隔的字符串。由于用户往往写得不是很好,我还希望允许在各种主机名和逗号之间留下任意数量的空格,但不要在主机名内

以下是一些应该匹配或不应该匹配的示例:

  • 应匹配:

    • domain.com,domain2.co.vu,sub.domain.org
    • ​ domai2n.com , dom-ain.org.co.vu.nl ,domain.it ​
    • dom-ain.it, domain.com, domain.eu.org.something
    • a.b.c, a.b, a.a.a , a.r
    • 0191481.com
  • 不应匹配:

    • domain.com., sub.domain.it不完整的主机名
    • domain.me, domain2不完整的主机名
    • sub.sub.sub.domain.tv, do main.it主机名包含空格
    • site不完整的主机名
    • hèy.com主机名不能包含重音符号
    • hey.01com主机名不能以数字结尾或包含数字的字符串
    • hello.org..wow不完整的主机名

到目前为止我尝试过什么

我使用以下代码构建了我的函数:

function validate(text) {
    return (
        (/^([a-z0-9'-'.]+ *, *)*[a-z0-9'-'.]+[^, ]$/i.test(text) 
        && !/'.[^a-z]|'.$/i.test(text)
        && ~text.indexOf('.'))
    );
}

不幸的是,我的函数不起作用。它无法识别不完整的主机名并返回true

有什么方法可以做到这一点吗?也许不使用正则表达式,即使我更喜欢使用单个正则表达式。

说不使用正则表达式的答案非常好,但我喜欢正则表达式,所以:

^'s*(?:(?:'w+(?:-+'w+)*'.)+[a-z]+)'s*(?:,'s*(?:(?:'w+(?:-+'w+)*'.)+[a-z]+)'s*)*$

是的。。没那么漂亮。但它有效 - 在您的样品盒上进行了测试 http://regex101.com

编辑:好的,让我们分解一下。并且只允许sub-domain-01.coma--b.com,不允许-.com

每个子域 thingo: 'w+(?:-+'w+)* 匹配单词字符字符串以及一些前面带有破折号的单词(可选)。

每个主机名:'s*(?:(?:'w+(?:-'w+)*'.)+[a-z]+)'s*一堆子域 thingos 后跟一个点。最后只跟一串字母(TLD)。当然还有两侧的可选空间。

整件事:'s*(?:(?:'w+(?:-'w+)*'.)+[a-z]+)'s*(?:,'s*(?:(?:'w+(?:-'w+)*'.)+[a-z]+)'s*)*一个主机名,后跟 0 或更多 ,hostname s作为我们的逗号分隔列表。

真的很简单。

虽然@dandavis的回答/评论令人印象深刻,但让我们将其分解为步骤。

  1. 从文本区域获取值,并trim()前导空格和结尾空格。
  2. 使用 /'s+/g 将所有空格替换为单个空格。 表示查找出现一次或多次的每个空格。
  3. ,<space><space>,<space>拆分。拆分返回数组。
  4. 使用过滤器迭代每个数组元素
  5. 检查元素是否为有效域。如果是这样,请将其退回。

var domains = document.querySelector("textarea").value;
domains = domains.trim().replace(/'s+/g, " ").split(/'s?,'s/);
var domainsTested = domains.filter(function(element){
                  if (element.match(/^[a-zA-Z0-9][a-zA-Z0-9-_]{0,61}[a-zA-Z0-9]{0,1}'.([a-zA-Z]{1,6}|[a-zA-Z0-9-]{1,30}'.[a-zA-Z]{2,3})$/))
                    {
                      return element;
                    }
              })
document.write(domainsTested.join(" | ")); //this is just here to show the results.
document.write("<br />Domainstring is ok: " + (domainsTested.length == domains.length)); //If it's valid then this should be equal.
<textarea style="width: 300px; height: 100px">www.example.com    , example.com, example.ca,     example, example.com example.nl     www.example,    www.exam ple.com,  sub.sub.sub.domain.tv, do main.it,   sub.domain.tv</textarea>

function validate() {
    //Get the user input
    var hostnames = document.getElementById('yourtextarea').value;
    //Regex to validate hostname
    var re = new RegExp(/^([a-zA-Z0-9]([a-zA-Z0-9'-]{0,61}[a-zA-Z0-9])?'.)+[a-zA-Z]{2,6}$/);
    //Trim whitespace
    hostnames = hostnames.trim();
    //Explode into an array
    hostnames = hostnames.split(",");
    //Loop through array & test each hostname with regex
    var is_valid = true;
    for (var i=0; i < hostnames.length; i++){
        var hostname = hostnames[i].trim();
        if (re.test(hostname)) {
           is_valid = true; //if valid, continue loop
        } else {
           is_valid = false; //if invalid, break loop and return false
           break;
        }
    } //end for loop
    return is_valid;
} //end function validate()

匹配您指示的每个示例,但"dom-ain.it,domain.com,域.eu.org.something"除外,因为"某些内容"无效。

JSFiddle: http://jsfiddle.net/nesutqjf/2/

我不会为此使用正则表达式,因为您有很多不同的规则要检查。当你只有几个规则时,正则表达式是很好的,这些规则很容易表达,但写成"解析代码"很痛苦。

正如大多数评论所建议的那样,我只会做hostnames.split(',').forEach(validateHostname);,并且在内部validateHostname拒绝任何中间有空格、两个相邻点、没有点、以点结尾、具有非 ASCII 字符、在最后一个点分隔标记中有数字的主机名,等等。

像这样的函数将比正则表达式更容易添加新规则。

我已经使用这种模式一段时间了,似乎也适用于您的情况:

/^[a-zA-Z0-9][a-zA-Z0-9'-_]*'.([a-zA-Z0-9]+|[a-zA-Z0-9'-_]+'.[a-zA-Z]+)+$/gi

逻辑很简单:

  • ^[a-zA-Z0-9]:URL 必须以字母数字字符开头
  • [a-zA-Z0-9'-_]*:第一个字母数字字符后可以跟零个或多个:字母数字字符、下划线或短划线
  • '.:第一件必须后跟句点。
  • 第二件必须遵循相同的模式:
    1. [a-zA-Z0-9]+:一个或多个字母数字字符,或
    2. [a-zA-Z0-9'-_]+'.[a-zA-Z0-9]+:一个或多个字母数字字符、下划线或短划线,后跟句点和一个或多个字母数字字符

您可以在以下代码片段中检查此模式是否适用于大多数 URL。我的做法类似于其他人描述的策略:

  • 在键上获取文本区域的值(或者您可以绑定提交,模糊,按键,键下,更改等)
  • ,字符拆分值
  • 使用$.trim()删除侧翼空格
  • 使用上面的正则表达式模式来评估每个单独的字符串

可选,用于视觉输出:

  • 生成网址列表
  • 指明输入的每个网址是否有效

$(function() {
    $('textarea').keyup(function() {
        var urls = $(this).val().split(',');
        $('ul').empty();
        $.each(urls, function(i,v) {
            // Trim URL
            var url = $.trim(v);
            
            // RegEx
            var pat = /^[a-zA-Z0-9][a-zA-Z0-9'-_]*'.([a-zA-Z0-9]+|[a-zA-Z0-9'-_]+'.[a-zA-Z]+)+$/gi,
                test = pat.test(url);
            
            // Append
            $('ul').append('<li>'+url+' <span>'+test+'</span></li>');
        });
    });
});
textarea {
    width: 100%;
    height: 100px;
}
ul span {
    background-color: #eee;
    display: inline-block;
    margin-left: .25em;
    padding: 0 .25em;
    text-transform: uppercase;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<textarea placeholder="Paste URLs here"></textarea>
<ul></ul>

一个包含 validate.js 的示例,该示例具有经过良好测试的例程,用于测试有效的 FQDN。或者查看源代码并获取您需要的内容。

function validate (e) {
    var target = e.target || e;
    
    target.value.split(',').some(function (item) {
        var notValid = !validator.isFQDN(item.trim());
        
        if (notValid) {
            target.classList.add('bad');
        } else {
            target.classList.remove('bad');
        }
      
      return notValid;
    });
}
var domains = document.getElementById('domains');
domains.addEventListener('change', validate);
validate(domains);
#domains {
    width: 300px;
    height: 100px;
}
.bad {
    background-color: red
}
<script src="http://rawgit.com/chriso/validator.js/master/validator.js"></script>
<textarea id="domains">www.example.com, example.com, example.ca, example, example.com example.nl www.example, www.exam ple.com</textarea>

此正则表达式应符合域的所有要求。它将 TLD 限制为 24 个字符,因为它是目前最长的 TLD,但您可以将其更改为理论上的 63 个字符(然后您必须将"25"更改为"64" - 请记住,它有两个实例):

^'s*(?!.*?_.*?)(?!(?:['d'w]+?'.)?'-['w'd'.'-]*?)(?!['w'd]+?'-'.(?:['d'w'.'-]+?))(?=['w'd])(?=['w'd'.'-]*?'.+['w'd'.'-]*?)(?!['w'd'.'-]{254})(?!(?:'.?['w'd'-'.]*?['w'd'-]{64,}'.)+?)['w'd'.'-]+?(?<!['w'd'-'.]*?'.['d]+?)(?<=['w'd'-]{2,})(?<!['w'd'-]{25})('s*,'s*(?!.*?_.*?)(?!(?:['d'w]+?'.)?'-['w'd'.'-]*?)(?!['w'd]+?'-'.(?:['d'w'.'-]+?))(?=['w'd])(?=['w'd'.'-]*?'.+['w'd'.'-]*?)(?!['w'd'.'-]{254})(?!(?:'.?['w'd'-'.]*?['w'd'-]{64,}'.)+?)['w'd'.'-]+?(?<!['w'd'-'.]*?'.['d]+?)(?<=['w'd'-]{2,})(?<!['w'd'-]{25}))*'s*$

在这里你可以测试它:https://regex101.com/r/ZyPMn4/1