Project Icon

table

Node.js文本表格生成库 支持复杂数据格式

Table是一个Node.js文本表格生成库,支持全角字符和ANSI转义码。该库提供可配置的边框样式、内容对齐、填充和列宽等功能,能够处理复杂数据格式并自动换行文本。Table适用于命令行或纯文本环境中展示结构化数据的场景,操作简便直观。

表格

生成一个表示数组数据的文本表格字符串。

Github action 状态 Coveralls NPM 版本 规范代码风格 Twitter 关注

展示登月任务列表的表格演示。

特性

  • 支持包含全角字符的字符串。
  • 支持包含ANSI 转义码的字符串。
  • 可配置的边框字符。
  • 可配置每列的内容对齐方式。
  • 可配置每列的内容填充。
  • 可配置列宽。
  • 文本自动换行。

安装

npm install table

给我买杯咖啡 成为赞助人

使用

import { table } from 'table';

// 使用 CommonJS?
// const { table } = require('table');

const data = [
    ['0A', '0B', '0C'],
    ['1A', '1B', '1C'],
    ['2A', '2B', '2C']
];

console.log(table(data));
╔════╤════╤════╗
║ 0A │ 0B │ 0C ║
╟────┼────┼────╢
║ 1A │ 1B │ 1C ║
╟────┼────┼────╢
║ 2A │ 2B │ 2C ║
╚════╧════╧════╝

API

table

返回表格格式的字符串

参数:

  • data: 要显示的数据

    • 类型:any[][]
    • 必需:true
  • config: 表格配置

    • 类型:object
    • 必需:false

config.border

类型:{ [type: string]: string }
默认值:honeywell 模板

自定义边框。键可以是以下任何一个:

  • topLeft, topRight, topBody,topJoin
  • bottomLeft, bottomRight, bottomBody, bottomJoin
  • joinLeft, joinRight, joinBody, joinJoin
  • bodyLeft, bodyRight, bodyJoin
  • headerJoin
const data = [
  ['0A', '0B', '0C'],
  ['1A', '1B', '1C'],
  ['2A', '2B', '2C']
];

const config = {
  border: {
    topBody: `─`,
    topJoin: `┬`,
    topLeft: `┌`,
    topRight: `┐`,

    bottomBody: `─`,
    bottomJoin: `┴`,
    bottomLeft: `└`,
    bottomRight: `┘`,

    bodyLeft: `│`,
    bodyRight: `│`,
    bodyJoin: `│`,

    joinBody: `─`,
    joinLeft: `├`,
    joinRight: `┤`,
    joinJoin: `┼`
  }
};

console.log(table(data, config));
┌────┬────┬────┐
│ 0A │ 0B │ 0C │
├────┼────┼────┤
│ 1A │ 1B │ 1C │
├────┼────┼────┤
│ 2A │ 2B │ 2C │
└────┴────┴────┘

config.drawVerticalLine

类型:(lineIndex: number, columnCount: number) => boolean
默认值:() => true

用于决定是否绘制垂直线。此回调函数会为表格的每个垂直边框调用。 如果表格有 n 列,那么 index 参数会依次接收 [0, n] 范围内的所有数字(包括边界值)。

const data = [
  ['0A', '0B', '0C'],
  ['1A', '1B', '1C'],
  ['2A', '2B', '2C'],
  ['3A', '3B', '3C'],
  ['4A', '4B', '4C']
];

const config = {
  drawVerticalLine: (lineIndex, columnCount) => {
    return lineIndex === 0 || lineIndex === columnCount;
  }
};

console.log(table(data, config));

╔════════════╗
║ 0A  0B  0C ║
╟────────────╢
║ 1A  1B  1C ║
╟────────────╢
║ 2A  2B  2C ║
╟────────────╢
║ 3A  3B  3C ║
╟────────────╢
║ 4A  4B  4C ║
╚════════════╝

config.drawHorizontalLine

类型:(lineIndex: number, rowCount: number) => boolean
默认值:() => true

