【问题标题】:Extract strings in quotation marks and brackets提取引号和括号中的字符串
【发布时间】:2013-11-12 11:15:29
【问题描述】:

我的字符串测试是:

My name is "Ralph" ("France" is my country, "123" my age, ... , "an other text", ...)

我想得到引号中的字符串,但只有括号中的字符串。在我的示例中:字符串 France123

我已经测试过这个模式:

#\(.*"(.*)".*\)#

但它只匹配最后一个字符串123(我使用preg_match_all(),所以它应该返回每个结果,不是吗?)

如果我添加Ungreedy 选项,它只匹配第一个字符串France。所以我不明白为什么没有 U 选项它不是贪婪的,有没有办法在引号和括号中获取我的字符串?

谢谢, 拉斐尔 N.

【问题讨论】:

  • 我无法让它工作,这让我心烦意乱......

标签: regex preg-match-all pcre


【解决方案1】:

我可以让它工作的唯一方法是使用:

$subject = 'My 00123450 "Ralph" ("France" is my country, "123" my age, ... , "an other text", ...)';

$pattern =  '/\((?:[^"\)]*"(.*?)")?(?:[^"\)]*"(.*?)")?(?:[^"\)]*"(.*?)")?[^"]*?\)/';
preg_match_all($pattern, $subject, $matches);

for ($i = 1; $i < count($matches); $i++)
{
    print($i.': '.$matches[$i][0].";\n");
}

输出:

1: France; 
2: 123; 
3: an other text;

该正则表达式仅适用于最多 3 次出现在一组括号内的“引用字符串”。但是,您可以扩展正则表达式字符串以获取 最多 N 次出现,如下所示:

在每组括号中查找 1 到 N 个带引号的字符串的正则表达式是:

n=1 /\((?:[^"\)]*"(.*?)")?[^"]*?\)/
n=2 /\((?:[^"\)]*"(.*?)")?(?:[^"\)]*"(.*?)")?[^"]*?\)/
n=3 /\((?:[^"\)]*"(.*?)")?(?:[^"\)]*"(.*?)")?(?:[^"\)]*"(.*?)")?[^"]*?\)/

要查找 1-N 个字符串,请重复部分 (?:[^"\)]*"(.*?)")? N 次。对于每组括号中的 1-100 个字符串,您必须重复该部分 100 次 - 显然正则表达式将开始非常缓慢地评估。

我意识到这绝不是理想的,但这是我在一次通过解决方案方面的最大努力。

在 2 次传球中:

$subject = 'My name is "Ralph" ("France" is my country, "123" my age, ... , "an other text", ...)';

$pattern =  '/\(.*?\)/';
preg_match_all($pattern, $subject, $matches);

$pattern2 =  '/".*?"/';
preg_match_all($pattern2, $matches[0][0], $matches2);

print_r($matches2);

在 2 遍中产生正确的输出。急切地等待一个答案,显示如何在 1 中做到这一点。我已经尝试了所有我能想到的变体,但无法让它包含重叠匹配。

【讨论】:

  • 感谢您的麻烦。我认为这是 PCRE 正则表达式提供的基本需求(可能对解析 BBCode 或 HTML 有用?我必须朝那个方向搜索)。但出于我的需要,我将使用 2 遍解决方案,等待假设的 1 遍解决方案。
  • 我认为这肯定很简单.. 不得不发布我完全失败的尝试,因为我浪费了很多时间试图破解它;)
  • 我想知道断言 (link) 是否有帮助。我的想法是如果后面没有左括号,则将所有字符串都放在引号中。但目前我没有成功地让断言起作用(我以前从未使用过它们)。我会及时通知你的!
  • 我尝试了各种断言都无济于事。我认为如果允许像 (?
【解决方案2】:

保持简单,分两步完成:

$s = 'My name is "Ralph" ("France" is my country, "123" my age) and "I" am. ';
$str = preg_replace('#^.*?\(([^)]*)\).*$#', '$1', $s);
if (preg_match_all('/"([^"]*)"/', $str, $arr))
   print_r($arr[0]);

输出:

Array
(
    [0] => "France"
    [1] => "123"
)

【讨论】:

    【解决方案3】:

    这应该适合你:

    \("([^"]+)".+?"(.+)"
    

    解释:

    \(" - 匹配括号和双引号

    ([^"]+)" - 捕获双引号内的所有内容

    .+?" - 匹配直到下一个双引号的任何内容

    (.+) - 捕获所有非双引号

    " - 匹配最后一个双引号

    只要您的示例数据完全符合给定,正则表达式就可以工作

    【讨论】:

    • 如果( 后面没有紧跟",则失败
    • @OGHaza 是的。但这不在样本数据中,也没有说它会发生。我只能处理 OP 提供的内容。
    • true 但是 (123|France) 是一个完全有效的答案
    • 感谢您的帮助。我忘了说要捕获的引号中的文本数量不是固定的。我的错。同时,只有一对括号。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-28
    • 1970-01-01
    • 1970-01-01
    • 2011-11-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多