我有一些数据,例如:
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 中实现此目的最简单的方法是什么?我想避免使用库来完成如此简单的任务。
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 所说,您可能需要先检查最长字符串的长度,否则您的桌子将被长字符串顶住。
另一个库具有自动列宽。
<?php
$renderer = new ArrayToTextTable($array);
echo $renderer->getTable();
我知道这个问题很老了,但它出现在我的谷歌搜索中,也许它对某人有帮助。
还有另一个 Stackoverflow 问题,有很好的答案,尤其是 这个问题,它指向一个名为 Zend/Text/Table 的 Zend Framework 模块。
希望有帮助。
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 │
└──────────┴────────────────────┘
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);
}
$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://stackoverflow.com/a/67708895/337554
// 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);
}
usort() 和回调来按最长数组值排序。 示例:
function lengthSort($a, $b){
$a = strlen($a);
$b = strlen($b);
if ($a == $b) {
return 0;
}
return ($a < $b) ? -1 : 1;
}
// 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 |
public static function arrayToTextTable(array $data): string
{
if (empty($data)) {
return "No data available.";
}
// 提取列名(从第一行的键中获取)
$headers = array_keys($data[0]);
$headerRow = array_combine($headers, $headers);
// 添加列名作为表头
array_unshift($data, $headerRow);
// Calculate column widths dynamically
$columnWidths = [];
foreach ($data as $row) {
foreach ($row as $key => $value) {
$valueLength = mb_strlen((string) $value);
$columnWidths[$key] = max($columnWidths[$key] ?? 0, $valueLength);
}
}
// Add padding for aesthetics
foreach ($columnWidths as &$width) {
$width += 2;
}
// Build horizontal separator line
$line = "+";
unset($width);
foreach ($columnWidths as $width) {
$line .= str_repeat("-", $width) . "+";
}
// Build the table
$output = $line . "\n";
foreach ($data as $index => $row) {
$output .= "|";
foreach ($row as $key => $value) {
$output .= " " . str_pad((string) $value, $columnWidths[$key] - 1) . "|";
}
$output .= "\n";
if ($index === 0) { // Add header separator
$output .= $line . "\n";
}
}
$output .= $line;
return "\n\n" . preg_replace('/^/m', "\t", $output) . "\n";
}
输出如下自动使用数组的键名作为头,这是一个命名数组
2025-01-20 00:25:02 [127.0.0.1][-][-][info][performance] LN-064 - IN20250120039020759653 - 第 2 行, 执行计划
+----+--------------+-------------------------------+------------+--------+---------------+-----------+---------+--------------+------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------+-------------------------------+------------+--------+---------------+-----------+---------+--------------+------+----------+--------------------------+
| 1 | PRIMARY | <derived2> | | ALL | | | | | 1299 | 100.00 | |
| 1 | UPDATE | io | | eq_ref | ID | ID | 122 | detail.IO_ID | 1 | 10.00 | Using where |
| 2 | DERIVED | cdt_prd_prod_wp_day_io_detail | | index | idx_IO_ID | idx_IO_ID | 127 | | 4337 | 10.00 | Using where; Using index |
| 3 | SUBQUERY | up | | ref | batchNo | batchNo | 123 | const | 2 | 2.50 | Using where |
| 4 | UNION | cdt_prd_prod_wp_day_io_detail | | index | idx_IO_ID | idx_IO_ID | 127 | | 4337 | 10.00 | Using where; Using index |
| 5 | SUBQUERY | up | | ref | batchNo | batchNo | 123 | const | 2 | 2.50 | Using where |
| 6 | UNION | cdt_prd_prod_wp_day_io_detail | | index | idx_IO_ID | idx_IO_ID | 127 | | 4337 | 10.00 | Using where; Using index |
| 7 | SUBQUERY | up | | ref | batchNo | batchNo | 123 | const | 2 | 2.50 | Using where |
| | UNION RESULT | <union2,4,6> | | ALL | | | | | | | Using temporary |
+----+--------------+-------------------------------+------------+--------+---------------+-----------+---------+--------------+------+----------+--------------------------+
2025-01-20 00:25:02 [127.0.0.1][-][-][info][performance] LN-064 - IN20250120039020759653 - 第 2 行, 执行分析: SELECT trx_rows_locked, trx_rows_modified, trx_id, trx_state, trx_started, trx_mysql_thread_id FROM INFORMATION_SCHEMA.INNODB_TRX;
+-----------------+-------------------+------------+-----------+---------------------+---------------------+
| trx_rows_locked | trx_rows_modified | trx_id | trx_state | trx_started | trx_mysql_thread_id |
+-----------------+-------------------+------------+-----------+---------------------+---------------------+
| 22382 | 32 | 3658441334 | RUNNING | 2025-01-20 00:25:02 | 443019 |
+-----------------+-------------------+------------+-----------+---------------------+---------------------+
2025-01-20 00:25:02 [127.0.0.1][-][-][info][performance] LN-064 - IN20250120039020759653 - 第 2 行, 分析完毕
2025-01-20 00:25:07 [127.0.0.1][-][-][info][performance] 360LN-064 - IN20250120039020759653 - SQL 数量 = 33,花费总时间 = 0.69 秒
2025-01-20 00:25:07 [127.0.0.1][-][-][info][performance] 360LN-064 - IN20250120039020759653 - 执行明细,打印阈值:0.04 秒,
+-------+-------+----------+--------+---------------------+-------+
| index | print | duration | memory | timestamp | level |
+-------+-------+----------+--------+---------------------+-------+
| 0 | Y | 0.05 | 5.31 | 2025-01-20 00:25:02 | 0 |
| 1 | | 0.02 | 5.35 | 2025-01-20 00:25:02 | 0 |
| 2 | | 0.02 | 5.34 | 2025-01-20 00:25:02 | 0 |
| 3 | | 0.01 | 5.34 | 2025-01-20 00:25:02 | 0 |
| 4 | | 0.01 | 5.36 | 2025-01-20 00:25:02 | 0 |
| 5 | | 0.01 | 5.37 | 2025-01-20 00:25:02 | 0 |