【问题标题】:Convert "1d2h3m" to ["day" => 1, ”hour” => 2,"minutes"=>3]将 "1d2h3m" 转换为 ["day" => 1, "hour" => 2,"minutes"=>3]
【发布时间】:2015-12-18 22:00:14
【问题描述】:

我正在尝试将时间表达式字符串解析为具有全字键的关联数组。

我的意见:

$time = "1d2h3m";

我想要的输出:

array(
    "day" => 1,
    "hour" => 2,
    "minutes" => 3
)

我尝试使用explode() 提取数字。

$time = "1d2h3m";
$day = explode("d", $time);
var_dump($day); // 0 => string '1' (length=1)
                // 1 => string '2h3m' (length=4)

如何将严格格式化的字符串转换为所需的关联数组?

【问题讨论】:

  • $ol 定义在哪里?
  • 将结果数组分解两次,每次使用hm,在循环中执行,然后将array_push 到另一个数组中。

标签: php arrays time associative-array text-parsing


【解决方案1】:

对于这种情况,您应该使用正则表达式

<?php
 $time = "1d2h3m";
 if(preg_match("/([0-9]+)d([0-9]+)h([0-9]+)m/i",$time,$regx_time)){
    $day = (int) $regx_time[1];
    $hour = (int) $regx_time[2];
    $minute = (int) $regx_time[3];
    var_dump($day);
 }
?>

解释:
[0-9]:匹配0到9之间的任意数字
[0-9]+:意思是,匹配数字在至少一个字符
([0-9]+) :表示,匹配数字至少一个字符并捕获结果
/......... ./i :为您设置的正则表达式模式设置不区分大小写

Regex 是更好的词法分析器和词法解析字符串的方式。学习正则表达式很好。几乎所有的编程语言都使用正则表达式

【讨论】:

  • 只需将这个基本的 RegEx 与其他的进行比较。我不知道为什么大学/大学不教 RegEx。
【解决方案2】:

另一种正则表达式解决方案

$subject = '1d2h3m';
if(preg_match('/(?P<day>\d+)d(?P<hour>\d+)h(?P<minute>\d+)m/',$subject,$matches))
{
  $result = array_map('intval',array_intersect_key($matches,array_flip(array_filter(array_keys($matches),'is_string'))));
  var_dump($result);
}

返回

array (size=3)
  'day' => int 1
  'hour' => int 2
  'minute' => int 3

【讨论】:

  • 对不起,我发错评论了。这对另一个(不是你的)帖子来说很重要。
  • 这个答案缺少教育解释。我不认为我推荐这个答案,因为这个任务可以在一个本地函数调用中完成,而不是......我不知道有多少——我数不清了,是 10 个函数调用吗?
【解决方案3】:

我认为对于这么小的字符串,如果格式始终相同,您可以使用array_pushsubstr 从字符串中提取数字并将它们放入数组中。

<?php
$time = "1d2h3m";
$array = array();
array_push($array, (int) substr($time, 0, 1));
array_push($array, (int) substr($time, 2, 1));
array_push($array, (int) substr($time, 4, 1));
var_dump($array);
?>

【讨论】:

  • 当一个函数调用可以完成这项工作时,我认为使用 6 个函数调用不是一个非常有吸引力的解决方案。
【解决方案4】:

您可以使用此代码。

<?php
$str = "1d2h3m";
list($arr['day'],$arr['day'],$arr['hour'],$arr['hour'],$arr['minute'],$arr['minute']) = $str;
print_r($arr);
?>

输出

Array ( 
   [minute] => 3
   [hour] => 2
   [day] => 1
)

DEMO

【讨论】:

  • 您的解决方案不适用于两位数的天、小时和分钟。
【解决方案5】:

可自定义的功能,最重要的是,如果输入格式不正确,您可以捕获异常

