括号可能不平衡的正则表达式
regex where parenthesis might not be balanced
我必须从 PDF 流中提取一些文本作为字符串。此流将包含描述文本外观的标记和文本本身。我收到的我的正则表达式必须 运行 的字符串永远不会包含任何回车 returns 或换行符。我感兴趣的文本区域将始终在括号内(并且可能在括号内有括号),并且在最后一个括号之后会有字母 'Tj'。总之,我所追求的总是遵循惯例:
(.....) Tj
目前,只要括号全部平衡,我的正则表达式就可以工作:
\((?:[^()]|(?'paren'\()|(?'-paren'\)))+(?(paren)(?!))\)
但是,如果文本本身包含不平衡的括号,则此正则表达式将无法满足我的要求,而且我不确定如何更改它才能处理不平衡的括号。
这是一个被认为是 'normal' 字符串的示例:
q Q /Tx BMC q 0 0 471.34 407.34 re W n BT 1 0 0 1 2 397.16 Tm /Helv 12 Tf 0 g (RE: Request for Additional Information) Tj
很明显,我想从中获取字符串 'RE: Request for Additional Information'。
这是我的正则表达式失败的示例(我添加了不平衡的括号):
q Q /Tx BMC q 0 0 471.34 407.34 re W n BT 1 0 0 1 2 397.16 Tm /Helv 12 Tf 0 g (RE: Request for (Additional Information) Tj 0 g 1 0 0 1 2 383.29 Tm 0 g ( 13. Processing TT Instructions -) Audit Note 12) Tj 0 g 1 0 0 1 2 369.42 Tm 0 g () Tj 0 g 1 0 0 1 2 355.55 Tm 0 g (Dear test:) Tj 0 g 1 0 0 1 2 341.68 Tm 0 g () Tj 0 g 1 0 0 1 2 327.8 Tm 0 g (Thank you for the more random words here. )Unfortunately, more words here) terminating (words here) Tj
这里还有空括号,看起来像:
() Tj
这些表示呈现 PDF 时的回车 returns 和换行。任何帮助表示赞赏。提前谢谢你。
---更新以回答以下问题
任何类型的用户输入都可以放在左括号和右括号之间。我想按照提供的方式提取所有内容,即使用户忘记平衡他们的括号也是如此。唯一的保证是括号之间的文本是用户输入的,但是他们输入的文本取决于他们,因此它不遵循预定义的格式,例如 ([abbrev]: [content]),等。内容只保证在一个开括号,一个闭括号之间,闭括号之后是字母'Tj'.
正如我在评论中提到的,我无法帮助您解决 .NET 问题,但我可以给您一个可能有帮助的表达方式。我认为该解决方案需要 "negative lookahead",而 perl 提供了这一点。问题是我太久没有使用 perl 了,我忘记了如何让它在整个流中运行。如果我将流分成“(...) Tj”块,每块单独一行,我的脚本将适用于您的所有示例:
$ cat pdf_data_line_by_line.txt
q Q /Tx BMC q 0 0 471.34 407.34 re W n BT 1 0 0 1 2 397.16 Tm /Helv 12 Tf 0 g (RE: Request for Additional Information) Tj
q Q /Tx BMC q 0 0 471.34 407.34 re W n BT 1 0 0 1 2 397.16 Tm /Helv 12 Tf 0 g (RE: Request for (Additional Information) Tj
0 g 1 0 0 1 2 383.29 Tm 0 g ( 13. Processing TT Instructions -) Audit Note 12) Tj
0 g 1 0 0 1 2 369.42 Tm 0 g () Tj
0 g 1 0 0 1 2 355.55 Tm 0 g (Dear test:) Tj
0 g 1 0 0 1 2 341.68 Tm 0 g () Tj
0 g 1 0 0 1 2 327.8 Tm 0 g (Thank you for the more random words here. )Unfortunately, more words here) terminating (words here) Tj
$ cat get_pdf_text.pl
#!/usr/bin/perl
while (<>) {
# find some text
if ( /[^(]*\((?!\)).*\) Tj/ ) {
# strip off leading junk
s/[^(]*\((?!\))[ ]*([^)].*)\) Tj//;
# output saved part of match
print $_;
print "YOUR DELIMITER HERE\n";
}
}
$ cat pdf_data_line_by_line.txt | ./get_pdf_text.pl
RE: Request for Additional Information
YOUR DELIMITER HERE
RE: Request for (Additional Information
YOUR DELIMITER HERE
13. Processing TT Instructions -) Audit Note 12
YOUR DELIMITER HERE
Dear test:
YOUR DELIMITER HERE
Thank you for the more random words here. )Unfortunately, more words here) terminating (words here
YOUR DELIMITER HERE
但是,如果我将示例组合成一个流,它会在第一个流之后停止。我尝试在 's' 命令末尾使用 "g",但没有帮助:
$ cat pdf_data_single_stream.txt
q Q /Tx BMC q 0 0 471.34 407.34 re W n BT 1 0 0 1 2 397.16 Tm /Helv 12 Tf 0 g (RE: Request for (Additional Information) Tj 0 g 1 0 0 1 2 383.29 Tm 0 g ( 13. Processing TT Instructions -) Audit Note 12) Tj 0 g 1 0 0 1 2 369.42 Tm 0 g () Tj 0 g 1 0 0 1 2 355.55 Tm 0 g (Dear test:) Tj 0 g 1 0 0 1 2 341.68 Tm 0 g () Tj 0 g 1 0 0 1 2 327.8 Tm 0 g (Thank you for the more random words here. )Unfortunately, more words here) terminating (words here) Tj
$ cat pdf_data_single_stream.txt | ./get_pdf_text.pl
RE: Request for (Additional Information) Tj 0 g 1 0 0 1 2 383.29 Tm 0 g ( 13. Processing TT Instructions -) Audit Note 12) Tj 0 g 1 0 0 1 2 369.42 Tm 0 g () Tj 0 g 1 0 0 1 2 355.55 Tm 0 g (Dear test:) Tj 0 g 1 0 0 1 2 341.68 Tm 0 g () Tj 0 g 1 0 0 1 2 327.8 Tm 0 g (Thank you for the more random words here. )Unfortunately, more words here) terminating (words here
YOUR DELIMITER HERE
替换字符串...
s/[^(]*\((?!\))[ ]*([^)].*)\) Tj//
... 执行以下操作:找到零个或多个不是“(”的字符,后跟单个“(”,后跟一个“)”(这是您需要否定前瞻的地方,并且这消除了 '() Tj' 的情况),后跟零个或多个空格,然后记住 {如果后面的字符不是 ')' 和零个或多个后续字符},如果后面跟着 ') Tj',和用记住的字符串替换所有这些。
如果有人可以建议(可能非常简单)让脚本在整个流中运行的方法,那么这应该可以解决手头的问题。
我必须从 PDF 流中提取一些文本作为字符串。此流将包含描述文本外观的标记和文本本身。我收到的我的正则表达式必须 运行 的字符串永远不会包含任何回车 returns 或换行符。我感兴趣的文本区域将始终在括号内(并且可能在括号内有括号),并且在最后一个括号之后会有字母 'Tj'。总之,我所追求的总是遵循惯例:
(.....) Tj
目前,只要括号全部平衡,我的正则表达式就可以工作:
\((?:[^()]|(?'paren'\()|(?'-paren'\)))+(?(paren)(?!))\)
但是,如果文本本身包含不平衡的括号,则此正则表达式将无法满足我的要求,而且我不确定如何更改它才能处理不平衡的括号。
这是一个被认为是 'normal' 字符串的示例:
q Q /Tx BMC q 0 0 471.34 407.34 re W n BT 1 0 0 1 2 397.16 Tm /Helv 12 Tf 0 g (RE: Request for Additional Information) Tj
很明显,我想从中获取字符串 'RE: Request for Additional Information'。
这是我的正则表达式失败的示例(我添加了不平衡的括号):
q Q /Tx BMC q 0 0 471.34 407.34 re W n BT 1 0 0 1 2 397.16 Tm /Helv 12 Tf 0 g (RE: Request for (Additional Information) Tj 0 g 1 0 0 1 2 383.29 Tm 0 g ( 13. Processing TT Instructions -) Audit Note 12) Tj 0 g 1 0 0 1 2 369.42 Tm 0 g () Tj 0 g 1 0 0 1 2 355.55 Tm 0 g (Dear test:) Tj 0 g 1 0 0 1 2 341.68 Tm 0 g () Tj 0 g 1 0 0 1 2 327.8 Tm 0 g (Thank you for the more random words here. )Unfortunately, more words here) terminating (words here) Tj
这里还有空括号,看起来像:
() Tj
这些表示呈现 PDF 时的回车 returns 和换行。任何帮助表示赞赏。提前谢谢你。
---更新以回答以下问题
任何类型的用户输入都可以放在左括号和右括号之间。我想按照提供的方式提取所有内容,即使用户忘记平衡他们的括号也是如此。唯一的保证是括号之间的文本是用户输入的,但是他们输入的文本取决于他们,因此它不遵循预定义的格式,例如 ([abbrev]: [content]),等。内容只保证在一个开括号,一个闭括号之间,闭括号之后是字母'Tj'.
正如我在评论中提到的,我无法帮助您解决 .NET 问题,但我可以给您一个可能有帮助的表达方式。我认为该解决方案需要 "negative lookahead",而 perl 提供了这一点。问题是我太久没有使用 perl 了,我忘记了如何让它在整个流中运行。如果我将流分成“(...) Tj”块,每块单独一行,我的脚本将适用于您的所有示例:
$ cat pdf_data_line_by_line.txt
q Q /Tx BMC q 0 0 471.34 407.34 re W n BT 1 0 0 1 2 397.16 Tm /Helv 12 Tf 0 g (RE: Request for Additional Information) Tj
q Q /Tx BMC q 0 0 471.34 407.34 re W n BT 1 0 0 1 2 397.16 Tm /Helv 12 Tf 0 g (RE: Request for (Additional Information) Tj
0 g 1 0 0 1 2 383.29 Tm 0 g ( 13. Processing TT Instructions -) Audit Note 12) Tj
0 g 1 0 0 1 2 369.42 Tm 0 g () Tj
0 g 1 0 0 1 2 355.55 Tm 0 g (Dear test:) Tj
0 g 1 0 0 1 2 341.68 Tm 0 g () Tj
0 g 1 0 0 1 2 327.8 Tm 0 g (Thank you for the more random words here. )Unfortunately, more words here) terminating (words here) Tj
$ cat get_pdf_text.pl
#!/usr/bin/perl
while (<>) {
# find some text
if ( /[^(]*\((?!\)).*\) Tj/ ) {
# strip off leading junk
s/[^(]*\((?!\))[ ]*([^)].*)\) Tj//;
# output saved part of match
print $_;
print "YOUR DELIMITER HERE\n";
}
}
$ cat pdf_data_line_by_line.txt | ./get_pdf_text.pl
RE: Request for Additional Information
YOUR DELIMITER HERE
RE: Request for (Additional Information
YOUR DELIMITER HERE
13. Processing TT Instructions -) Audit Note 12
YOUR DELIMITER HERE
Dear test:
YOUR DELIMITER HERE
Thank you for the more random words here. )Unfortunately, more words here) terminating (words here
YOUR DELIMITER HERE
但是,如果我将示例组合成一个流,它会在第一个流之后停止。我尝试在 's' 命令末尾使用 "g",但没有帮助:
$ cat pdf_data_single_stream.txt
q Q /Tx BMC q 0 0 471.34 407.34 re W n BT 1 0 0 1 2 397.16 Tm /Helv 12 Tf 0 g (RE: Request for (Additional Information) Tj 0 g 1 0 0 1 2 383.29 Tm 0 g ( 13. Processing TT Instructions -) Audit Note 12) Tj 0 g 1 0 0 1 2 369.42 Tm 0 g () Tj 0 g 1 0 0 1 2 355.55 Tm 0 g (Dear test:) Tj 0 g 1 0 0 1 2 341.68 Tm 0 g () Tj 0 g 1 0 0 1 2 327.8 Tm 0 g (Thank you for the more random words here. )Unfortunately, more words here) terminating (words here) Tj
$ cat pdf_data_single_stream.txt | ./get_pdf_text.pl
RE: Request for (Additional Information) Tj 0 g 1 0 0 1 2 383.29 Tm 0 g ( 13. Processing TT Instructions -) Audit Note 12) Tj 0 g 1 0 0 1 2 369.42 Tm 0 g () Tj 0 g 1 0 0 1 2 355.55 Tm 0 g (Dear test:) Tj 0 g 1 0 0 1 2 341.68 Tm 0 g () Tj 0 g 1 0 0 1 2 327.8 Tm 0 g (Thank you for the more random words here. )Unfortunately, more words here) terminating (words here
YOUR DELIMITER HERE
替换字符串...
s/[^(]*\((?!\))[ ]*([^)].*)\) Tj//
... 执行以下操作:找到零个或多个不是“(”的字符,后跟单个“(”,后跟一个“)”(这是您需要否定前瞻的地方,并且这消除了 '() Tj' 的情况),后跟零个或多个空格,然后记住 {如果后面的字符不是 ')' 和零个或多个后续字符},如果后面跟着 ') Tj',和用记住的字符串替换所有这些。 如果有人可以建议(可能非常简单)让脚本在整个流中运行的方法,那么这应该可以解决手头的问题。