用于决定是否绘制水平线。此回调函数会为表格的每个水平边框调用。 如果表格有 n 行,那么 index 参数会依次接收 [0, n] 范围内的所有数字(包括边界值)。 如果表格有 n 行且包含表头,那么范围将是 [0, n+1](包括边界值)。

const data = [
  ['0A', '0B', '0C'],
  ['1A', '1B', '1C'],
  ['2A', '2B', '2C'],
  ['3A', '3B', '3C'],
  ['4A', '4B', '4C']
];

const config = {
  drawHorizontalLine: (lineIndex, rowCount) => {
    return lineIndex === 0 || lineIndex === 1 || lineIndex === rowCount - 1 || lineIndex === rowCount;
  }
};

console.log(table(data, config));

╔════╤════╤════╗
║ 0A │ 0B │ 0C ║
╟────┼────┼────╢
║ 1A │ 1B │ 1C ║
║ 2A │ 2B │ 2C ║
║ 3A │ 3B │ 3C ║
╟────┼────┼────╢
║ 4A │ 4B │ 4C ║
╚════╧════╧════╝

config.singleLine

类型:boolean 默认值:false

如果设为true,表格内部的水平线将不会绘制。此选项也会覆盖指定的config.drawHorizontalLine

const data = [
  ['-rw-r--r--', '1', 'pandorym', 'staff', '1529', 'May 23 11:25', 'LICENSE'],
  ['-rw-r--r--', '1', 'pandorym', 'staff', '16327', 'May 23 11:58', 'README.md'],
  ['drwxr-xr-x', '76', 'pandorym', 'staff', '2432', 'May 23 12:02', 'dist'],
  ['drwxr-xr-x', '634', 'pandorym', 'staff', '20288', 'May 23 11:54', 'node_modules'],
  ['-rw-r--r--', '1,', 'pandorym', 'staff', '525688', 'May 23 11:52', 'package-lock.json'],
  ['-rw-r--r--@', '1', 'pandorym', 'staff', '2440', 'May 23 11:25', 'package.json'],
  ['drwxr-xr-x', '27', 'pandorym', 'staff', '864', 'May 23 11:25', 'src'],
  ['drwxr-xr-x', '20', 'pandorym', 'staff', '640', 'May 23 11:25', 'test'],
];

const config = {
  singleLine: true
};

console.log(table(data, config));
╔═════════════╤═════╤══════════╤═══════╤════════╤══════════════╤═══════════════════╗
║ -rw-r--r--  │ 1   │ pandorym │ staff │ 1529   │ May 23 11:25 │ LICENSE           ║
║ -rw-r--r--  │ 1   │ pandorym │ staff │ 16327  │ May 23 11:58 │ README.md         ║
║ drwxr-xr-x  │ 76  │ pandorym │ staff │ 2432   │ May 23 12:02 │ dist              ║
║ drwxr-xr-x  │ 634 │ pandorym │ staff │ 20288  │ May 23 11:54 │ node_modules      ║
║ -rw-r--r--  │ 1,  │ pandorym │ staff │ 525688 │ May 23 11:52 │ package-lock.json ║
║ -rw-r--r--@ │ 1   │ pandorym │ staff │ 2440   │ May 23 11:25 │ package.json      ║
║ drwxr-xr-x  │ 27  │ pandorym │ staff │ 864    │ May 23 11:25 │ src               ║
║ drwxr-xr-x  │ 20  │ pandorym │ staff │ 640    │ May 23 11:25 │ test              ║
╚═════════════╧═════╧══════════╧═══════╧════════╧══════════════╧═══════════════════╝

config.columns

类型:Column[] | { [columnIndex: number]: Column }

列特定配置。

config.columns[*].width

类型:number 默认值:该列的最大单元格宽度

列宽(不包括内边距)。

const data = [
  ['0A', '0B', '0C'],
  ['1A', '1B', '1C'],
  ['2A', '2B', '2C']
];

