【问题标题】:Is there a better way than this of creating an PHP array from TSV data?有没有比从 TSV 数据创建 PHP 数组更好的方法?
【发布时间】:2012-05-08 00:13:25
【问题描述】:

假设一个带有标题行的制表符分隔值 (TSV) 文件,如何创建一个以标题字段为键、数据字段为数据的 PHP 数组?

假设 $txtArray 包含文件中的所有行,

$hdrArray = explode( "\t", $txtArray[0]);
$i = 0;
foreach ($hdrArray as $hdr) {
  $heads[$hdr] = '';
  $headerNames[$i++] = $hdr;
}
for ($i = 1; $i < (count($txtArray) - 1); $i++ ) {
  $datArray = explode( "\t", $txtArray[$i]);
  if (count($datArray) > 1) {
    for($j = 0; $j < count($datArray); $j++) {
      $heads[$headerNames[$j]] = $datArray[$j];
    }
  }
  # process the line
}

对于文件的每一行中的所有字段,我都有包含 field_name => field_data 的 $heads。有没有更好的编码方式?

【问题讨论】:

  • 您的代码无法正常工作,能否提供您正在解析的文件的示例?而且由于这是一个技术问题,它更适合 Stack Overflow,除非我遗漏了什么?

标签: php text-processing


【解决方案1】:

什么才是“更好”?

您可以使用正则表达式拆分使其更加健壮,但如果您可以控制源 CSV,则不必担心脏数据。

我看到的一个明显优化是缓存 count() 结果。

用途:

for ($i = 1, $c=count($txtArray); $i < c - 1); $i++)

代替:

for ($i = 1; $i < (count($txtArray) - 1); $i++ )

每次调用 count() 都会重新计算结果。计算一次就足够了,您只需保存结果即可。

我不明白你为什么需要:

if (count($datArray) > 1)

如果您使用的是“干净”数据,则每行应该有固定数量的值,因此无需对它们进行计数检查无。为了加快速度,您可以通过计算标题中的行数来缓存行长度。

之后:

$hdrArray = explode( "\t", $txtArray[0]);

做:

$c2 = count($hdrArray);

然后在第二个for循环中使用:

for($j = 0; $j < $c2; $j++)

如果您确实不得不担心空行,那么搜索空行并在循环中跳过它可能会更快。

像这样:

// skip the row if the $datArray contains an empty array
if($datArray == array()) {
    continue;
}
$heads[$headerNames[$j]] = $datArray[$j];       

总而言之:

$hdrArray = explode( "\t", $txtArray[0]);
$c2 = count($hdrArray);

// it has an iterator variable...
// I don't understand why you wouldn't use a for loop here
$i = 0;
foreach ($hdrArray as $hdr) {
    $heads[$hdr] = '';
    $headerNames[$i++] = $hdr;
}

for ($i = 1, $c = count($txtArray); $i < $c - 1; $i++) {
    $datArray = explode( "\t", $txtArray[$i]);
    for($j = 0; $j < $c2; $j++)
        // skip the row if the $datArray contains an empty array
        if($datArray == array()) {
            continue;
        }
        $heads[$headerNames[$j]] = $datArray[$j];
    }      
}

我假设您的第一个实现有效,源数据实际上是 CSV(即固定数量的行/列。

我所做的只是应用一些简单(和常见)的优化来减少不必要的计算次数。一段时间后您就会习惯看到的非常基本的东西。

【讨论】:

    【解决方案2】:

    假设您的输入文件如下所示:

    head1 head2 head3
    v11   v12   v13
    v21   v22   v23
    v31   v32   v33
    

    解析它的更简单方法是:

    $hdrArray = explode("\t", $txtArray[0]); 
    // array(3) { [0]=> string(5) "head1" [1]=> string(5) "head2" [2]=> string(6) "head3 " }
    
    $length = count($txtArray) - 1; // having count in the for loop means it'll be executed at each iteration
    $data   = array();
    
    for($i = 1; $i <= $length; $i++) {
        $datArray = explode("\t", $txtArray[$i]); 
        // array(3) { [0]=> string(3) "v11" [1]=> string(3) "v12" [2]=> string(4) "v13 " }
    
        foreach($hdrArray as $key => $header) {
            $data[$i - 1][$header] = $datArray[$key]; // $i - 1 to start your $data array from 0
        }   
    }
    

    您的 $data 数组看起来像:

    array(3) {
      [0]=>
      array(3) {
        ["head1"]=>
        string(3) "v11"
        ["head2"]=>
        string(3) "v12"
        ["head3"]=>
        string(4) "v13"
      }
      [1]=>
      array(3) {
        ["head1"]=>
        string(3) "v21"
        ["head2"]=>
        string(3) "v22"
        ["head3"]=>
        string(4) "v23"
      }
      [2]=>
      array(3) {
        ["head1"]=>
        string(3) "v31"
        ["head2"]=>
        string(3) "v32"
        ["head3"]=>
        string(3) "v33"
      }
    }
    

    您可以完全跳过count,但需要额外的迭代:

    foreach($txtArray as $i => $value) {
        if($i == 0) continue; // header, move on
    
        $datArray = explode("\t", $value); 
    
        foreach($hdrArray as $key => $header) {
            $data[$i - 1][$header] = $datArray[$key];
        }   
    }
    

    这实际上可能比使用 count 快一点,因为第一次迭代立即停止并且循环继续到下一次。但这实际上取决于您的阵列的大小,而收益/损失可能微不足道。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-02-04
      • 1970-01-01
      • 1970-01-01
      • 2012-10-28
      • 2014-02-16
      • 2011-03-09
      • 2011-07-28
      相关资源
      最近更新 更多