【问题标题】:Code Golf: Email Address Validation without Regular ExpressionsCode Golf:没有正则表达式的电子邮件地址验证
【发布时间】:2009-09-07 17:25:29
【问题描述】:

(编辑:什么是 Code Golf:Code Golf 是用您喜欢的任何语言的字符数以最短的代码量来解决特定问题的挑战。 More info here 在 Meta StackOverflow 上。)

Code Golfers,这是关于字符串操作的挑战。

电子邮件地址验证,当然没有正则表达式(或类似的解析库)。与其说是电子邮件地址,不如说是您可以编写多短的不同字符串操作和下面给出的约束。

规则如下(是的,我知道,这不符合 RFC,但这些将是本次挑战的 5 条规则):

  • 该组中至少有 1 个字符位于 @: 之前

    A-Z, a-z, 0-9, . (period), _ (underscore)
    
  • @ 必须存在,只存在一次

    john@smith.com
        ^
    
  • 句点 (.) 必须在 @ 之后恰好存在一次

    john@smith.com
              ^
    
  • @ 和后面的 之间至少有 1 个仅 [A-Z, a-z] 字符。 (句号)

    john@s.com
         ^
    
  • 在最后的 .期间

    john@smith.ab
               ^^
    

请仅发布方法/函数,它将采用字符串(建议的电子邮件地址),然后根据电子邮件地址有效(真)或无效(假)返回布尔结果(真/假) .

Samples:
b@w.org    (valid/true)          @w.org     (invalid/false)    
b@c@d.org  (invalid/false)       test@org   (invalid/false)    
test@%.org (invalid/false)       s%p@m.org  (invalid/false)    
j_r@x.c.il (invalid/false)       j_r@x.mil  (valid/true)
r..t@x.tw  (valid/true)          foo@a%.com (invalid/false)

祝你好运!

【问题讨论】:

  • 最近有太多 [code-golf]。如果这种情况继续下去,我会很不情愿地加入 Pax 并开始投票结束。
  • dmckee:什么,stackoverflow 的问题编号用完了吗?
  • @caf:就像最好的 [joke|comic|...] 和类似的问题,这些不在 SO 的职权范围内。只要它们很少见,这不是问题。事实上,它们起到消遣和提供社区感的作用。但是,如果它们变得过于普遍,它们给新来者关于网站文化和目的的错误印象;他们会潜水出现很多乱七八糟的东西。真可惜,因为我喜欢编码高尔夫,喜欢解决一些出现的问题,并且为我的参赛作品感到非常自豪。
  • @dmckee - 虽然我是代码高尔夫的粉丝,但我倾向于同意我们最近看到大量高尔夫问题。

标签: code-golf rosetta-stone


【解决方案1】:

C89(166 个字符)

#define B(c)isalnum(c)|c==46|c==95
#define C(x)if(!v|*i++-x)return!1;
#define D(x)for(v=0;x(*i);++i)++v;
v;e(char*i){D(B)C(64)D(isalpha)C(46)D(isalpha)return!*i&v>1;}

不可重入,但可以多次运行。测试台:

#include<stdio.h>
#include<assert.h>
main(){
    assert(e("b@w.org"));
    assert(e("r..t@x.tw"));
    assert(e("j_r@x.mil"));
    assert(!e("b@c@d.org"));
    assert(!e("test@%.org"));
    assert(!e("j_r@x.c.il"));
    assert(!e("@w.org"));
    assert(!e("test@org"));
    assert(!e("s%p@m.org"));
    assert(!e("foo@a%.com"));
    puts("success!");
}

【讨论】:

  • +1,喜欢嵌套宏并声明一个没有类型的全局变量!
  • 我只想问一下,这个100%是你自己想出来的,还是你从其他地方有什么线索?并不是说你不能自己想出它:) 我真的很惊讶你的解决方案如此之短。
  • 真的应该是 C89+ASCII,我很确定在使用 EBCDIC 的 C89 实现上它会失败;)
  • @strager: isalpha()c&lt;91&amp;c&gt;64|c&lt;123&amp;c&gt;96 短得多,并且可以在没有 #include 的情况下工作。事实上,你只需要#define A isalpha。嗯...想一想,现在只要在有A 的地方使用isalpha,而将#define A 放在一起,将是相同的长度。
  • &lt;ctype.h&gt; 声明 isalpha。这并不意味着它是必需的。如果没有原型,编译器会假设它返回一个 int,它会这样做,并且不会检查参数的数量或类型,但这没关系。
