regex 如何修复此正则表达式模式?

ie3xauqp  于 2023-05-30  发布在  其他
关注(0)|答案(4)|浏览(155)

我试图创建一个正则表达式模式来捕获我的信用卡发票信息,我只能以PDF格式获得。我将文本复制到文本编辑器,然后使用notepad++中的替换工具将复制的文本转换为CSV文本。
我遇到了负值的问题。
给定这段文本:

16/04 RC GRACAS 2 - 0,02
SAÚDE .RECIFE

16/04 RC GRACAS 2 02/03 45,97
SAÚDE .RECIFE

上面的文本包含2个清单数据的数据。正则表达式应该在第一个条目中捕获以下组:

"16/04": date
"RC GRACAS 2": description
"-": value sign
"0,02": value
"SAÚDE .RECIFE": categories

以及第二个条目中的以下组:

"16/04": date
"RC GRACAS 2 02/03": description
"": value sign
"45,97": value
"SAÚDE .RECIFE": categories

我现在的正则表达式是这样的:^(\d{2}/\d{2})\s+(.*)\s+([-+]?)\s?(\d{1,3}(?:\.\d{3})*(?:,\d+)?)\s+(.*)?
我遇到的问题是,在第一次购买中,正则表达式无法捕获减号,它成为第二组(描述)的一部分。
我怎样才能改变这个正则表达式来在它自己的组中捕获那个符号?

bmvo0sr5

bmvo0sr51#

使用 * reliable * 量词.*?,它匹配尽可能少的 * 字符,并使用[\r\n]匹配新行:

^(\d\d\/\d\d)\s+(.*?)\s+([-+])?\s?(\d{1,3}(?:\.\d{3})*(?:,\d+)?)[\r\n]+(.*)?

参见live demo

olhwl3o2

olhwl3o22#

问题是描述组太一般化(并且也匹配下一个组的模式),并且符号组是可选的,因此它被描述捕获。真正造成问题的是,在符号组的右侧还有另一个可选的.*组。
您可以通过对正则表达式进行两个简单的更改来解决这个问题。第一种是使描述组变懒(通过在*之后添加?)。第二个是在表达式中添加一个结束行$

^(\d{2}/\d{2})\s+(.*?)\s+([-+]?)\s?(\d{1,3}(?:\.\d{3})*(?:,\d+)?)\s+(.*)?$
                    ^                                                    ^

对惰性组的更改可以防止描述字段传递到下一个组中,并且结束行为表达式添加了更多的结构,从而允许惰性工作。

kx1ctssn

kx1ctssn3#

.匹配所有内容,包括-+。如果你的描述保证没有连字符和加号,你可以通过将第二组改为([^-+]*)来防止它们与这两个匹配:

^
(\d{2}/\d{2})\s+
([^-+]*)\s+
([-+]?)\s?
(\d{1,3}(?:\.\d{3})*(?:,\d+)?)\s+
(.*)?

试试on regex101.com
或者,我的建议是:

^                                # Match at the start of line
(?<date>\d{2}/\d{2})             # a date,
(?:                              # a description
  \s+(?<description>.*?)         # consists of at least some spaces
)??                              # (optional, lazily matched)
(?:                              # 
  \s+                            # some other spaces
  (?:(?<value_sign>[-+])\s)?     # then a sign and a space, collectively optional,
  (?<value>                      # followed by a value
    \d{1,3}(?:\.\d{3})*(?:,\d+)? # (which is a number)
  )                              # 
)                                # 
\s*$                             # right before the end of line,
\n                               # after which is a new line
(?<categories>.*)                # containing categories.

试试on regex101.com

bq8i3lrv

bq8i3lrv4#

可以使用以下正则表达式。

(?x)                        # invoke free-spacing mode
^                           # match beginning of line
(?<date>\d{2}\/\d{2})       # match 2 digits, '/', 2 digits and save to capture
                            # group 'date'
[ ]+                        # match 1 or more spaces, as many as possible

(?<description>[\w ]*\w(?:[ ]+\d{2}\/\d{2})?)
                            # match zero or more word chars or spaces, as many as
                            # possible, followed by a word char,  optionally
                            # followed by a one or more spaces, 2 digits, '/', 2 digits,
                            # save to capture group 'description'
[ ]+                        # match 1 or more spaces, as many as possible
(?<value_sign>[-+]|)        # match '-', '+' or an empty space, save to caputure
                            # group 'value_sign' 
[ ]+                        # match 1 or more spaces, as many as possible
(?<value>\d+,\d{2})         # match 1 or more digits, ',', 2 digits, save to capture
                            # group 'value'
\r?\n                       # match a line feed optionally preceded by a carriage
                            # return (for Windows support)
(?<categories>\S.*)         # match a non-whitespace character followed by
                            # zero or more characters other than line
                            # terminators, as many as possible, save to
                            # capture group 'categories'

Demo
如果未指定自由间距模式,则将

^(?<date>\d{2}\/\d{2}) +(?<description>[\w ]*\w(?: +\d{2}\/\d{2})?) +(?<value_sign>[-+]|) +(?<value>\d+,\d{2})\r?\n(?<categories>\S.*)

如果使用编号的捕获组,则这将是

^(\d{2}\/\d{2}) +([\w ]*\w(?: +\d{2}\/\d{2})?) +([-+]|) +(\d+,\d{2})\r?\n(\S.*)

相关问题