如何在PHP中输出一个简单的ASCII表格?

8

我有一些类似的数据:

Array
(
    [0] => Array
        (
            [a] => largeeeerrrrr
            [b] => 0
            [c] => 47
            [d] => 0
        )

    [1] => Array
        (
            [a] => bla
            [b] => 1
            [c] => 0
            [d] => 0
        )

    [2] => Array
        (
            [a] => bla3
            [b] => 0
            [c] => 0
            [d] => 0
        )

)

我希望能够产生以下输出:

title1        | title2 | title3 | title4
largeeeerrrrr | 0      | 47     | 0
bla           | 1      | 0      | 0
bla3          | 0      | 0      | 0

在PHP中实现这个最简单的方法是什么?我想避免使用针对这种简单任务的库。


步骤如下:1)找到每列所需的最长列大小(您可能需要使用一些PHP数组函数)2)打印标题,使用空格右填充每个标题3)添加|4)打印一行,使用空格右填充每个值5)为每一行重复#4 - BoltClock
可能是PHP ASCII表库的重复问题。 - azerafati
7个回答

11

使用 printf

$i=0;
foreach( $itemlist as $items)
{
 foreach ($items as $key => $value)
 {
   if ($i++==0) // print header
   {
     printf("[%010s]|",   $key );
     echo "\n";
   }
   printf("[%010s]|",   $value);
 }
 echo "\n"; // don't forget the newline at the end of the row!
}

这里使用了10个填充空格。正如BoltClock所说,您可能需要先检查最长字符串的长度,否则您的表格将因为长字符串而变形。


我该如何将那个10替换为一个变量,比如$a? - Juanjo Conti
就像替换字符串中的任何其他变量一样。printf("[%0{$a}s]|", $value); - Byron Whitlock
Printf很有用,但会破坏ASCII序列,比如颜色。 - NVRM

7

另一个库,具有自动列宽。

 <?php
 $renderer = new ArrayToTextTable($array);
 echo $renderer->getTable();

很遗憾,它不支持像“€”这样的多字节字符。 - Dorian Marchal

5
我知道这个问题很老,但它出现在我的谷歌搜索结果中,也许会对某些人有所帮助。
有一个另一个Stackoverflow问题有很好的答案,特别是这个答案指向一个名为Zend/Text/Table的Zend框架模块。
希望它有所帮助。

文档介绍

Zend\Text\Table是一个使用修饰符动态创建基于文本的表格的组件。这对于在文本电子邮件中发送结构化数据或在CLI应用程序中显示表格信息非常有用。Zend\Text\Table支持多行列、列跨度和对齐。


基本用法

$table = new Zend\Text\Table\Table(['columnWidths' => [10, 20]]);

// Either simple
$table->appendRow(['Zend', 'Framework']);

// Or verbose
$row = new Zend\Text\Table\Row();

$row->appendColumn(new Zend\Text\Table\Column('Zend'));
$row->appendColumn(new Zend\Text\Table\Column('Framework'));

$table->appendRow($row);

echo $table;

┌──────────┬────────────────────┐
│Zend      │Framework           │
|──────────|────────────────────|
│Zend      │Framework           │
└──────────┴────────────────────┘

2

如果您不想使用任何外部库,这是一个简单的类可以完成这项工作。

class StringTools
{
  public static function convertForLog($variable) {
    if ($variable === null) {
      return 'null';
    }
    if ($variable === false) {
      return 'false';
    }
    if ($variable === true) {
      return 'true';
    }
    if (is_array($variable)) {
      return json_encode($variable);
    }
    return $variable ? $variable : "";
  }

  public static function toAsciiTable($array, $fields, $wrapLength) {
    // get max length of fields
    $fieldLengthMap = [];
    foreach ($fields as $field) {
      $fieldMaxLength = 0;
      foreach ($array as $item) {
        $value = self::convertForLog($item[$field]);
        $length = strlen($value);
        $fieldMaxLength = $length > $fieldMaxLength ? $length : $fieldMaxLength;
      }
      $fieldMaxLength = $fieldMaxLength > $wrapLength ? $wrapLength : $fieldMaxLength;
      $fieldLengthMap[$field] = $fieldMaxLength;
    }

    // create table
    $asciiTable = "";
    $totalLength = 0;
    foreach ($array as $item) {
      // prepare next line
      $valuesToLog = [];
      foreach ($fieldLengthMap as $field => $maxLength) {
        $valuesToLog[$field] = self::convertForLog($item[$field]);
      }

      // write next line
      $lineIsWrapped = true;
      while ($lineIsWrapped) {
        $lineIsWrapped = false;
        foreach ($fieldLengthMap as $field => $maxLength) {
          $valueLeft = $valuesToLog[$field];
          $valuesToLog[$field] = "";
          if (strlen($valueLeft) > $maxLength) {
            $valuesToLog[$field] = substr($valueLeft, $maxLength);
            $valueLeft = substr($valueLeft, 0, $maxLength);
            $lineIsWrapped = true;
          }
          $asciiTable .= "| {$valueLeft} " . str_repeat(" ", $maxLength - strlen($valueLeft));
        }
        $totalLength = $totalLength === 0 ? strlen($asciiTable) + 1 : $totalLength;
        $asciiTable .= "|\n";
      }
    }

    // add lines before and after
    $horizontalLine = str_repeat("-", $totalLength);
    $asciiTable = "{$horizontalLine}\n{$asciiTable}{$horizontalLine}\n";
    return $asciiTable;
  }
}