const config = {
  columns: {
    1: { width: 10 }
  }
};

console.log(table(data, config));
╔════╤════════════╤════╗
║ 0A │ 0B         │ 0C ║
╟────┼────────────┼────╢
║ 1A │ 1B         │ 1C ║
╟────┼────────────┼────╢
║ 2A │ 2B         │ 2C ║
╚════╧════════════╧════╝

config.columns[*].alignment

类型:'center' | 'justify' | 'left' | 'right' 默认值:'left'

单元格内容的水平对齐方式

const data = [
  ['0A', '0B', '0C', '0D 0E 0F'],
  ['1A', '1B', '1C', '1D 1E 1F'],
  ['2A', '2B', '2C', '2D 2E 2F'],
];

const config = {
  columnDefault: {
    width: 10,
  },
  columns: [
    { alignment: 'left' },
    { alignment: 'center' },
    { alignment: 'right' },
    { alignment: 'justify' }
  ],
};

console.log(table(data, config));
╔════════════╤════════════╤════════════╤════════════╗
║ 0A         │     0B     │         0C │ 0D  0E  0F ║
╟────────────┼────────────┼────────────┼────────────╢
║ 1A         │     1B     │         1C │ 1D  1E  1F ║
╟────────────┼────────────┼────────────┼────────────╢
║ 2A         │     2B     │         2C │ 2D  2E  2F ║
╚════════════╧════════════╧════════════╧════════════╝

config.columns[*].verticalAlignment

类型:'top' | 'middle' | 'bottom' 默认值:'top'

单元格内容的垂直对齐方式

const data = [
  ['A', 'B', 'C', 'DEF'],
];

const config = {
  columnDefault: {
    width: 1,
  },
  columns: [
    { verticalAlignment: 'top' },
    { verticalAlignment: 'middle' },
    { verticalAlignment: 'bottom' },
  ],
};

console.log(table(data, config));
╔═══╤═══╤═══╤═══╗
║ A │   │   │ D ║
║   │ B │   │ E ║
║   │   │ C │ F ║
╚═══╧═══╧═══╧═══╝

config.columns[*].paddingLeft

类型:number 默认值:1

用于在左侧填充内容的空格数。

config.columns[*].paddingRight

类型:number 默认值:1

用于在右侧填充内容的空格数。

paddingLeftpaddingRight选项不计入列宽。因此,如果列的width为5,paddingLeft为2,paddingRight为2,那么总宽度将为9。

const data = [
  ['0A', 'AABBCC', '0C'],
  ['1A', '1B', '1C'],
  ['2A', '2B', '2C']
];

const config = {
  columns: [
    {
      paddingLeft: 3
    },
    {
      width: 2,
      paddingRight: 3
    }
  ]
};

console.log(table(data, config));
╔══════╤══════╤════╗
║   0A │ AA   │ 0C ║
║      │ BB   │    ║
║      │ CC   │    ║
╟──────┼──────┼────╢
║   1A │ 1B   │ 1C ║
╟──────┼──────┼────╢
║   2A │ 2B   │ 2C ║
╚══════╧══════╧════╝

config.columns[*].truncate

类型:number 默认值:Infinity 内容将被截断的字符数。

为了处理超出容器宽度的内容,table包实现了文本换行。然而,有时你可能想要截断太长而无法在表格中显示的内容。

const data = [
  ['Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus pulvinar nibh sed mauris convallis dapibus. Nunc venenatis tempus nulla sit amet viverra.']
];

const config = {
  columns: [
    {
      width: 20,
      truncate: 100
    }
  ]
};

console.log(table(data, config));
╔══════════════════════╗
║ Lorem ipsum dolor si ║
║ t amet, consectetur  ║
║ adipiscing elit. Pha ║
║ sellus pulvinar nibh ║
║ sed mauris convall…  ║
╚══════════════════════╝

config.columns[*].wrapWord

类型:boolean 默认值:false

