基于标点符号/空格将字符串拆分为数组 - 正则表达式

5

我需要一种方法,根据标点符号或空格的存在将字符串分成几个不同的部分。

我的意思是,每个单词都应该被分成自己的数组元素,此外,位于单词开头或结尾的标点符号也应该放入它自己的数组元素中。

例如: 我需要能够将字符串Hello, Harry Potter. I'm Tom Riddle.转换为

array(
   "Hello",
    ", "
    "Harry",
    "Potter"
    ". ",
    "I'm",
    "Tom",
    "Riddle",
    ". "
)

所以单词中的标点符号(例如单词中的撇号)不应该造成分离 **编辑:** 为了澄清所需行为,I'mdidn't等应保持一个单词,但是hello!"okay,等应与开头或结尾的标点符号分开。
另外,我希望包括在搜索中的标点符号是:
  • . (句号/周期)
  • ? (问号)
  • ! (感叹号)
  • ,(逗号)
  • ; (分号)
  • :(冒号)
  • - (连字符)
  • ((开始括号)
  • )(结束括号)
  • { (开始花括号)
  • } (结束花括号)
  • [ (开始方括号)
  • ] (结束方括号)
  • ' (单引号)
  • " (双引号)
  • … (省略号)
我找到的最接近我所需结果的是这个:
preg_split('/(\s|[\.,\/])/', $string, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);

