问题来源
在开发过程中,经常会遇到一些数据校验的问题,比如我们必须从屏幕字段、Web Dynpro 输入字段或上传文件的文本中验证电子邮件地址。验证可确保您具有有效的电子邮件格式以进行进一步处理,例如发送通知或将其存储在数据库中。
这次在实际业务中,用户比较看重邮件的正确性,如果是 SAP 的数据元素 AD_SMTPADR
就是带标准的校验功能的。
如果我们输入不带 @
符号的邮箱,就会在标准错误中得到如下的提示:
消息号 XS138:电子邮箱地址 XXXsam.com 无效
通过 DEBUG 可以看到这段程序:
IF ADDRESS_UNSTRUCT-ADDRESS NP '*@*' AND COMPLETE_ADDRESS = SX_TRUE.
MESSAGE E138 WITH ADDRESS_UNSTRUCT-ADDRESS ADDRESS_UNSTRUCT-TYPE
TEXT-028 TEXT-039
RAISING ERROR_ADDRESS.
ENDIF.
解读:从上面的代码可以看出有一个条件是查看 ADDRESS_UNSTRUCT-ADDRESS NP '*@*'
,它检查 ADDRESS_UNSTRUCT-ADDRESS
这个邮箱变量的值是否包含字符串 @
。 如果包含就报出 E138
这个错误。
这段校验会有缺陷,当我们输入一个不带域名的邮箱时,不会得到系统的错误:
正则表达式
关于电子邮箱的正则表达式有很多,具体可以看一下这个网站提供了一些常见的正则库:RegExLib。
这里使用一种常见的电子邮箱的正则表达式:w+(.w+)*@(w+.)+(w{2,4})
w
表示匹配任意字母、数字、下划线+
表示匹配前面的元素至少出现一次*
表示匹配前面的元素任意次
接下来解释一下这个正则模式:
w+(.w+)
: @ 前面的第一部分将接受任何字母、数字字符或带或不带 (.
) 点的字符。这部分w+
匹配多个字母或数字。(.w+)
验证是否有一个点,且.
后面必须跟一个字符。(w+.)
:@ 后面的部分允许灵活地使用任何名称(后面带有 (.
) 点)。这一部分(w+.)
确保结束部分之前至少有一个域。如果有多个,它将检查域的每个部分后面的 (.
) 点。(w{2,4})
:最后一部分确保电子邮件在最后一个点之后至少有 2 个字符,最多 4 个字符。
此外,你可以在 regerxr.com 中找到每种模式的解释
问题解决
SAP 提供了一个非常有用的API,让您可以使用正则表达式验证电子邮件地址。使用 ABAP RegEx
实用程序类 CL_ABAP_REGEX
和 CL_ABAP_MATCHER
验证电子邮件。
验证电子邮件的步骤如下:
- 首先,编写一个与电子邮件地址匹配的正则表达式。
- 然后,创建一个类的实例
cl_abap_regex
,该实例具有名为regex
对象的电子邮件地址模式。 - 最后,从上面的
regex
对象创建一个matcher
对象,它是 class 的实例cl_abap_matcher
,并调用 匹配对象的match()
方法。如果方法的返回值match()
不为空,则电子邮件地址有效。否则无效。
REPORT zvalidateemail.
DATA: go_regex TYPE REF TO cl_abap_regex,
go_matcher TYPE REF TO cl_abap_matcher,
go_match TYPE c LENGTH 1,
gv_msg TYPE string.
PARAMETERS: p_email TYPE ad_smtpadr.
START-OF-SELECTION.
CREATE OBJECT go_regex
EXPORTING
pattern = 'w+(.w+)*@(w+.)+(w{2,4})'
ignore_case = abap_true.
go_matcher = go_regex->create_matcher( text = p_email ).
IF go_matcher->match( ) IS INITIAL.
gv_msg = 'Email address is invalid'.
ELSE.
gv_msg = 'Email address is valid'.
ENDIF.
MESSAGE gv_msg TYPE 'I'.
类实现
CLASS lcl_mail DEFINITION.
PUBLIC SECTION.
METHODS validate_email_id
IMPORTING
iv_email TYPE ad_smtpadr
RETURNING
VALUE(RESULT) TYPE flag .
ENDCLASS. "lcl_mail DEFINITION
*
START-OF-SELECTION.
DATA: lo_mail TYPE REF TO lcl_mail.
CREATE OBJECT lo_mail.
IF lo_mail->validate_email_id( 'szdfd@' ) IS INITIAL.
WRITE: 'Invalid Email'.
ELSE.
WRITE: 'Valid'.
ENDIF.
*
CLASS lcl_mail IMPLEMENTATION.
METHOD validate_email_id.
* Local Data
DATA: REGEX TYPE REF TO cl_abap_regex, " Regex Object
matcher TYPE REF TO cl_abap_matcher, " Matcher Object
MATCH TYPE c LENGTH 1, " Match ?
mail_to_check(100). " Email ID to check
mail_to_check = iv_email.
RESULT = abap_true. "Email is valid
* Instntiate Regex
CREATE OBJECT REGEX
EXPORTING
pattern = 'w+(.w+)*@(w+.)+(w{2,4})'
ignore_case = abap_true.
* Create the Matcher
matcher = regex->create_matcher( text = mail_to_check ).
* Match not found, invalid
IF matcher->MATCH( ) IS INITIAL.
RESULT = abap_false. "Email not valid
EXIT.
ENDIF.
ENDMETHOD. "validate_email_id
ENDCLASS. "lcl_mail IMPLEMENTATION
官方实现
程序通过将输入的电子邮件地址与正则表达式进行比较,检查其形式上的正确性。
- 第一个正则表达式检查不含特殊字符的标准电子邮件地址,而第二个正则表达式则根据 RFC 822 执行更宽松的语法检查。
- 即使是使用相对简单的正则表达式作为示例的第二种检查,也并非总是与 RFC 822 规定的所有电子邮件地址兼容。
DEMO_VALIDATE_RFC_822_ADDRESS
程序使用的正则表达式来自互联网,旨在识别 RFC 822 允许的所有电子邮件地址。这里的正则表达式最初是为 Perl 编写的,有 6000 多个字符。因此,该程序是如何在 ABAP 中不使用正则表达式的一个示例。
REPORT demo_matches.
CLASS demo DEFINITION.
PUBLIC SECTION.
CLASS-METHODS main.
ENDCLASS.
CLASS demo IMPLEMENTATION.
METHOD main.
DATA email TYPE string VALUE `abc.def@ghi.jkl`.
cl_demo_input=>request( CHANGING field = email ).
IF matches( val = email
regex = `w+(.w+)*@(w+.)+(([a-z]|[A-Z]){2,4})` )
##REGEX_POSIX.
cl_demo_output=>display( 'Format OK' ).
ELSEIF matches(
val = email
pcre = `[[:alnum:],!#$%&'*+/=?^_``{|}~-]+` &
`(.[[:alnum:],!#$%&'*+/=?^_``{|}~-]+)*` &
`@[[:alnum:]-]+(.[[:alnum:]-]+)*` &
`.([[:alpha:]]{2,})` ).
cl_demo_output=>display( 'Syntax OK but unusual' ).
ELSE.
cl_demo_output=>display( 'Wrong format!' ).
ENDIF.
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
demo=>main( ).
使用方式
DATA: go_regex TYPE REF TO cl_abap_regex,
go_matcher TYPE REF TO cl_abap_matcher,
go_match TYPE c LENGTH 1.
CREATE OBJECT go_regex
EXPORTING
pattern = '([a-zA-Z0-9_-.]+)@(([[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.)|(([a-zA-Z0-9-]+.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(]?)'
ignore_case = abap_true.
IF gv_smtp_addr IS NOT INITIAL.
go_matcher = go_regex->create_matcher( text = gv_smtp_addr ).
IF go_matcher->match( ) IS INITIAL.
CLEAR:gv_msg.
gv_msg = |Email address is invalid|.
MESSAGE s001(00) WITH gv_msg DISPLAY LIKE 'E'.
p_error = 'X'.
RETURN.
ENDIF.
ENDIF.
IF gv_appli_email IS NOT INITIAL.
go_matcher = go_regex->create_matcher( text = gv_appli_email ).
IF go_matcher->match( ) IS INITIAL.
CLEAR:gv_msg.
gv_msg = |Vendor Applicant Email address is invalid|.
MESSAGE s001(00) WITH gv_msg DISPLAY LIKE 'E'.
p_error = 'X'.
RETURN.
ENDIF.
ENDIF.