table包实现了自动文本换行,即宽度大于容器宽度的文本将在最近的空格或以下特殊字符处分成多行:\|/_.,;-

wrapWordfalse时:

const data = [
    ['Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus pulvinar nibh sed mauris convallis dapibus. Nunc venenatis tempus nulla sit amet viverra.']
];

const config = {
  columns: [ { width: 20 } ]
};

console.log(table(data, config));
╔══════════════════════╗
║ Lorem ipsum dolor si ║
║ t amet, consectetur  ║
║ adipiscing elit. Pha ║
║ sellus pulvinar nibh ║
║ sed mauris convallis ║
║ dapibus. Nunc venena ║
║ tis tempus nulla sit ║
║ amet viverra.        ║
╚══════════════════════╝

wrapWordtrue时:

╔══════════════════════╗
║ Lorem ipsum dolor    ║
║ sit amet,            ║
║ consectetur          ║
║ adipiscing elit.     ║
║ Phasellus pulvinar   ║
║ nibh sed mauris      ║
║ convallis dapibus.   ║
║ Nunc venenatis       ║
║ tempus nulla sit     ║
║ amet viverra.        ║
╚══════════════════════╝

config.columnDefault

类型:Column 默认值:{}

所有列的默认配置。特定列的设置将覆盖默认值。

config.header

类型:object

表头配置。

已弃用,推荐使用新的跨越单元格API。

表头配置继承了大部分列的配置,除了:

  • content {string}:表头内容。
  • width::根据内容宽度自动计算。
  • alignment::默认为center
  • verticalAlignment::不支持。
  • config.border.topJoin将变为config.border.topBody以获得更好的外观。
const data = [
      ['0A', '0B', '0C'],
      ['1A', '1B', '1C'],
      ['2A', '2B', '2C'],
    ];

const config = {
  columnDefault: {
    width: 10,
  },
  header: {
    alignment: 'center',
    content: 'THE HEADER\nThis is the table about something',
  },
}

console.log(table(data, config));
╔══════════════════════════════════════╗
║              THE HEADER              ║
║  This is the table about something   ║
╟────────────┬────────────┬────────────╢
║ 0A         │ 0B         │ 0C         ║
╟────────────┼────────────┼────────────╢
║ 1A         │ 1B         │ 1C         ║
╟────────────┼────────────┼────────────╢
║ 2A         │ 2B         │ 2C         ║
╚════════════╧════════════╧════════════╝

config.spanningCells

类型:SpanningCellConfig[]

跨越单元格配置。

配置应该很直观:只需指定一个包含左上角单元格位置以及从该位置扩展的列数和/或行数的最小单元格配置数组。

重叠单元格的内容将被忽略,以保持data形状的一致性。

默认情况下,左上角单元格所属列的配置将应用于整个跨越单元格,除了:

  • width将是所有跨越列的宽度之和。
  • paddingRight将有意从最右边的列接收。

可以为每个跨越单元格配置高级自定义列样式,以覆盖默认行为。

const data = [
  ['Test Coverage Report', '', '', '', '', ''],
  ['Module', 'Component', 'Test Cases', 'Failures', 'Durations', 'Success Rate'],
  ['Services', 'User', '50', '30', '3m 7s', '60.0%'],
  ['', 'Payment', '100', '80', '7m 15s', '80.0%'],
  ['Subtotal', '', '150', '110', '10m 22s', '73.3%'],
  ['Controllers', 'User', '24', '18', '1m 30s', '75.0%'],
  ['', 'Payment', '30', '24', '50s', '80.0%'],
  ['Subtotal', '', '54', '42', '2m 20s', '77.8%'],
  ['Total', '', '204', '152', '12m 42s', '74.5%'],
];