这是一个如何使用它的示例,以下是终端上的结果。

public function handle() {
  $array = [
      ["name" => "something here", "description" => "a description here to see", "value" => 3],
      ["name" => "and a boolean", "description" => "this is another thing", "value" => true],
      ["name" => "a duck and a dog", "description" => "weird stuff is happening", "value" => "truly weird"],
      ["name" => "with rogue field", "description" => "should not show it", "value" => false, "rogue" => "nie"],
      ["name" => "some kind of array", "description" => "array i tell you", "value" => [3, 4, 'banana']],
      ["name" => "can i handle null?", "description" => "let's see", "value" => null],
  ];
  $table = StringTools::toAsciiTable($array, ["name", "value", "description"], 50);
  print_r($table);
}

table on terminal


1
除了Byron Whitlock之外: 您可以使用{{link1:usort()}}与回调来按最长数组值进行排序。 示例:
function lengthSort($a, $b){
    $a = strlen($a);
    $b = strlen($b);
    if ($a == $b) {
        return 0;
    }
    return ($a < $b) ? -1 : 1;
}

1

对于任何对更一般的解决方案感兴趣的人,这里是一个我在自己的代码中使用的函数,用于格式化形式为:

$data = [
  ['Header 1', 'Header 2'],
  ['Row1Cell1', 'Row1Cell2'],
  ['Row2Cell1', 'Row2Cell2'],
];

获取:

┌───────────────────────┐
│ Header 1  │ Header 2  │
├───────────────────────┤
│ Row1Cell1 │ Row1Cell2 │
│ Row2Cell1 │ Row2Cell2 │
└───────────────────────┘

代码:

// needed because str_pad doesn't play nice with Unicode
// https://dev59.com/kmUq5IYBdhLWcg3wHs2Y#67708895
// https://bugs.php.net/bug.php?id=21317
public static function pad(string $string, int $length, string $pad_string = " "): string
{
    return $string . str_repeat($pad_string, $length - mb_strlen($string));
}

public static function asciiTable(array $rows): string
{
    if (count($rows) === 0) {
        return '';
    }

    $widths = [];
    foreach ($rows as $cells) {
        foreach ($cells as $j => $cell) {
            if (($width = strlen($cell) + 2) >= ($widths[$j] ?? 0)) {
                $widths[$j] = $width;
            }
        }
    }

    $hBar = str_repeat('─', array_sum($widths) + count($widths) - 1);
    $topB = sprintf("┌%s┐", $hBar);
    $midB = sprintf("├%s┤", $hBar);
    $botB = sprintf("└%s┘", $hBar);

    $result[] = $topB;
    $fn = fn(string $c, int $w): string => self::pad(" {$c} ", $w);
    foreach ($rows as $i => $cells) {
        $result[] = sprintf("│%s│", implode('│', array_map($fn, $cells, $widths)));
        if ($i === 0) {
            $result[] = $midB;
        }
    }
    $result[] = $botB;

    return implode("\n", $result);
}

这很好,只是一个注意事项:通常情况下,数据从数据库中按列名键入的列数据中提取出来,所以为了使这段代码能够与之配合工作,您需要在前面添加一个标题行:$data = array_prepend($data, array_combine(array_keys($data[0]), array_keys($data[0]))); - dave

0
我喜欢Bryon的回答,但我希望每一列都能自动适应长度,所以我用纯PHP/无包依赖编写了以下代码。
// set up the data set you specified
$data = [
    [ 'title1' => 'largeeeerrrrr', 'title2' => 0, 'title3' => 47, 'title4' => 0 ],
    [ 'title1' => 'bla', 'title2' => 1, 'title3' => 0, 'title4' => 0 ],
    [ 'title1' => 'bla3', 'title2' => 0, 'title3' => 0, 'title4' => 0 ]
];

// parse array to make it easier to use
foreach ($data as $i => $lines)
{
    foreach ($lines as $key => $value) {
        $columns[$key][0] = $key;
        $columns[$key][] = $value;
        $rows[$i][] = $value;
    }
}
$rows = array_prepend($rows, array_keys($columns));
$lengths = array_values(array_map(fn($x) => max(array_map('strlen', $x)) , $columns));

// output ascii table
foreach ($rows as $row) {
    foreach ($row as $key => $data) {
        $length = $lengths[$key];
        $data = (is_bool($data)) ? (($data) ? 'true' : 'false') : $data;

        echo str_pad($data, $length, ' ', STR_PAD_RIGHT) . ' | ';
    }
    echo "\n";
}


输出(完全符合用户要求,但非常容易自定义):
title1        | title2 | title3 | title4 | 
largeeeerrrrr | 0      | 47     | 0      | 
bla           | 1      | 0      | 0      | 
bla3          | 0      | 0      | 0      |

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