正则表达式:除了表情符号外,删除所有非字母数字字符

4

我需要删除除了空格和允许的表情符号以外的所有非字母数字字符。

允许使用的表情符号包括:):(:P等(最常见的)。

我有一个字符串:

$string = 'Hi! Glad # to _ see : you :)';

我需要处理这个字符串并得到以下结果:

$string = 'Hi Glad to see  you :)';

此外,请注意表情符号可以包含空格
例如。
:     ) 代替 :)
或者
:     P 代替 :P
有没有函数可以做到这一点?
如果有人帮助我,那就太棒了 :)
更新
非常感谢您的帮助。
buckley提供了现成的解决方案,
但是如果字符串中包含带有空格的表情符号
例如。 Hi! Glad # to _ see : you :   )
结果等于 Hi Glad to see you
正如您所看到的,表情符号 :  )被截断了。

这个 preg_replace("**/[^a-zA-Z0-9\s*]/**", '', $string); 会执行吗? - chris85
我收到了错误信息 警告:preg_replace():未知修饰符'/' 和空字符串。 - xyz
这是因为第一个 * 被读取为前导分隔符,而下一个 * 被读取为结束分隔符,这使得 / 成为了一个它本不应该是的修饰符。您可以在此处阅读有关分隔符的更多信息,http://php.net/manual/en/regexp.reference.delimiters.php。我认为下面的正则表达式应该能够实现您想要的功能,这只是未来参考的一条注释。 - chris85
5个回答

3
我不“说”php ;),但这可以用JS实现。也许你可以转换它。
var sIn = 'Hi! Glad # to _ see : you :)',
    sOut;

sOut = sIn.match(/([\w\s]|: ?\)|: ?\(|: ?P)*/g).join('');

它与您的尝试相反 - 它找到所有“合法”的字符/组合,并将它们连接在一起。

敬礼

编辑:更新正则表达式以处理表情符号中的可选空格(如早先评论所述)。


非常感谢。 我会尝试转换为PHP。 - xyz
非常感谢!它适用于字符串 **Hi!Glad#to_see:you:),但如果字符串包含带有空格的表情符号例如 Hi!Glad#to_see:you: )结果等同于 Hi Glad to see you正如您所看到的,表情符号 : ) 被切断了。 - xyz
你可以添加任何表情符号,使其在正则表达式中有效。并且你可以修改它以允许空格:/([\w\s]|:\s?\)|:\s?\(|:\s?P)*/ - SamWhan

2

哈!这个很有趣

替换

(?!(:\)|:\(|:P))[^a-zA-Z0-9 ](?<!(:\)|:\(|:P))

没有任何东西

这个想法是,你用同样的正则表达式一次作为负向前瞻,一次作为负向后顾,夹在非法字符之间。

结果中会有连续的空格。据我所知,正则表达式不能在一次扫描中完成多个匹配,因此它无法做到这一点。

要消除连续的空格,您可以使用\s+替换为(一个空格)。


非常感谢!但是如果字符串包含带有空格的表情符号,例如:“嗨!高兴#看到_你:)”,结果应为“Hi Glad to see you”。你可以看到表情符号“:)”被截断了。 - xyz

2
这里是一个更新后的答案,满足了表情符号可以包含空格的新要求。
替换
((:\))|(:\()|(:P)|(: \))|: P)|[^0-9a-zA-Z\r\n ]

带着

$1

以自由间距模式格式化后,它变成了:

(?x)
(
  (?::\))|
  (?::\()|
  (?::P)|
  (?::\ \))|
  :\ P
)|
[^0-9a-zA-Z\r\n ]

在PHP中

$result = preg_replace('/((:\))|(:\()|(:P)|(: \))|: P)|[^0-9a-zA-Z\r\n ]/', '$1', $subject);