【解决方案2】:

J

:[[/%^(:[[+-/^,&i|:[$[' ']^j+0__:k<3:]]

【讨论】:

  • 这是我见过的第五个 J 程序,它以 :[[ 开头并以 :]] 结尾 - 是什么?
  • 一开始特别难过,但到最后真的很开心。
【解决方案3】:

C89,175 个字符。

#define G &&*((a+=t+1)-1)==
#define H (t=strspn(a,A
t;e(char*a){char A[66]="_.0123456789Aa";short*s=A+12;for(;++s<A+64;)*s=s[-1]+257;return H))G 64&&H+12))G 46&&H+12))>1 G 0;}

我使用的是标准库函数strspn(),所以我觉得这个答案不像 strager 的答案那样“干净”,没有任何库函数。 (我还借鉴了他声明一个没有类型的全局变量的想法!)

这里的技巧之一是通过将._ 放在字符串A 的开头,可以在strspn() 测试中轻松包含或排除它们:当你想允许它们时, 使用strspn(something, A);如果不这样做,请使用strspn(something, A+12)。另一个是假设sizeof (short) == 2 * sizeof (char),并从“种子”对Aa 中一次构建有效字符数组 2。其余的只是寻找一种方法来强制子表达式看起来足够相似,以便可以将它们提取到#defined 宏中。

要使此代码更“可移植”(呵呵 :-P),您可以更改数组构建代码

char A[66]="_.0123456789Aa";short*s=A+12;for(;++s<A+64;)*s=s[-1]+257;

char*A="_.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

额外花费 5 个字符。

【讨论】:

  • 我认为应该包括#include&lt;string.h&gt;。否则,它不是便携式的。 (你的 short 东西也不是便携的,但至少你提供了一个便宜的替代品。)
  • size_t strspn(); 的字符数少于 #include 并且可以完成这项工作(也不需要换行符)。
  • @caf - 在许多平台上(“很多”我的意思是“我的”),size_t 仅在 &lt;stddef.h&gt; 中定义,但如果你说可移植性很糟糕,你可以 也许让它被隐式声明为返回int,因为它在许多(再次,“我的”)平台上的大小相同。
  • @strager:点了,但我认为既然我们大多数人都在假设ASCII,那么可移植性已经不在了。当然,如果它可以编译(并且至少在 MSVC++9 和 Linux gcc 4.1.2 上可以编译),就可以了吗?
【解决方案4】:

Python(181 个字符,包括换行符)

def v(E):
 import string as t;a=t.ascii_letters;e=a+"1234567890_.";t=e,e,"@",e,".",a,a,a,a,a,"",a
 for c in E:
  if c in t[0]:t=t[2:]
  elif not c in t[1]:return 0>1
 return""==t[0]

基本上只是一个使用令人困惑的短变量名的状态机。

【讨论】:

  • 您可以通过将 t 放入一个平面列表并增加 2 来删除大约 10 个字符。 t[s][1] 变成 t[s+1] 还有,最后的返回值太远了。
  • @AColie:谢谢!到目前为止,它实际上似乎将 my^H^H我们的 解决方案置于领先地位。
  • 没关系,只有当我在计数上作弊时才领先。哦,好吧。
  • 我更进一步,通过重新排列列表、将其更改为元组、消除空格、消除列表索引等。
  • 可以通过将循环内的缩进改为制表符来节省两个空格。
【解决方案5】:

C(166 个字符)

#define F(t,u)for(r=s;t=(*s-64?*s-46?isalpha(*s)?3:isdigit(*s)|*s==95?4:0:2:1);++s);if(s-r-1 u)return 0;
V(char*s){char*r;F(2<,<0)F(1=)F(3=,<0)F(2=)F(3=,<1)return 1;}

需要一个换行符,我已经算作一个字符了。

