正则表达式匹配引号中带有双引号的字符串

时间:2014-04-14 11:35:01

标签: java regex java-6

我面临一个挑战,即按以下格式匹配输入:

  • 输入由键=值对组成。关键始于斜线。值可以是数字或引号中的字符串。
  • 该值可以选择包含转义引号,即引号后跟引号(“”)。这种逃逸的报价应被视为价值的一部分。无需检查转义的报价是否平衡(例如,以另一个转义报价结束)。

正则表达式应与序列的给定key = value部分匹配,并且不应因长输入而中断(例如,值为10000个字符)。

首先我来到这个解决方案:

/(\w+)=(\d+|"(?:""|[^"])+"(?!"))

并且它执行得不错,但是在Java6中,对于长输入(例如,成就regexplanet),StackOverflowError失败。我尝试改进它以加快运行速度:

/(\w+)=(\d+|"(?:""|[^"]+)+"(?!"))

但是如果输入不匹配,它会在回溯中进入无限循环,试图匹配它。

然后我来到这个正则表达式:

/(\w+)=(\d+|".+?(?<!")(?:"")*"(?!"))

执行速度较慢,但​​似乎可以解决任务。

有人能建议更好/更快的正则表达式吗?

示例输入:

/mol_type="protein" /transl_table=11 /note="[CDS] (""multi
line)"  nn  /organism="""Some"" Sequence" nn  /organism="Some ""Sequence"""
/translation="MHPSSSRIPHIAVVGVSAIFPGSLDAHGFWRDILSGTDLITDVPSTHWLVE
DYYDPDPSAPDKTYAKRGAFLKDVPFDPLEWGVPPSIVPATDTTQLLALIVAKRVLEDAAQGQFE
SMSRERMSVILGVTSAQELLASMVSRIQRPVWAKALRDLGYPEDEVKRACDKIAGNYVPWQESSF
PGLLGNVVAGRIANRLDLGGTNCVTDAACASSLSAMSMAINELALGQSDLVIAGGCDTMNDAFMY
MCFSKTPALSKSGDCRPFSDKADGTLLGEGIAMVALKRLDDAERDGDRVYAVIRGIGSSSDGRSK
SVYAPVPEGQAKALRRTYAAAGYGPETVELMEAHGTGTKAGDAAEFEGLRAMFDESGREDRQWCA
LGSVKSQIGHTKAAAGAAGLFKAIMALHHKVLPPTIKVDKPNPKLDIEKTAFYLNTQARPWIRPG
DHPRRASVSSFGFGGSNFHVALEEYTGPAPKAWRVRALPAELFLLSADTPAALADRARALAKEAE
VPEILRFLARESVLSFDASRPARLGLCATDEADLRKKLEQVAAHLEARPEQALSAPLVHCASGEA
PGRVAFLFPGQGSQYVGMGADALMTFDPARAAWDAAAGVAIADAPLHEVVFPRPVFSDEDRAAQE
ARLRETRWAQPAIGATSLAHLALLAALGVRAEAFAGHSFGEITALHAAGALSAADLLRVARRRGE
LRTLGQVVDHLRASLPAAGPAASASPAAAASVPKASTAAVPAVASVAAPGAAEVERVVMAVVAET
TGYPAEMLGLQMELESDLGIDSIKRVEILSAVRDRTPGLSEVDASALAQLRTLGQVVDHLRASLP
AASAGPAVAAPAAKAPAVAAPTGVSGATPGAAEVERVVMAVVAETTGYPAEMLGLQMELESDLGI
DSIKRVEILSAVRDRTPGLAEVDASALAQLRTLGQVVDHLRASLGPAAVTAGAAPAEPAEEPAST
PLGRWTLVEEPAPAAGLAMPGLFDAGTLVITGHDAIGPALVAALAARGIAAEYAPAVPRGARGAV
FLGGLRELATADAALAVHREAFLAAQAIAAKPALFVTVQDTGGDFGLAGSDRAWVGGLPGLVKTA
ALEWPEASCRAIDLERAGRSDGELAEAIASELLSGGVELEIGLRADGRRTTPRSVRQDAQPGPLP
LGPSDVVVASGGARGVTAATLIALARASHARFALLGRTALEDEPAACRGADGEAALKAALVKAAT
SAGQRVTPAEIGRSVAKILANREVRATLDAIRAAGGEALYVPVDVNDARAVAAALDGVRGALGPV
TAIVHGAGVLADKLVAEKTVEQFERVFSTKVDGLRALLGATAGDPLKAIVLFSSIAARGGNKGQC
DYAMANEVLNKVAAAEAARRPGCRVKSLGWGPWQGGMVNAALEAHFAQLGVPLIPLAAGAKMLLD
ELCDASGDRGARGQGGAPPGAVELVLGAEPKALAAQGHGGRVALAVRADRATHPYLGDHAINGVP
VVPVVIALEWFARAARACRPDLVVTELRDVRVLRGIKLAAYESGGEVFRVDCREVSNGHGAVLAA
ELRGPQGALHYAATIQMQQPEGRVAPKGPAAPELGPWPAGGELYDGRTLFHGRDFQVIRRLDGVS
RDGIAGTVVGLREAGWVAQPWKTDPAALDGGLQLATLWTQHVLGGAALPMSVGALHTFAEGPSDG
PLRAVVRGQIVARDRTKADIAFVDDRGSLVAELRDVQYVLRPDTARGQA"
/note="primer of  Streptococcus pneumoniae

预期输出(来自regexhero.net):

RegEx

3 个答案:

答案 0 :(得分:3)

为了在合理的时间内失败,你确实需要避免灾难性的回溯。这可以使用原子分组(?>...)

来完成
/(\w+)=(\d+|"(?>(?>""|[^"]+)+)"(?!"))

# (?>(?>""|[^"]+)+)
(?>               # throw away the states created by (...)+
    (?>           # throw away the states created by [^"]+
        ""|[^"]+
    )+
)

在永不匹配的字符串上使用(?:""|[^"]+)+时出现的问题与每次匹配新[^"]字符的情况相关联,正则表达式引擎可以选择使用内部或外部{ {1}}量词。

这为回溯带来了很多可能性,在返回故障之前,引擎必须全部尝试。

我们知道,如果我们在引擎到达终点时没有找到匹配,我们永远不会:我们需要做的就是抛弃回溯位置以避免问题,这就是原子分组的用途

查看DEMO:失败的24个步骤,同时保留成功案例的速度(不是真正的基准测试工具,但灾难性的回溯很容易发现)

答案 1 :(得分:2)

这个怎么样:

/(\w+)=("(?:[^"]|"")*"|\d+)

(请注意,/是此处正则表达式的一部分。请根据您的宿主语言对其进行转义。)

如果您的正则表达式引擎支持它(Java确实如此),请创建* possessive

/(\w+)=("(?:[^"]|"")*+"|\d+)

经过一些调试后,后一个表达式可以改进为:

/(\w+)=("(?:""|[^"]*+)*+"|\d++)

请注意双*+)*+,它允许在一步中匹配连续文本,而不会受到灾难性回溯的影响。

答案 2 :(得分:2)

你的初始正则表达式已经非常好了,但它比必要的更复杂,导致catastrophic backtracking

你应该使用

/(\w+)=(\d+|"(?:""|[^"])*"(?!"))

live on regex101.com

<强>解释

/                # Slash
(\w+)            # Indentifier --> Group 1
=                # Equals sign
(                # Group 2:
 \d+             # Either a number
|                # or
 "(?:""|[^"])*"  # a quoted string
 (?!")           # unless another quote follows
)                # End of group 2