这个想法是,我们以包含多个字符的表情符号作为正则表达式的开头,这些字符中每个都可能包含一个非法字符。
这个组被捕获并稍后用作替换 $1。
然后,在选择分支之后,我们使用一个白名单来否定一些字符,这样它们就会被匹配但不会在替换模式中出现。
所有未匹配的内容(即我们的白名单)将按照约定原样重复出现在结果中。
需要注意的是,在列出表情符号时存在大量的分组,这可能会影响性能。为了防止这种情况,我们可以使正则表达式更加冗长。
 ((?::\))|(?::\()|(?::P)|(?:: \))|: P)|[^0-9a-zA-Z\r\n ]

据我所知,多个连续的空格仍然存在,无法在一次操作中解决。


1

这里有一个字符串格式化程序,可以做出假设,即表情符号通常是2个字符长:

<?php

class StringFormatter
{
  private $blacklist;
  private $whitelist;

  public function __construct(array $blacklist, array $whitelist)
  {
    $this->blacklist = $blacklist;
    $this->whitelist = $whitelist;
  }

  public function format($str)
  {
    $strLen = strlen($str);

    $result = '';
    $counter = 0;
    while ($counter < $strLen) {
      // get a character from the string
      $char = substr($str, $counter, 1);

      // if not blacklisted, allow it in the result
      if (!in_array($char, $this->blacklist)) {
        $result .= $char;
        $counter++;
        continue;
      }

      // if we reached the last letter, break out of the loop
      if ($counter >= $strLen - 1) {
        break;
      }

      // we assume all whitelisted entries have same length (e.g. 2
      // for emoticons)
      if (in_array(substr($str, $counter, 2), $this->whitelist)) {
        $result .= substr($str, $counter, 2);
        $counter += 2;
      } else {
        $counter++;
      }
    }

    return $result;
  }
}

// example usage
// $whitelist is not the entire whitelist, actually it's the exceptions
// to the blacklist, so more complext strings including blacklisted  characters that should be allowed
$formatter = new StringFormatter(['#', '_', ':', '!'], [':)', ':(']);
echo $formatter->format('Hi! Glad # to _ see : you :)');

上面的代码可以进一步重构以使其更加简洁,但你已经明白了。

1

我会使用这个正则表达式:

(?i)(:\s*[)p(])(*SKIP)(*FAIL)|[^a-z0-9 ]

示例:https://regex101.com/r/nW6iL3/2

PHP用法:

$string = ':     ) instead of :)

or

:     P instead of :P

Hi! Glad # to _ see : you :)';

echo preg_replace('~(?i)(:\s*[)p(])(*SKIP)(*FAIL)|[^a-z0-9 ]~', '', $string);

输出:

: )而不是:)或: P而不是:PHi很高兴见到你:)

演示:https://eval.in/416394

如果表情符号的结束部分发生变化,或者您有其他表情符号,可以将它们添加到此字符类中[)p(]

您还可以通过将:更改为字符类来更改眼睛,这样您就可以执行

(?i)([:;]\s*[)p(])(*SKIP)(*FAIL)|[^a-z0-9 ] 

如果您希望允许眨眼的表情符号(我认为分号是眨眼),则需要进行以下操作: 更新 逐步解释... (?i) = 使正则表达式不区分大小写 : = 搜索眼睛(一个冒号) \s* = 搜索零个或多个(*表示前面字符的0个或多个)空格字符(\h 在这里可能更好,\s 包括换行符和制表符) [)p(] = 这是一个字符类,允许其中任何字符出现。所以)p(都可以在此处使用。 (*SKIP)(*FAIL) = 如果找到了前面的正则表达式,请忽略它,www.rexegg.com/regex-best-trick.html。 | = 或 [^a-z0-9 ] - 否定字符类,表示查找不在此列表中的任何字符。
regex101还有关于正则表达式的文档。

谢谢。 它帮了我。 你能解释一下你的正则表达式中分离部分的含义吗? 特别是 (*SKIP) 和 (*FAIL)。 - xyz

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