/**
  * @param $inputs string : date time on this format 1d2h3m
  * @return array on this format                 "day"      => 1,
  *                                              "hour"     => 2,
  *                                              "minutes"  => 3        
  * @throws Exception
  *
  */
function dateTimeConverter($inputs) {
    // here you can customize how the function interprets input data and how it should return the result
    // example : you can add "y"    => "year"
    //                       "s"    => "seconds"
    //                       "u"    => "microsecond"
    // key, can be also a string
    // example                "us"  => "microsecond"
    $dateTimeIndex  = array("d" => "day",
                               "h" => "hour",
                               "m" => "minutes");

    $pattern        = "#(([0-9]+)([a-z]+))#";
    $r              = preg_match_all($pattern, $inputs, $matches);
    if ($r === FALSE) {
        throw new Exception("can not parse input data");
    }
    if (count($matches) != 4) {
        throw new Exception("something wrong with input data");
    }
    $datei      = $matches[2]; // contains number
    $dates      = $matches[3]; // contains char or string
    $result    = array();
    for ($i=0 ; $i<count ($dates) ; $i++) {
        if(!array_key_exists($dates[$i], $dateTimeIndex)) {
            throw new Exception ("dateTimeIndex is not configured properly, please add this index : [" . $dates[$i] . "]");
        }
        $result[$dateTimeIndex[$dates[$i]]] = (int)$datei[$i];
    }
    return $result;
}

【讨论】:

  • @Sorin 在这种情况下,您应该以这种方式调整 $dateTimeIndex 以避免歧义$dateTimeIndex = array( "mon" =&gt; "month", "d" =&gt; "day", "h" =&gt; "hour", "min" =&gt; "minutes"); 并且,当然,输入字符串必须遵守规则:)跨度>
  • 在捕获括号时包装整个模式没有任何好处。这会不必要地膨胀匹配数组。
【解决方案6】:

你不能把它炸3次吗...

// Define an array
$day = explode("d", $time);
// add it in array with key as "day" and first element as value
$hour= explode("h", <use second value from above explode>);
// add it in array with key as "hour" and first element as value
$minute= explode("m", <use second value from above explode>);
// add it in array with key as "minute" and first element as value

我现在没有任何可行的示例,但我认为它会起作用。

【讨论】:

  • 我不认为我推荐这种 7 月 4 日的技术。这个任务可以通过一个函数调用来解决。
【解决方案7】:

一个简单的sscanf 会将其解析为一个数组。然后你array_combine它和你想要的键列表。

示例:

$time = "1d2h3m";

$result = array_combine(
    ['day', 'hour', 'minutes'],
    sscanf($time, '%dd%dh%dm')
);

print_r($result);

输出:

Array
(
    [day] => 1
    [hour] => 2
    [minutes] => 3
)

分解使用的sscanf 格式:

  • %d - 读取一个(有符号的)十进制数,输出一个整数
  • d - 匹配文字字符“d”
  • %d - (作为第一个)
  • h - 匹配文字字符“h”
  • %d - (作为第一个)
  • m - 匹配文字字符“m”(可选,因为它位于您想要抓取的所有内容之后)

它也适用于多个数字和负值:

$time = "42d-5h3m";
Array
(
    [day] => 42
    [hour] => -5
    [minutes] => 3
)

