我应该如何解析 expl3 中的 ISO 日期?

我应该如何解析 expl3 中的 ISO 日期?

假设date是 ISO 格式的(YYYY-MM-DD),我如何将其各个部分解析为变量?

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\int_new:N \mytest_date_day_int
\int_new:N \mytest_date_month_int
\int_new:N \mytest_date_year_int

\keys_define:nn { note-verbale } {
  date .code:n = { },           % ???
}

\NewDocumentCommand \Information { m } {
  \keys_set:nn { note-verbale } { #1 }
}

\NewDocumentCommand \PrintDate { } {
  Date:\nobreakspace

  \int_use:N \mytest_date_day_int
  \slash
  \int_use:N \mytest_date_month_int
  \slash
  \int_use:N \mytest_date_year_int
}

\ExplSyntaxOff

\Information{
  date = 2015-02-10,
}

\begin{document}
\PrintDate 
\end{document}

答案1

改进您的代码:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\int_new:N \mytest_date_day_int
\int_new:N \mytest_date_month_int
\int_new:N \mytest_date_year_int

\cs_new_protected:Npn \mytest_date_parse:n #1
 {
  \__mytest_date_parse:www #1 \q_stop
 }
\cs_new_protected:Npn \__mytest_date_parse:www #1-#2-#3 \q_stop
 {
  \int_set:Nn \mytest_date_year_int  {#1}
  \int_set:Nn \mytest_date_month_int {#2}
  \int_set:Nn \mytest_date_day_int   {#3}
 }

\keys_define:nn { my-test } {
  date .code:n = { \mytest_date_parse:n { #1 } },
}

\NewDocumentCommand \Information { m } {
  \keys_set:nn { my-test } { #1 }
}

\NewDocumentCommand \PrintDate { } {
  Date:\nobreakspace

  \int_use:N \mytest_date_day_int
  \slash
  \int_use:N \mytest_date_month_int
  \slash
  \int_use:N \mytest_date_year_int
}

\ExplSyntaxOff

\Information{
  date = 2015-02-10,
}

\begin{document}
\PrintDate 
\end{document}

最好将.code:n位与实际实现分开。没有必要用六个参数定义解析器宏,因为三个参数是预先确定的。

不要忘记执行分配的函数应该是protected

这里检查日期是否为 ISO 格式:YYYY-MM-DD(但允许月份和年份为一位数字);这l3regex是最简单的工具。如果日期无效,0/0/0则会打印,但很容易添加合适的错误消息。

\documentclass{article}
\usepackage{xparse,l3regex}

\ExplSyntaxOn

\int_new:N \mytest_date_day_int
\int_new:N \mytest_date_month_int
\int_new:N \mytest_date_year_int

\cs_new_protected:Npn \mytest_date_parse:n #1
 {
  \regex_match:nnTF { \A \d{4}-\d{1,2}-\d{1,2} \Z } { #1 }
   {
    \__mytest_date_parse:www #1 \q_stop
   }
   {
    \__mytest_date_parse:www 0000-00-00 \q_stop
   }
 }
\cs_new:Npn \__mytest_date_parse:www #1-#2-#3 \q_stop
 {
  \int_set:Nn \mytest_date_year_int  {#1}
  \int_set:Nn \mytest_date_month_int {#2}
  \int_set:Nn \mytest_date_day_int   {#3}
 }

\keys_define:nn { my-test } {
  date .code:n = { \mytest_date_parse:n {#1} },
}

\NewDocumentCommand \Information { m } {
  \keys_set:nn { my-test } { #1 }
}

\NewDocumentCommand \PrintDate { } {
  Date:\nobreakspace

  \int_use:N \mytest_date_day_int
  \slash
  \int_use:N \mytest_date_month_int
  \slash
  \int_use:N \mytest_date_year_int
}

\ExplSyntaxOff

\Information{
  date = 2015-02-10,
}

\begin{document}
\PrintDate 

\Information{date=201-1-1}
\PrintDate

\Information{date=2015-1}
\PrintDate

\Information{date=2015/1/1}
\PrintDate

\end{document}

该正则表达式的意思是:整个字符串(\A\Z代表开始和结束)应由四位数字(\d{4})、一个连字符、一到两位数字\d{1,2}、一个连字符以及一到两位数字组成。任何其他格式都将返回 false。


另一个版本也接受date=20150101,即一串八位数字,并将解析分为两种情况。

\documentclass{article}
\usepackage{xparse,l3regex}

\ExplSyntaxOn

\int_new:N \mytest_date_day_int
\int_new:N \mytest_date_month_int
\int_new:N \mytest_date_year_int

\cs_new_protected:Npn \mytest_date_validate_parse:n #1
 {
  \regex_match:nnTF { \A \d{4}-\d{1,2}-\d{1,2} \Z } { #1 }
   {
    \__mytest_date_parse:www #1 \q_stop
   }
   {
    \regex_match:nnTF { \A \d{8} \Z } { #1 }
     {
      \__mytest_date_parse:nnnnnnnn #1
     }
     {
      \__mytest_date_parse:nnnnnnnn 00000000
     }
   }
 }

\cs_new:Npn \__mytest_date_parse:www #1-#2-#3 \q_stop
 {
  \int_set:Nn \mytest_date_year_int  {#1}
  \int_set:Nn \mytest_date_month_int {#2}
  \int_set:Nn \mytest_date_day_int   {#3}
 }

\cs_new:Nn \__mytest_date_parse:nnnnnnnn
 {
  \int_set:Nn \mytest_date_year_int  {#1#2#3#4}
  \int_set:Nn \mytest_date_month_int {#5#6}
  \int_set:Nn \mytest_date_day_int   {#7#8}
 }

\keys_define:nn { my-test } {
  date .code:n = { \mytest_date_validate_parse:n {#1} },
}

\NewDocumentCommand \Information { m } {
  \keys_set:nn { my-test } { #1 }
}

\NewDocumentCommand \PrintDate { } {
  Date:\nobreakspace

  \int_use:N \mytest_date_day_int
  \slash
  \int_use:N \mytest_date_month_int
  \slash
  \int_use:N \mytest_date_year_int
}

\ExplSyntaxOff

\Information{
  date = 2015-02-10,
}

\begin{document}
Good: \PrintDate 

\Information{date=201-1-1}%bad
Bad: \PrintDate

\Information{date=2015-1}%bad
Bad: \PrintDate

\Information{date=2015/1/1}%bad
Bad: \PrintDate

\Information{date=20150101}%good
Good: \PrintDate

\Information{date=201511}%bad
Bad: \PrintDate

\Information{date=201501012}%bad
Bad: \PrintDate

\end{document}

在此处输入图片描述

答案2

您可以使用解析器函数并在中引入夸克date .code:n。请注意,夸克\q_stop用于限定最后一个参数的长度。

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\int_new:N \mytest_date_day_int
\int_new:N \mytest_date_month_int
\int_new:N \mytest_date_year_int

\cs_new:Npn \mytest_date_parse:wNNN #1-#2-#3 \q_stop #4 #5 #6
 {
  \int_set:Nn #4 {#1}
  \int_set:Nn #5 {#2}
  \int_set:Nn #6 {#3}
 }

\keys_define:nn { my-test } {
  date .code:n = {
  \mytest_date_parse:wNNN
    #1 \q_stop
    \mytest_date_day_int
    \mytest_date_month_int
    \mytest_date_year_int
  },
}

\NewDocumentCommand \Information { m } {
  \keys_set:nn { my-test } { #1 }
}

\NewDocumentCommand \PrintDate { } {
  Date:\nobreakspace

  \int_use:N \mytest_date_day_int
  \slash
  \int_use:N \mytest_date_month_int
  \slash
  \int_use:N \mytest_date_year_int
}

\ExplSyntaxOff

\Information{
  date = 2015-02-10,
}

\begin{document}
\PrintDate 
\end{document}

相关内容