const config = {
  columns: [
    { alignment: 'center', width: 12 },
    { alignment: 'center', width: 10 },
    { alignment: 'right' },
    { alignment: 'right' },
    { alignment: 'right' },
    { alignment: 'right' }
  ],
  spanningCells: [
    { col: 0, row: 0, colSpan: 6 },
    { col: 0, row: 2, rowSpan: 2, verticalAlignment: 'middle'},
    { col: 0, row: 4, colSpan: 2, alignment: 'right'},
    { col: 0, row: 5, rowSpan: 2, verticalAlignment: 'middle'},
    { col: 0, row: 7, colSpan: 2, alignment: 'right' },
    { col: 0, row: 8, colSpan: 2, alignment: 'right' }
  ],
};

console.log(table(data, config));
╔════════════════════════════════════════════════════════════════════════════════╗
║                                 测试覆盖率报告                                 ║
╟──────────────┬────────────┬────────────┬──────────┬───────────┬──────────────╢
║     模块     │    组件    │  测试用例  │  失败数  │   耗时    │   成功率     ║
╟──────────────┼────────────┼────────────┼──────────┼───────────┼──────────────╢
║              │   用户     │         50 │       30 │     3分7秒│        60.0% ║
║   服务       ├────────────┼────────────┼──────────┼───────────┼──────────────╢
║              │   支付     │        100 │       80 │   7分15秒 │        80.0% ║
╟──────────────┴────────────┼────────────┼──────────┼───────────┼──────────────╢
║                    小计   │        150 │      110 │  10分22秒 │        73.3% ║
╟──────────────┬────────────┼────────────┼──────────┼───────────┼──────────────╢
║              │   用户     │         24 │       18 │   1分30秒 │        75.0% ║
║   控制器     ├────────────┼────────────┼──────────┼───────────┼──────────────╢
║              │   支付     │         30 │       24 │      50秒 │        80.0% ║
╟──────────────┴────────────┼────────────┼──────────┼───────────┼──────────────╢
║                    小计   │         54 │       42 │   2分20秒 │        77.8% ║
╟───────────────────────────┼────────────┼──────────┼───────────┼──────────────╢
║                    总计   │        204 │      152 │  12分42秒 │        74.5% ║
╚═══════════════════════════╧════════════╧══════════╧═══════════╧══════════════╝

createStream

table 包导出 createStream 函数用于绘制表格并追加行。

参数:

  • config:table 的配置相同,除了 config.columnDefault.widthconfig.columnCount 必须提供。
import { createStream } from 'table';

const config = {
  columnDefault: {
    width: 50
  },
  columnCount: 1
};

const stream = createStream(config);

setInterval(() => {
  stream.write([new Date()]);
}, 500);

流式输出当前日期。

table 包使用 ANSI 转义码在打印新行时覆盖最后一行的输出。

底层实现在这个 Stack Overflow 回答 中有解释。

流式输出支持静态表格的所有配置属性和功能(如自动文本换行、对齐和填充),例如:

import { createStream } from 'table';

import _ from 'lodash';

const config = {
  columnDefault: {
    width: 50
  },
  columnCount: 3,
  columns: [
    {
      width: 10,
      alignment: 'right'
    },
    { alignment: 'center' },
    { width: 10 }

  ]
};

const stream = createStream(config);

let i = 0;

setInterval(() => {
  let random;

  random = _.sample('abcdefghijklmnopqrstuvwxyz', _.random(1, 30)).join('');

  stream.write([i++, new Date(), random]);
}, 500);

流式输出随机数据。

getBorderCharacters

参数:

  • template
    • 类型: 'honeywell' | 'norc' | 'ramac' | 'void'
    • 必需: true

你可以使用 getBorderCharacters 函数加载预定义的边框模板之一。

import { table, getBorderCharacters } from 'table';

const data = [
  ['0A', '0B', '0C'],
  ['1A', '1B', '1C'],
  ['2A', '2B', '2C']
];

const config = {
  border: getBorderCharacters(`模板名称`)
};

console.log(table(data, config));
# honeywell

╔════╤════╤════╗
║ 0A │ 0B │ 0C ║
╟────┼────┼────╢
║ 1A │ 1B │ 1C ║
╟────┼────┼────╢
║ 2A │ 2B │ 2C ║
╚════╧════╧════╝