【讨论】:

    【解决方案8】:

    这种方法使用正则表达式提取值并使用数组将字母映射到单词。

    <?php
       // Initialize target array as empty
       $values = [];
    
       // Use $units to map the letters to the words
       $units = ['d'=>'days','h'=>'hours','m'=>'minutes'];
    
       // Test value
       $time = '1d2h3m';
    
       // Extract out all the digits and letters
       $data = preg_match_all('/(\d+)([dhm])/',$time,$matches);
    
       // The second (index 1) array has the values (digits)
       $unitValues = $matches[1];
    
       // The third (index 2) array has the keys (letters)
       $unitKeys = $matches[2];
    
       // Loop through all the values and use the key as an index into the keys
       foreach ($unitValues as $k => $v) {
           $values[$units[$unitKeys[$k]]] = $v;
       }
    

    【讨论】:

    • array_combine(array_values(array_intersect_key($units,array_fill_keys($matches[2],1))),$matches[1])) ?
    • @Sorin - 这是一个不错的解决方案。我使用 foreach 的原因是它只有三个元素,我喜欢循环的简单性。
    • 我明白,我主要是在开玩笑,我开始写它时认为它会更干净,但到最后很明显它更难遵循;) foreach 很好 php 并不是真正的函数式语言
    【解决方案9】:

    将这个简单的实现与字符串替换和数组组合一起使用。

    <?php
       $time = "1d2h3m";
       $time=str_replace(array("d","h","m")," " ,$time);
       $time=array_combine(array("day","hour","minute"),explode(" ",$time,3));
    
       print_r($time);
    ?>
    

    【讨论】:

      【解决方案10】:

      我们也可以使用str_replace()explode() 函数来实现这一点。

      $time = "1d2h3m"; 
      $time = str_replace(array("d","h","m"), "*", $time);
      $exp_time =  explode("*", $time); 
      $my_array =  array(  "day"=>(int)$exp_time[0],
                           "hour"=>(int)$exp_time[1],
                           "minutes"=>(int)$exp_time[2] ); 
      var_dump($my_array);
      

      【讨论】:

        【解决方案11】:
        $time = "1d2h3m";
        $split = preg_split("/[dhm]/",$time);
        $day = array(
            "day"     => $split[0]
            "hour"    => $split[1]
            "minutes" => $split[2]
        );
        

        【讨论】:

        • 这个答案缺少教育解释。
        【解决方案12】:

        您可以使用一条线解决方案,

        $time = "1d2h3m";
        $day = array_combine(
                   array("day","hour","months")   , 
                   preg_split("/[dhm]/", substr($time,0,-1)  )    
               );
        

        【讨论】:

        • 如果在拆分之前无条件从字符串中删除m,为什么还要拆分m?此答案缺少教育解释。
        【解决方案13】:

        这里有很多很好的答案,但我想再添加一个,以展示更通用的方法。

        function parseUnits($input, $units = array('d'=>'days','h'=>'hours','m' => 'minutes')) {
            $offset = 0;
            $idx = 0;
        
            $result = array();
            while(preg_match('/(\d+)(\D+)/', $input,$match, PREG_OFFSET_CAPTURE, $offset)) {
                $offset = $match[2][1];
        
                //ignore spaces
                $unit = trim($match[2][0]);
                if (array_key_exists($unit,$units)) { 
                    // Check if the unit was allready found
                    if (isset($result[$units[$unit]])) {  
                        throw new Exception("duplicate unit $unit");
                    }
        
                    // Check for corect order of units
                    $new_idx = array_search($unit,array_keys($units));
                    if ($new_idx < $idx) {
                        throw new Exception("unit $unit out of order");             
                    } else {
                        $idx = $new_idx;
                    }
                    $result[$units[trim($match[2][0])]] = $match[1][0];
        
                } else {
                    throw new Exception("unknown unit $unit");
                }
            }
            // add missing units
            foreach (array_keys(array_diff_key(array_flip($units),$result)) as $key) {
                $result[$key] = 0;
            }
            return $result;
        }
        print_r(parseUnits('1d3m'));
        print_r(parseUnits('8h9m'));
        print_r(parseUnits('2d8h'));
        print_r(parseUnits("3'4\"", array("'" => 'feet', '"' => 'inches')));
        print_r(parseUnits("3'", array("'" => 'feet', '"' => 'inches')));
        print_r(parseUnits("3m 5 d 5h 1M 10s", array('y' => 'years', 
                   'm' => 'months', 'd' =>'days', 'h' => 'hours', 
                   'M' => 'minutes', "'" => 'minutes', 's' => 'seconds' )));
        print_r(parseUnits("3m 5 d 5h 1' 10s", array('y' => 'years', 
                   'm' => 'months', 'd' =>'days', 'h' => 'hours',
                   'M' => 'minutes', "'" => 'minutes', 's' => 'seconds' )));
        

        【讨论】:

          【解决方案14】:

          preg_split 返回一个按模式分割的值数组(#[dhm]#)。

          list() 为每个数组元素设置值。

          $d = [];
          list($d['day'],$d['hour'],$d['minutes']) = preg_split('#[dhm]#',"1d2h3m");
          

          【讨论】:

            【解决方案15】:

            您可以使用这个 fn 来获取格式化的数组来检查字符串验证:

            function getFormatTm($str)
            {
                $tm=preg_split("/[a-zA-z]/", $str);
            
                if(count($tm)!=4) //if not a valid string
                   return "not valid string<br>";
                else
                   return array("day"=>$tm[0],"hour"=>$tm[1],"minutes"=>$tm[2]);
            }
            $t=getFormatTm("1d2h3m");
            var_dump($t);
            

            【讨论】:

              【解决方案16】:

              此答案与问题中的特定输入字符串格式并非 100% 相关。

              但它基于PHP日期解析机制(不是我自己的日期解析自行车)。

              PHP >=5.3.0 有 DateInterval 类。

              您可以从两种格式的字符串创建 DateInterval 对象:

              $i = new DateInterval('P1DT12H'); // ISO8601 format
              $i = createFromDateString('1 day + 12 hours'); // human format
              

              PHP 官方文档:http://php.net/manual/en/dateinterval.createfromdatestring.php

              在 ISO8601 格式中,P 代表“句点”。格式支持三种形式的句点:

              • PnYnMnDTnHnMnS
              • PnW
              • PT

              大写字母代表如下:

              • P 是持续时间指示符(历史上称为“周期”),位于持续时间表示的开头。
              • Y 是年数之后的年份指示符。
              • M 是月份指示符,跟在月份数的值之后。
              • W 是周指示符,紧跟在 周。
              • D 是日期指示符,跟在数字的值之后 天。
              • T 是时间指示符,位于 代表。
              • H 是小时数指示符 小时。
              • M 是分钟指示符,跟在数字的值之后 分钟。
              • S 是第二个指示符,跟在编号的值之后 秒。

              例如,“P3Y6M4DT12H30M5S”表示“三年, 六个月零四天十二小时三十分钟五 秒”。

              详情请见https://en.wikipedia.org/wiki/ISO_8601#Durations

              【讨论】:

                【解决方案17】:

                一行代码:

                $result = array_combine(array("day", "hour", "minutes"), preg_split('/[dhm]/', "1d2h3m", -1, PREG_SPLIT_NO_EMPTY));
                

                【讨论】:

                  【解决方案18】:

                  sscanf() 无疑是完成这项工作的理想工具。与preg_match() 不同,此原生文本解析功能避免了创建无用的全字符串匹配。提取的数值可以转换为整数并直接分配给结果数组中它们各自的键。

                  代码(创建引用变量):(Demo)

                  $time = "1d2h3m";
                  sscanf($time, '%dd%dh%dm', $result['day'], $result['hour'], $result['minutes']);
                  var_export($result);
                  

                  或者如果您在一个循环中处理多个字符串并希望避免创建引用变量(或者在每次迭代结束时发现 unset() 变量很难看),那么您可以将返回数组“解构”到所需的关联结构。

                  代码:(Demo)

                  $time = "1d2h3m";
                  [$result['day'], $result['hour'], $result['minutes']] = sscanf($time, '%dd%dh%dm');
                  var_export($result);
                  

                  输出:

                  array (
                    'day' => 1,
                    'hour' => 2,
                    'minutes' => 3,
                  )
                  

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 2018-01-05
                    • 2018-03-07
                    • 2013-06-13
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2020-03-31
                    相关资源
                    最近更新 更多