【讨论】:

  • 不错!调用参数少于声明的宏很有趣——我发现它在 MSVC++ 上编译(带有警告),但在 gcc 4.1.2 上没有。知道语言规范中“官方”允许什么?
  • @j_random_hacker:我不确定规范是怎么说的,但 gcc 根本不喜欢这段代码。将逗号放在那些有问题的宏调用(F(1=,)F(2=,))中修复了“宏 'F' 需要 2 个参数,但只有 1 个给定”错误,但我的版本(3.4.6)仍然因“之前的语法错误”而崩溃'=' 令牌”和“')' 令牌之前的语法错误”。
【解决方案6】:

Python,149 个字符(在将整个 for 循环放入一个以分号分隔的行之后,出于“可读性”的目的,我在这里没有这样做):

def v(s,t=0,o=1):
 for c in s:
   k=c=="@"
   p=c=="."
   A=c.isalnum()|p|(c=="_")
   L=c.isalpha()
   o&=[A,k|A,L,L|p,L,L,L][t]
   t+=[1,k,1,p,1,1,0][t]
 return(t>5)&o

测试用例,借自strager's answer

assert v("b@w.org")
assert v("r..t@x.tw")
assert v("j_r@x.mil")
assert not v("b@c@d.org")
assert not v("test@%.org")
assert not v("j_r@x.c.il")
assert not v("@w.org")
assert not v("test@org")
assert not v("s%p@m.org")
assert not v("foo@a%.com")
print "Yeah!"

说明:在遍历字符串时,两个变量不断更新。

t保持当前状态:

  • t = 0:我们才刚刚开始。
  • t = 1:我们在哪里开始并且找到了至少一个合法字符(字母、数字、下划线、句号)
  • t = 2:我们找到了“@
  • t = 3:我们至少在“@”之后发现了合法字符(即字母)
  • t = 4:我们在域名中找到了句号
  • t = 5:在句号后面找到一个合法字符(字母)
  • t = 6:我们在句号之后发现了至少两个合法字符

o 在“okay”中以 1 开头,即 true,一旦发现在当前状态下非法的字符,就会设置为 0。 合法字符是:

  • 在状态0:字母、数字、下划线、句点(在任何情况下都将状态更改为1
  • 在状态1:字母、数字、下划线、句点、at-sign(如果找到“@”,则将状态更改为2
  • 在状态2:字母(将状态更改为3
  • 在状态3:字母、句号(如果找到句号,则将状态更改为 4)
  • 46 的状态中:字母(在45 时的增量状态)

当我们一直遍历字符串时,我们返回 t==6t&gt;5 少一个字符)和 o 是否为 1。

【讨论】:

  • 比这里的其他 Python 解决方案短很多! +1。
【解决方案7】:

无论 C++ MSVC2008 支持什么版本。

这是我谦虚的提交。现在我知道为什么他们告诉我永远不要做我在这里做过的事情:

#define N return 0
#define I(x) &&*x!='.'&&*x!='_'
bool p(char*a) {
 if(!isalnum(a[0])I(a))N;
 char*p=a,*b=0,*c=0;
 for(int d=0,e=0;*p;p++){
  if(*p=='@'){d++;b=p;}
  else if(*p=='.'){if(d){e++;c=p;}}
  else if(!isalnum(*p)I(p))N;
  if (d>1||e>1)N;
 }
 if(b>c||b+1>=c||c+2>=p)N;
 return 1;
}

【讨论】:

  • 假设 a 正确地以 NULL 结尾。
  • 很高兴在您的答案中提供字符数以及使用的语言。
【解决方案8】:

毫无疑问,这不是最好的解决方案,而且非常冗长,但它是有效的。

已修复(所有测试用例现已通过)

    static bool ValidateEmail(string email)
{
    var numbers = "1234567890";
    var uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    var lowercase = uppercase.ToLower();
    var arUppercase = uppercase.ToCharArray();
    var arLowercase = lowercase.ToCharArray();
    var arNumbers = numbers.ToCharArray();
    var atPieces = email.Split(new string[] { "@"}, StringSplitOptions.RemoveEmptyEntries);
    if (atPieces.Length != 2)
        return false;
    foreach (var c in atPieces[0])
    {
        if (!(arNumbers.Contains(c) || arLowercase.Contains(c) || arUppercase.Contains(c) || c == '.' || c == '_'))
            return false;
    }
    if(!atPieces[1].Contains("."))
        return false;
    var dotPieces = atPieces[1].Split('.');
    if (dotPieces.Length != 2)
        return false;
    foreach (var c in dotPieces[0])
    {
        if (!(arLowercase.Contains(c) || arUppercase.Contains(c)))
            return false;
    }
    var found = 0;
    foreach (var c in dotPieces[1])
    {
        if ((arLowercase.Contains(c) || arUppercase.Contains(c)))
            found++;
        else
            return false;
    }
    return found >= 2;
}

【讨论】:

  • 也许尝试发布一个压缩的解决方案(单字符变量名,最少的空格等),这样你就可以在字符数上竞争。保留更长的时间,很高兴看到你是怎么做到的! +1
  • 刚刚注意到它没有通过 2 个测试用例!我会在几秒钟后回来更新。 :)
  • 这是什么语言?另外,您了解代码高尔夫的目的是尽可能小的程序吗? :)
  • 那将是 C#。我没有意识到这是最短的解决方案,但我只是想看看我能不能做到。看到这篇文章后,我将“code-golf”添加到了我的首选标签中。 :)
