Javascript 正则表达式能否匹配字符串开头或结尾的字符(但不能同时匹配两者)?

Javascript 正则表达式能否匹配字符串开头或结尾的字符(但不能同时匹配两者)?,javascript,regex,Javascript,Regex,我正在编写一个正则表达式来验证欧元货币字符串。它允许几种不同的格式,因为一些地区使用小数点表示数千个分隔符,一些地区使用空格,一些地区将欧元放在开头,一些地区将欧元放在结尾。以下是我的想法: /^(€?)?\-([1-9]{1,3}(\d{3})*.[1-9]{1,3}(\.\d{3})*.-(0 |([1-9]\d*))(,[0-9]{2})(?)$/ 这适用于以下测试: 有效: 123456,78 123.456,78 6.954.231欧元 896.954.231欧元 16.954.231

我正在编写一个正则表达式来验证欧元货币字符串。它允许几种不同的格式,因为一些地区使用小数点表示数千个分隔符,一些地区使用空格,一些地区将欧元放在开头,一些地区将欧元放在结尾。以下是我的想法:

/^(€?)?\-([1-9]{1,3}(\d{3})*.[1-9]{1,3}(\.\d{3})*.-(0 |([1-9]\d*))(,[0-9]{2})(?)$/

这适用于以下测试:

有效:

123456,78
123.456,78
6.954.231欧元
896.954.231欧元
16.954.231欧元
12346954231欧元
10,03欧元
10,03
1,39
,03
0,10
10567,01欧元
0.01欧元
1234567,89欧元
1.234.567,89欧元

无效

1234 1.1欧元
50#,50
123,欧元
500欧元
0001
欧元,001
0001欧元
12.34,56
123456.123.123456

这样做的一个问题是,它验证两端带有欧元符号的字符串,例如123欧元。这对于我来说可能是可以接受的,但是有没有一种方法可以生成一个只允许该字符在一端,而不允许在两端都使用的紧凑正则表达式,或者我只需要编写一个两倍长的正则表达式,首先检查开头带有可选欧元的有效字符串,然后检查结尾带有可选欧元的有效字符串

更新
被接受的答案中的那个仍然有一些误报。最后,我编写了一个函数,该函数采用多个选项来定制验证器。这是中的
isCurrency
函数。仍然使用前瞻来避免某些边缘情况,这是回答此问题的关键。

根据您的正则表达式引擎,您可能可以使用负前瞻来实现这一点

^€(?!(.*€))

您可以将正则表达式分成两部分,并将它们与“|”组合。 一个以欧元起价,另一个以欧元结束

/(^(€ ?)?\-?([1-9]{1,3}( \d{3})*|[1-9]{1,3}(\.\d{3})*|(0|([1-9]\d*)?))(,[0-9]{2})?$)|(^\-?([1-9]{1,3}( \d{3})*|[1-9]{1,3}(\.\d{3})*|(0|([1-9]\d*)?))(,[0-9]{2})?( ?€)?$)/
编辑:

我错过了你的最后一句话。
我认为最简单的方法是编写两倍长的正则表达式。

有了lookahead,这就行了

^(?!€*$)(€ ?(?!.*€)(?=,?\d))?\-?([1-9]{1,3}( \d{3})*|[1-9]{1,3}(\.\d{3})*|(0|([1-9]\d*)?))(,[0-9]{2})?( ?€)?$
见:


@Necreaux首先指出了前瞻性,这是值得称赞的

您可以使用此模式:

^
(?=(.))          # you capture the first character in a lookahead
(?:€[ ]?)?
(?:
    [1-9][0-9]{0,2}
    (?:
        ([ .]) [0-9]{3} (?: \2 [0-9]{3})*
      |
        [0-9]*
    )
    (?:,[0-9]{2})?
  |
    0?,[0-9]{2}
)

(?:
    [ ]?
    (?!\1)€   # you test if the first character is not an €
)?
$

我们的想法是捕捉第一个角色,并在最后测试它是否不一样

/(^(€ ?)?\-?([1-9]{1,3}( \d{3})*|[1-9]{1,3}(\.\d{3})*|(0|([1-9]\d*)?))(,[0-9]{2})?$)|(^\-?([1-9]{1,3}( \d{3})*|[1-9]{1,3}(\.\d{3})*|(0|([1-9]\d*)?))(,[0-9]{2})?( ?€)?$)/
要将其与javascript一起使用,需要删除格式:

var re = /^(?=(.))(?:€ ?)?(?:[1-9][0-9]{0,2}(?:([ .])[0-9]{3}(?:\2[0-9]{3})*|[0-9]*)(?:,[0-9]{2})?|0?,[0-9]{2})(?: ?(?!\1)€)?$/;
关于这一点:唯一感兴趣的是短处。如果您想要获得性能,最好的方法是逐字写出两种可能性:

var re = /^(?:€ ?(?:[1-9][0-9]{0,2}(?:([ .])[0-9]{3}(?:\1[0-9]{3})*|[0-9]*)(?:,[0-9]{2})?|0?,[0-9]{2})|(?:[1-9][0-9]{0,2}(?:([ .])[0-9]{3}(?:\2[0-9]{3})*|[0-9]*)(?:,[0-9]{2})?|0?,[0-9]{2})(?: ?€)?)$/;
它的编写时间更长,但它减少了正则表达式引擎的工作

对于支持条件子模式(如PCRE)的正则表达式引擎,您可以编写以下代码:

\A
(€ ?)?
(?:
    [1-9][0-9]{0,2}
    (?: ([ .]) [0-9]{3} (?:\2[0-9]{3})* | [0-9]*)
    (?:,[0-9]{2})?
  | 
    0?,[0-9]{2}
)
(?(1)| ?€)
\z

其中,
(?(1)|?€)
是一个if..then..else:
(?(条件)true | false)
,用于检查是否定义了捕获组1。

这是我能得到的最接近的结果。它使用负前瞻确保字符串不以欧元符号开头和结尾。

^(?!€.*€$)€?\s*(0|[1-9][0-9]{0,2})?([. ]?[0-9]{3})*(,[0-9]{2})?\s*€?$
获取完整解释和示例。正如您所见,它通过了所有测试,但也通过了一些不好的测试。我相信数字部分可以调整,使其适合您。确保没有两个欧元符号的部分是:

^(?!€.*€$)€?\s*<digit validation goes here>\s*€?$
^(?)€?\s*\s*€$

负前瞻确保字符串不以欧元符号开头和结尾,然后在开头检查可选的欧元符号,后跟任意的空格,验证数字,然后检查末尾是否有任意的空格和欧元符号。

您使用哪种语言?请查看并相应地更新您的问题。用JavaScript实现这一点。如果在其他语言中可行,我很想知道。这是我的备份计划。你的测试对我所有的测试都有效,加上123欧元。这只能确保它从…开始。。。OP希望接受以€开头或结尾的字符串,但不能同时接受两者。@davidaber这将在结尾处使用负向后看来完成,或者如果不是欧元符号,则第一个数字也可以使用正向前看。但是,完整正则表达式可能不支持负lookback。@davidaber lookback是多余的,因为一旦正则表达式开始计算字符串的最后一个字符,您就可以肯定前面没有欧元符号,或者,如果有,因为已经执行了前瞻操作,所以结尾将不会出现欧元符号。@gijswijs好吧,我有答案的开头,很好的完成=)适用于我的所有测试。模式匹配一个单一的欧元符号
@casimirithippolyte你到底想要什么?在前瞻中?我指的是一个单一的欧元符号,没有数字。换句话说,字符串“€”匹配整个模式。这是真的。本来是个问题。我不知道为什么。这是失败的“123456,78”为我。我这样做的是这样的,我这样做的这样这样的:<代码>/^((<代码>///)(((<代码>>///)以下以下以下以下我这样的我这样的我这样的我这样的我这样的我这样的我这样的我这样的我这样的我这样的我这样的我这样的:::::<代码>/(((::::::::::((((<代码>>//////////////))(((((::::::::::::::::::::::::::::::::::::::)))))(((((((((((:::::::::::::::::::::::::::::::::::::::::::::::::::::::[0-9]{2}(2[0-9]{3})*(0-9]*)(?:,[0-9]{2})?0?,[0-9]{2})(?:[](?!\1)€)$/
/(^(€ ?)?\-?([1-9]{1,3}( \d{3})*|[1-9]{1,3}(\.\d{3})*|(0|([1-9]\d*)?))(,[0-9]{2})?$)|(^\-?([1-9]{1,3}( \d{3})*|[1-9]{1,3}(\.\d{3})*|(0|([1-9]\d*)?))(,[0-9]{2})?( ?€)?$)/