【问题标题】:Get the first letter/number of string that is not a special character获取不是特殊字符的字符串的第一个字母/数字
【发布时间】:2020-11-19 02:11:08
【问题描述】:

我有:

$string = ')(8234@#$ABCFG3478&* hello';

我想得到字符串中不是特殊字符的 first 字母/数字。在上面的字符串中,它应该是8。

有没有一种非正则表达式的方式来做到这一点,并且这种方式比下面的工作正则表达式更快吗?

if (preg_match('/[a-z0-9]/i', $string, $match)) {
    $firstLetter = $match[0];
} else {
    // no match
}

echo $firstLetter;

【问题讨论】:

  • 刺痛总是以)( 开头吗?
  • @B001ᛦ 不,这是一个例子。我想定位第一个 不是 特殊字符的字母/数字。更新了我的问题。
  • 为什么是非正则表达式方式?使用正则表达式,您可以使用 str.replace(/[^a-zA-Z ]/g, "") 之类的东西来摆脱特殊字符。
  • strcspn() 怎么样?
  • @Jax-p 你的正则表达式方法和我的有什么区别?我要避免的原因是对代码进行微优化。

标签: php preg-match extract alphanumeric


【解决方案1】:

您可以使用strpbrk 检查字符串中是否存在任何非特殊字符,并从中取出子字符串并打印第一个字符。我使用range 和implode 来生成字符列表。

<?php

$string = ')(8234@#$ABCFG3478&* hello';
$chars = implode("",range('a','z')) . implode("",range('A','Z')) . implode("",range(0,9));

$str = strpbrk($string, $chars);

if($str !== false){
    echo $str[0];
}else{
    echo "No non-special character found";
}

演示: https://3v4l.org/nOQl7

【讨论】:

  • 这种方法比我发布的正则表达式快多少?谢谢。
  • @HenrikPetterson,这比你的方法稍微慢一些,在基准循环之外不断处理。以 0.015 秒和 0.021 秒对您的方法进行基准测试,进行 100,000 次迭代。
【解决方案2】:

所以现在这有点牵强,但你可以用相反的方式来做。我不建议检查字符串中的每个字符,这会花费太长时间。

PHP 通过替换字符串中的内容非常快。这就是为什么您通过数组定义列表的原因,其中包含实际可能出现在字符串中的所有“特殊字符元素”,并将字符串替换为... NOTHING:

<?php
$string = ')(8234@#$ABCFG3478&* hello';
$special_character_list = ['(', ')', ]; // Extend the list if you like
$sanitized_string = str_replace($special_character_list, '', string);

编辑:另一种方法,我们创建一个允许字符列表,而不是定义无限的不允许字符列表。

<?php
$string = ')(8234@#$ABCFG3478&* hello';
$allowed_characters = array_merge(range('a', 'z'), range('A', 'Z'), range(0,9));

// We replace the valid characters with nothing - the result is: All     bad characters
$bad_characters = str_replace($allowed_characters, '', $string);

// We use the bad_characters to remove them from the original string
$sanitized_string = str_replace(str_split($bad_characters), '', $string);
echo $sanitized_string[0];

【讨论】:

  • 虽然这可行,但我需要列出无穷无尽的$special_character_list =D
  • 但是如果 OP 只需要第一个符号 - 他可以检查每个字符并在找到后 break。有时这会比替换整个字符串中的符号更快。
  • @HenrikPetterson 是的,你是对的。检查我的第二种方法 - 这可能会。
  • 超级有趣。有什么方法可以运行测试看看这是否比正则表达式更快?
  • @HenrikPetterson,这比您在基准循环之外不断处理的方法慢了大约 4 倍。
【解决方案3】:
$string = ')(8234@#$ABCFG3478&* hello';

$length = strlen($string);
$firstLetter = null;

for ($i = 0; $i < $length; ++$i) {
    if (ctype_alnum($string[$i])) {
        $firstLetter = $string[$i];
        break;
    }
}

这似乎有点快:https://3v4l.org/M08qg

(但我更喜欢你的 preg_match 方法!)

编辑:对于开头有更多特殊字符的字符串,它要慢得多! https://3v4l.org/EFdLW
所以性能取决于字符串开头特殊字符的平均长度。

【讨论】:

    【解决方案4】:

    除非您对该技术进行数十万次迭代,否则我认为“速度”不应作为决策标准考虑在内。

    对我来说,我非常重视直接性然后是可读性。出于这个原因,我可能不会接受一种生成数组、合并循环或坦率地说进行多个函数调用的技术。正则表达式并不邪恶(我喜欢它),所以除非有另一种单功能技术,否则我每次都选择正则表达式。

    也就是说,我发现的唯一非正则表达式方式(不是非常复杂)是@rid 用strcspn() 评论的。

    • preg_match() 的好处是更简洁,可以说更容易阅读,并且如果您的项目将来需要支持多字节字符,也不会让人头疼。
    • strcspn() 不会生成必须从中提取正确值的临时数组/变量,并且很可能优于任何 preg_ 调用,但没有明显/有价值的程度。

    请注意,在我的演示中,“字符掩码”似乎接受的字符范围不超过一个。如果我可以指定 A..Za..z0..9 会很方便,但我无法让它工作。

    代码:(Demo)

    $strings = [
        ')(8234@#$ABCFG3478&* hello', // mix
        '456456', // numeric
        'ghijkl', // alphabetic
        '!@#$%^&*(', // non-alphanumeric
    ];
    
    foreach ($strings as $string) {
        var_export(
            [
                'string' => $string,
                'firstAlphaNumeric1' => preg_match('/[a-z0-9]/i', $string, $match) ? $match[0] : null,
                'firstAlphaNumeric2' => $string[strcspn($string, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789')] ?? null,
                'firstAlphaNumeric3' => $string[strcspn($string, 'A..Zabcdefghijklmnopqrstuvwxyz0123456789')] ?? null,
            ]
        );
        echo "\n";
    }
    

    输出:

    array (
      'string' => ')(8234@#$ABCFG3478&* hello',
      'firstAlphaNumeric1' => '8',
      'firstAlphaNumeric2' => '8',
      'firstAlphaNumeric3' => '8',
    )
    array (
      'string' => '456456',
      'firstAlphaNumeric1' => '4',
      'firstAlphaNumeric2' => '4',
      'firstAlphaNumeric3' => '4',
    )
    array (
      'string' => 'ghijkl',
      'firstAlphaNumeric1' => 'g',
      'firstAlphaNumeric2' => 'g',
      'firstAlphaNumeric3' => 'g',
    )
    array (
      'string' => '!@#$%^&*(',
      'firstAlphaNumeric1' => NULL,
      'firstAlphaNumeric2' => NULL,
      'firstAlphaNumeric3' => NULL,
    )
    

    最终,我会选择preg_match(),因为在我作为 php 开发人员的职业生涯中,我已经使用了数千次。相反,我可能在我的职业生涯中最多使用过 1 次 strcspn()(除了在 Stack Overflow 上玩耍之外)——因此缺乏熟悉度/直观性。

    【讨论】:

      猜你喜欢
      • 2019-10-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-16
      • 2016-03-12
      • 1970-01-01
      • 1970-01-01
      • 2018-07-11
      相关资源
      最近更新 更多