【解决方案9】:

C89 字符集不可知(262 个字符)

#include <stdio.h>

/* the 'const ' qualifiers should be removed when */
/* counting characters: I don't like warnings :) */
/* also the 'int ' should not be counted. */

/* it needs only 2 spaces (after the returns), should be only 2 lines */
/* that's a total of 262 characters (1 newline, 2 spaces) */

/* code golf starts here */

#include<string.h>
int v(const char*e){
const char*s="0123456789._abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
if(e=strpbrk(e,s))
  if(e=strchr(e+1,'@'))
    if(!strchr(e+1,'@'))
      if(e=strpbrk(e+1,s+12))
        if(e=strchr(e+1,'.'))
          if(!strchr(e+1,'.'))
            if(strlen(e+1)>1)
              return 1;
return 0;
}

/* code golf ends here */

int main(void) {
  const char *t;
  t = "b@w.org"; printf("%s ==> %d\n", t, v(t));
  t = "r..t@x.tw"; printf("%s ==> %d\n", t, v(t));
  t = "j_r@x.mil"; printf("%s ==> %d\n", t, v(t));
  t = "b@c@d.org"; printf("%s ==> %d\n", t, v(t));
  t = "test@%.org"; printf("%s ==> %d\n", t, v(t));
  t = "j_r@x.c.il"; printf("%s ==> %d\n", t, v(t));
  t = "@w.org"; printf("%s ==> %d\n", t, v(t));
  t = "test@org"; printf("%s ==> %d\n", t, v(t));
  t = "s%p@m.org"; printf("%s ==> %d\n", t, v(t));
  t = "foo@a%.com"; printf("%s ==> %d\n", t, v(t));

  return 0;
}

版本 2

仍然与 C89 字符集无关,希望已纠正错误(303 个字符;284 个不带#include)

#include<string.h>
#define Y strchr
#define X{while(Y
v(char*e){char*s="0123456789_.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
if(*e!='@')X(s,*e))e++;if(*e++=='@'&&!Y(e,'@')&&Y(e+1,'.'))X(s+12,*e))e++;if(*e++=='.'
&&!Y(e,'.')&&strlen(e)>1){while(*e&&Y(s+12,*e++));if(!*e)return 1;}}}return 0;}

#define X 实在是太恶心了!

测试我的第一个(错误)版本。

【讨论】:

  • 而不是if链,为什么不是a,&&/||链?这应该会删除相当多的字符。
  • 似乎我们想出了相同的想法,即使用单个字符串的后缀作为 str...() 函数的参数...实际上,在看到您的代码后,我注意到我的代码中有一个错误!
【解决方案10】:

VBA/VB6 - 484 个字符

显式关闭
用法:VE("b@w.org")

Function V(S, C)
V = True
For I = 1 To Len(S)
 If InStr(C, Mid(S, I, 1)) = 0 Then
  V = False: Exit For
 End If
Next
End Function

