【问题标题】:matching a part of a string from the beginning从头开始匹配字符串的一部分
【发布时间】:2016-05-17 14:53:50
【问题描述】:

我有一个日期(例如 yyyy-mm-dd hh:mm:ss),如果我们有一个完全匹配该模式或部分匹配该模式的字符串,我希望能够从一开始就返回 true结束。例如。 44 将匹配该模式,因为 44 将匹配 yy,但 -12 将不匹配,因为 - 不是有效的“y”字符。

我想到了一些不优雅的解决方案。我可以在这样的事情上做preg_match

#^\d(\d(\d(\d(-)?)?)?)?$#

这只是根据年份执行部分匹配。它需要扩展以考虑月、日、小时、分钟等,但这应该显示想法 #1。

我也可以做类似...

$patterns = [
    '',
    '\d',
    '\d\d',
    '\d\d\d',
    '\d\d\d\d',
    '\d\d\d\d-',
    ...
];
isset($patterns[strlen($str)] && preg_match('#^' . $patterns[strlen($str)] . '$#', $str)

但这似乎也有点令人费解。

我也可以这样做:

switch (strlen($str)) {
    case 1: return preg_match('#^\d$#', $str);
    case 2: return preg_match('#^\d\d$#', $str);
    case 3: return preg_match('#^\d\d\d$#', $str);
    case 4: return preg_match('#^\d\d\d\d$#', $str);
    case 5: return preg_match('#^\d\d\d\d-$#', $str);
    ...
}

但这似乎也很臃肿。

换句话说,我正在寻找2005- 将返回true 的函数,22005-1 也将返回true。但-2005 将返回 false,205-neubert 也将返回 false。

我希望能够将有效值传递给 SQL 查询。例如。 WHERE date_column LIKE '$str%'。如果 date_column 是 DATETIME,那么搜索 -12- 是浪费时间,因为 date_column 不可能将其作为值。

有什么想法吗?

【问题讨论】:

  • 我在我的模式中使用^$。我的第一个和preg_match 通话中的第二个一样。 $patterns 数组在 #^$# 之间连接。
  • 你能为上述模式添加一些有效和无效的输入吗?
  • @anubhava - 我已经更新了我的帖子。
  • 2005-1 的长度为 6,2005-12 的长度为 7,我认为两者都是有效的,但不会被现有模式覆盖
  • @anubhava - 没错。为简洁起见,我没有完全扩展这些模式。

标签: php regex date


【解决方案1】:

这样的事情应该可以工作:

function doesMatch($str) {
    // The date pattern split so that each array entry matches exactly one character
    $pattern_chunks = array(
        '\d', '\d', '\d', '\d', '-',
        '\d', '\d', '-',
        '\d', '\d',
        '\s',
        '\d', '\d', ':',
        '\d', '\d', ':',
        '\d', '\d'
    );

    $chunk_count = count($pattern_chunks);
    $str_len = strlen($str);

    // If the string is empty, it's clearly not a date
    if ( $str_len < 1 ) { return false; }

    // If the string is longer than our pattern chunks, there's no way it matches
    if ( $str_len > $chunk_count ) { return false; }

    // Make a pattern using the first N chunks of our pattern parts
    $pattern = '^' . implode('', array_slice($pattern_chunks, 0, $str_len)) . '$';

    // Return if the string matches
    return (preg_match($pattern, $str) > 0);
}

例如,您的输入字符串长度为 6 个字符,它仅使用日期模式的前 6 个块 (^\d\d\d\d-\d$)。

【讨论】:

    【解决方案2】:

    根据您问题下方的 cmets,如果我正确理解了问题,您可以使用这个单一的正则表达式来匹配您的输入:

    ^(?:\d{1,3}|\d{4}(?:-(?:\d{1,2}(?:-\d{0,2})?)?)?)$
    

    RegEx Demo

    【讨论】:

    • 它没有做 hh:mm:ss 但我明白你在做什么。我想这样做 hh:mm:ss 我可能不得不继续 )?)?)?)?进行中 )?)?)?)?)?)?)?。我还必须在中间添加一些东西,然后...... ick。似乎很难管理正则表达式哈哈。
    【解决方案3】:

    我知道这个问题已经得到解答并结束,但我认为这是一个有趣的挑战,我决心想出一个非正则表达式的解决方案。我还希望它更具可重用性,而不是针对特定情况进行硬编码。

    这是我最终的结果;我相信它可以进一步改进! :)

    // case-insensitive string format comparison
    // if $strict is true, the string lengths must also match
    // if $strict is false, the strings are compared left to right
    function strfcasecmp($str, $format, $strict = false) {
        $len1 = strlen($str);
        $len2 = strlen($format);
    
        // make sure we have a valid length
        if ($len1 < 1 || $len1 > $len2) {
            return false;
        }
    
        // if strict, make sure length matches as well
        if ($strict) {
            if ($len1 !== $len2) {
                return false;
            }
        }
    
        // compare alpha, numeric, space & printable characters
        for ($i = 0; $i < $len1; $i++) {
            switch (true) {
                case ctype_alpha($format[$i]):
                    if (!ctype_alpha($str[$i])) {
                        return false;
                    }
                    break;
    
                case ctype_digit($format[$i]):
                    if (!ctype_digit($str[$i])) {
                        return false;
                    }
                    break;
    
                case ctype_space($format[$i]):
                    if (!ctype_space($str[$i])) {
                        return false;
                    }
                    break;
    
                case ctype_punct($format[$i]):
                    if ($str[$i] !== $format[$i]) {
                        return false;
                    }
                    break;
    
                default:
                    // character must match at least one type specified above
                    return false;
            }
        }
    
        return true;
    }
    

    基于您的原始问题的示例用法:

    $format = '0000-00-00 00:00:00';
    var_dump(strfcasecmp('44', $format)); // true
    var_dump(strfcasecmp('-12', $format)); // false
    var_dump(strfcasecmp('2005-', $format)); // true
    var_dump(strfcasecmp('2', $format)); // true
    var_dump(strfcasecmp('2005-1', $format)); // true
    var_dump(strfcasecmp('-2005', $format)); // false
    var_dump(strfcasecmp('205-', $format)); // false
    var_dump(strfcasecmp('neubert', $format)); // false
    

    【讨论】:

      【解决方案4】:

      你也可以使用strpos()进行匹配:

      $a = "2016-05-17 was a good day.";
      if (strpos(substr($a, 0, 10), '2016-05-17') !== false) {
          echo "Indeed it was";
      }
      

      【讨论】:

      • 这不是我真正想做的。我想看看有问题的字符串是完整日期还是部分日期,没有别的。您的代码所做的是告诉您字符串中的任何位置是否存在预定长度的日期。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多