Code Golf: проверка адреса электронной почты без регулярных выражений

(Правка: что такое Code Golf: Code Golf - это задачи для решения конкретной проблемы с минимальным количеством кодов по количеству символов на любом языке, который вы предпочитаете. Подробнее о Meta Stackru.)

Code Golfers, вот вызов на строковых операциях.

Проверка адреса электронной почты, но без регулярных выражений (или подобной библиотеки разбора), конечно. Речь идет не столько об адресах электронной почты, сколько о том, как быстро вы можете написать различные строковые операции и ограничения, приведенные ниже.

Правила следующие (да, я знаю, это не соответствует RFC, но это будет 5 правил для этой задачи):

  • Как минимум 1 символ из этой группы до @:

    A-Z, a-z, 0-9, . (period), _ (underscore)
    
  • @ должен существовать ровно один раз

    john@smith.com
        ^
    
  • Точка (.) Должна существовать ровно один раз после @

    john@smith.com
              ^
    
  • Как минимум 1 только [AZ, az] символ между @ и следующим. (Период)

    john@s.com
         ^
    
  • Как минимум 2 символа [AZ, az] после финала. период

    john@smith.ab
               ^^
    

Пожалуйста, опубликуйте только метод / функцию, которая будет принимать строку (предлагаемый адрес электронной почты), а затем возвращать логический результат (true / false) в зависимости от того, является ли адрес электронной почты действительным (true) или недействительным (false).

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)

Удачи!

15 ответов

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!");
}

J

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

С89, 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()поэтому я чувствую, что этот ответ не такой "чистый", как ответ Страгера, который обходится без каких-либо библиотечных функций. (Я также украл его идею объявления глобальной переменной без типа!)

Один из приемов заключается в том, что . а также _ в начале строки Aих можно легко включить или исключить в strspn() test: когда вы хотите разрешить их, используйте strspn(something, A); когда вы не используете strspn(something, A+12), Другой предполагает, что sizeof (short) == 2 * sizeof (char)и создание массива допустимых символов 2 за один раз из пары "семя" Aa, Остальные просто искали способ заставить подвыражения выглядеть достаточно похожими, чтобы их можно было вытащить в #defineМакросы.

Чтобы сделать этот код более "переносимым" (хе:-P), вы можете изменить код построения массива с

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

в

char*A="_.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

за стоимость 5 дополнительных символов.

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]

В основном просто конечный автомат, использующий запутанно короткие имена переменных.

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;}

Требуется один символ новой строки, и я посчитал его одним символом.

Питон, 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

Тестовые случаи, заимствованные из ответа Страгера:

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 как в "хорошо" начинается как 1, т. е. истина, и устанавливается в 0 как только найден персонаж, который является недопустимым в текущем состоянии. Юридические символы:

  • В состоянии 0: буква, число, подчеркивание, точка (изменить состояние на 1 в любом случае)
  • В состоянии 1: буква, число, подчеркивание, точка, знак подписи (изменить состояние на 2 если "@" найден)
  • В состоянии 2: письмо (изменить состояние на 3)
  • В состоянии 3: письмо, период (изменить состояние на 4, если найден период)
  • В штатах 4 через 6: буква (состояние приращения, когда в 4 или же 5)

Когда мы прошли весь путь через строку, мы возвращаем t==6 (t>5 на один символ меньше) и o это 1.

Не самое лучшее решение, без сомнения, и чертовски милое, но оно действительно.

Исправлено (все тесты пройдены сейчас)

    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;
}

Какую бы версию 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;
}

Набор символов 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 абсолютно отвратителен!

Тест как для моей первой (глючной) версии.

Java: 257 символов (не считая 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;}

Проходит все тесты (моя старая версия была неверной).

Haskell (GHC 6.8.2), 165 161 144C символов


Используя сопоставление с образцом, elem, span а также all:

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"
  ]

Эрланг 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.

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

Рубин, 225 символов. Это моя первая программа на 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

"Использование без регулярных выражений": PHP 47 символов.

<?=filter_var($argv[1],FILTER_VALIDATE_EMAIL);
Другие вопросы по тегам