Function VE(E)
VE = False
C1 = "abcdefghijklmnopqrstuvwxyzABCDEFGHILKLMNOPQRSTUVWXYZ"
C2 = "0123456789._"
P = Split(E, "@")
If UBound(P) <> 1 Then GoTo X
If Len(P(0)) < 1 Or Not V(P(0), C1 & C2) Then GoTo X
E = P(1): P = Split(E, ".")
If UBound(P) <> 1 Then GoTo X
If Len(P(0)) < 1 Or Not V(P(0), C1) Or Len(P(1)) < 2 Or Not V(P(1), C1) Then GoTo X
VE = True
X:
End Function

【讨论】:

    【解决方案11】:

    Java257 个字符(为了便于阅读,不包括 3 行结尾 ;-))。

    boolean q(char[]s){int a=0,b=0,c=0,d=0,e=0,f=0,g,y=-99;for(int i:s)
    d=(g="@._0123456789QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm".indexOf(i))<0?
    y:g<1&&++e>0&(b<1|++a>1)?y:g==1&e>0&(c<1||f++>0)?y:++b>0&g>12?f>0?d+1:f<1&e>0&&++c>0?
    d:d:d;return d>1;}
    

    通过所有测试(我的旧版本不正确)。

    【讨论】:

      【解决方案12】:

      Erlang 266 个字符:

      -module(cg_email).
      
      -export([test/0]).
      
      %%% golf code begin %%%
      -define(E,when X>=$a,X=<$z;X>=$A,X=<$Z).
      -define(I(Y,Z),Y([X|L])?E->Z(L);Y(_)->false).
      -define(L(Y,Z),Y([X|L])?E;X>=$0,X=<$9;X=:=$.;X=:=$_->Z(L);Y(_)->false).
      ?L(e,m).
      m([$@|L])->a(L);?L(m,m).
      ?I(a,i).
      i([$.|L])->l(L);?I(i,i).
      ?I(l,c).
      ?I(c,g).
      g([])->true;?I(g,g).
      %%% golf code end %%%
      
      test() ->
        true  = e("b@w.org"),
        false = e("b@c@d.org"),
        false = e("test@%.org"),
        false = e("j_r@x.c.il"),
        true  = e("r..t@x.tw"),
        false = e("test@org"),
        false = e("s%p@m.org"),
        true  = e("j_r@x.mil"),
        false = e("foo@a%.com"),
        ok.
      

      【讨论】:

        【解决方案13】:

        Ruby225 个字符。 这是我的第一个 Ruby 程序,所以它可能不太像 Ruby :-)

        def v z;r=!a=b=c=d=e=f=0;z.chars{|x|case x when'@';r||=b<1||!e;e=!1 when'.'
        e ?b+=1:(a+=1;f=e);r||=a>1||(c<1&&!e)when'0'..'9';b+=1;r|=!e when'A'..'Z','a'..'z'
        e ?b+=1:f ?c+=1:d+=1;else r=1 if x!='_'||!e|!b+=1;end};!r&&d>1 end
        

        【讨论】:

          【解决方案14】:

          '不使用正则表达式': PHP 47 个字符。

          <?=filter_var($argv[1],FILTER_VALIDATE_EMAIL);
          

          【讨论】:

            【解决方案15】:

            Haskell (GHC 6.8.2), 165 161 144C 字符


            使用模式匹配,elemspanall

            a=['A'..'Z']++['a'..'z']
            e=f.span(`elem`"._0123456789"++a)
            f(_:_,'@':d)=g$span(`elem`a)d
            f _=False
            g(_:_,'.':t@(_:_:_))=all(`elem`a)t
            g _=False
            

            上面是用下面的代码测试的:

            main :: IO ()
            main = print $ and [
              e "b@w.org",
              e "r..t@x.tw",
              e "j_r@x.mil",
              not $ e "b@c@d.org",
              not $ e "test@%.org",
              not $ e "j_r@x.c.il",
              not $ e "@w.org",
              not $ e "test@org",
              not $ e "s%p@m.org",
              not $ e "foo@a%.com"
              ]
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2013-01-18
              • 2016-11-15
              • 2023-04-05
              • 2017-01-12
              • 2014-03-02
              • 2016-10-09
              相关资源
              最近更新 更多