然而,这样做的问题如下:
  • 单词中的标点符号被视为普通标点符号
  • 包含数组元素的数组元素不包含空格。 编辑:抱歉表达不清;我的意思是我希望标点符号包含其后/前的空格。例如,如果是逗号,则应为,(后面有空格),但如果是开括号,则应为((前面有空格)。
  • 当我添加所需的其余标点符号时(preg_split("/(\s|[\.?!,;:-(){}[]'\"…\/])/",)会出现错误。我非常确定这个错误是由于未转义的字符引起的,所以我将整个内容都运行了preg_quote,它返回了\.\?\!,;\:\-\(\)\{\}\[\]'"…,但仍然出现错误:Parse error: syntax error, unexpected '…' (T_STRING), expecting ',' or ')' in [...][...] on line 5

我对正则表达式的理解相当有限,但在查看了php文档之后,我可以得出上面的代码在遇到每个空格或逗号或标点符号时分离单词。(如果我理解错了,请纠正我?)并且,据我所知,在方括号中添加其余字符将使其在任何这些字符处分隔字符串(?)由于这种方法不起作用,我想我对它的工作原理有一些基本的误解,因此非常感谢能够得到解释。

3个回答

4

这样做可以实现,但输出结果会稍有不同,因为你把 ' 作为分割符,所以 I'm 会被拆分:

$result = preg_split('/(\.\.\.\s?|[-.?!,;:(){}\[\]\'"]\s?)|\s/',
                     $string, null, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);

这可能会让事情简化,但我只包含了省略号 ... 和可选空格,或者所有其他字符和可选空格,或者一个空格。

您需要转义字符类 [] 外部的点 .,转义字符类内部的 [],并且需要转义连字符 - 或将其放在开头或结尾以避免表示范围。显然,您需要转义用于包含模式的引号,在本例中为单引号 '

您没有指定标点符号两侧是否需要空格,并且不清楚此处的"Punctuation mid-word counts as normal punctuation" 是应该计算还是不计算。


哈哈,我正要问一个问题,但是我看到你已经编辑了它 :P 非常感谢,这很有效 :) 我可以问一下为什么没有一种方法来排除落在A-Z字符之间的结果吗?当我自己尝试解决这个问题时,我认为我可以使用 ^ 来排除这样的实例,但我找不到方法。我是否误解了否定的工作原理? - M. Salman Khan
抱歉不够清晰;我更新了问题以澄清所需的行为。 - M. Salman Khan
我最初的计划是让它们没有空格(这只是为了更容易地从数组中重新创建字符串),但现在我想,最好保留标点符号而不带任何空格,并保留它们之间的空格(所以会有一个仅包含空格( )的数组元素)。这可能吗?我应该编辑问题并改为这样说吗? - M. Salman Khan
哦,嗯,还有,你给出的第一段代码(/(\.\.\.\s|[-.?!,;:(){}\[\]\'"]\s?)/')是有效的,但这个新的代码在字符串开头/结尾处的标点符号无法正常工作:https://gyazo.com/f58e00605becc46d94292449abd1e34a - M. Salman Khan
感谢您的帮助;另一个答案能够阻止单词在标点符号中间被分开,所以我将其标记为已接受,但是对于您的解释,我给予了赞赏;谢谢! - M. Salman Khan
显示剩余2条评论

1

你是否真的希望所有单词内的标点符号都保持连接?此外,看起来你想将每个标点符号分开进行标记(但附加附近的空格),这是大部分工作。如果你确实想要这样做,那么这应该可以解决问题。附带一个测试字符串以展示它的工作方式。

$string = "Hello, it's me-me-it's-me!!! o... (a friend?)";
print_r( preg_split("/(\w\S+\w)|(\w+)|(\s*\.{3}\s*)|(\s*[^\w\s]\s*)|\s+/", $string, 
        -1, PREG_SPLIT_NO_EMPTY|PREG_SPLIT_DELIM_CAPTURE) );

输出:

Array
(
    [0] => Hello
    [1] => ,
    [2] => it's
    [3] => me-me-it's-me
    [4] => !
    [5] => !
    [6] => !
    [7] => o
    [8] => ... 
    [9] => (
    [10] => a
    [11] => friend
    [12] => ?
    [13] => )
)

这是它的工作原理:
  1. (\w\S+\w) 捕获任何长度大于3个字符的单词,允许嵌入非字母字符。
  2. (\w+) 捕获任何单词(以捕获短单词)。
  3. (\s*\.{3}\s*) 捕获省略号...,以及周围的任何空格。
  4. (\s*[^\w\s]\s*) 单独捕获任何非字母、非空格字符;但附加任何附近的空格。
  5. \s+ 任何其他空格(即单词之间的空格)会分割字符串,但不会被捕获。

如果您想选择单词内可以包含的内容,请将第一个备选项中的\S+替换为您想要允许的内容列表,例如,[\w'-]+仅允许撇号和连字符。


非常感谢您的回复;您在第4点中描述的似乎没有起作用,或者您可能误解了我的问题(如果表述不清,我很抱歉)。https://gyazo.com/2db04904b5f9a5c9d06a7986c507b057 我想要的结果是逗号后面有一个空格,所以2应该是 ", "。如果这不可能,那么是否可以使空格也返回到数组中,以便我可以循环遍历并进行操作? - M. Salman Khan
实际上忽略上一个评论,看着我的代码,我认为将空格作为它们自己的元素会更容易(因此将有一个仅包含单个空格的数组元素)。是否可以修改代码来实现这一点? - M. Salman Khan
哎呀,如果我也使用了 var_dump(),我就能捕捉到这个错误了。确实,那个代码应该捕捉到逗号后面的空格,但我还不确定我到底做错了什么。但我希望你们能看出这种方法的作用,并且能够根据你们实际想要的(尽管你们原始需求中这方面还不是很清楚)进行调整。 - alexis
当然可以更改需求。但要注意,当您这样做时,SO和所有程序员都会感到有些烦恼。下次您无法避免提出“解决我的混乱问题”的问题时,请尽一切努力理解和明确从一开始就需要的内容。现在,您是要将所有空格作为单独的标记返回,还是仍然希望将词之间的空格丢弃(这是一种疯狂的分词方式)? - alexis
抱歉回复晚了,SO应用程序退出了,所以我没有收到通知 >.< 对此感到抱歉,我知道这很烦人,我会坚持原来的问题,你的答案已经解决得足够好了,所以我会标记它为已接受。至于理解它的工作原理,我认为除了第4点中空格的附加方式之外,我都理解了;你能否请解释一下? - M. Salman Khan
第四部分应该以\s*结束--那是一个复制粘贴错误。因此,中间部分(字符类[^\w\s])选择一个不是单词或空格字符的字符,而两侧的\s*添加任何相邻的空格(如果它们还没有被正则表达式的另一部分读取)。 - alexis

0
通常情况下,您可以使用该模式。
word character+[all your punctuation characters here]+word character(*SKIP)(*FAIL)

例如:

\w[\[\].?\"\']\w(*SKIP)(*FAIL)|[\[\].?\"\']

请点击此处查看regex101.com上的演示


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接