# norc

┌────┬────┬────┐
│ 0A │ 0B │ 0C │
├────┼────┼────┤
│ 1A │ 1B │ 1C │
├────┼────┼────┤
│ 2A │ 2B │ 2C │
└────┴────┴────┘

# ramac (ASCII; 用于不支持 Unicode 字符的终端)

+----+----+----+
| 0A | 0B | 0C |
|----|----|----|
| 1A | 1B | 1C |
|----|----|----|
| 2A | 2B | 2C |
+----+----+----+

# void (无边框; 参见文档的"无边框表格"部分)

 0A  0B  0C

 1A  1B  1C

 2A  2B  2C

如果你想贡献新的边框模板,请提出 issue

无边框表格

简单使用 void 边框字符模板会创建一个有很多不必要空格的表格。

要创建一个更美观的表格,重置填充并移除连接行,例如:


const output = table(data, {
    border: getBorderCharacters('void'),
    columnDefault: {
        paddingLeft: 0,
        paddingRight: 1
    },
    drawHorizontalLine: () => false
    }
);

console.log(output);
0A 0B 0C
1A 1B 1C
2A 2B 2C
项目侧边栏1项目侧边栏2
推荐项目
Project Cover

豆包MarsCode

豆包 MarsCode 是一款革命性的编程助手,通过AI技术提供代码补全、单测生成、代码解释和智能问答等功能,支持100+编程语言,与主流编辑器无缝集成,显著提升开发效率和代码质量。

Project Cover

AI写歌

Suno AI是一个革命性的AI音乐创作平台,能在短短30秒内帮助用户创作出一首完整的歌曲。无论是寻找创作灵感还是需要快速制作音乐,Suno AI都是音乐爱好者和专业人士的理想选择。

Project Cover

有言AI

有言平台提供一站式AIGC视频创作解决方案,通过智能技术简化视频制作流程。无论是企业宣传还是个人分享,有言都能帮助用户快速、轻松地制作出专业级别的视频内容。

Project Cover

Kimi

Kimi AI助手提供多语言对话支持,能够阅读和理解用户上传的文件内容,解析网页信息,并结合搜索结果为用户提供详尽的答案。无论是日常咨询还是专业问题,Kimi都能以友好、专业的方式提供帮助。

Project Cover

阿里绘蛙

绘蛙是阿里巴巴集团推出的革命性AI电商营销平台。利用尖端人工智能技术,为商家提供一键生成商品图和营销文案的服务,显著提升内容创作效率和营销效果。适用于淘宝、天猫等电商平台,让商品第一时间被种草。

Project Cover

吐司

探索Tensor.Art平台的独特AI模型,免费访问各种图像生成与AI训练工具,从Stable Diffusion等基础模型开始,轻松实现创新图像生成。体验前沿的AI技术,推动个人和企业的创新发展。

Project Cover

SubCat字幕猫

SubCat字幕猫APP是一款创新的视频播放器,它将改变您观看视频的方式!SubCat结合了先进的人工智能技术,为您提供即时视频字幕翻译,无论是本地视频还是网络流媒体,让您轻松享受各种语言的内容。

Project Cover

美间AI

美间AI创意设计平台,利用前沿AI技术,为设计师和营销人员提供一站式设计解决方案。从智能海报到3D效果图,再到文案生成,美间让创意设计更简单、更高效。

Project Cover

稿定AI

稿定设计 是一个多功能的在线设计和创意平台,提供广泛的设计工具和资源,以满足不同用户的需求。从专业的图形设计师到普通用户,无论是进行图片处理、智能抠图、H5页面制作还是视频剪辑,稿定设计都能提供简单、高效的解决方案。该平台以其用户友好的界面和强大的功能集合,帮助用户轻松实现创意设计。

投诉举报邮箱: service@vectorlightyear.com
@2024 懂AI·鲁ICP备2024100362号-6·鲁公网安备37021002001498号