Greg Pittman 发布的文章

无论你需要的东西简单(如一个购物清单)亦或复杂(如色卡生成器) ,PostgreSQL 命令都能使它变得容易起来。

PostgreSQL 入门一文中, 我解释了如何安装、设置和开始使用这个开源数据库软件。不过,使用 PostgreSQL 中的命令可以做更多事情。

例如,我使用 Postgres 来跟踪我的杂货店购物清单。我的大多数杂货店购物是在家里进行的,而且每周进行一次大批量的采购。我去几个不同的地方购买清单上的东西,因为每家商店都提供特定的选品或质量,亦或更好的价格。最初,我制作了一个 HTML 表单页面来管理我的购物清单,但这样无法保存我的输入内容。因此,在想到要购买的物品时我必须马上列出全部清单,然后到采购时我常常会忘记一些我需要或想要的东西。

相反,使用 PostgreSQL,当我想到需要的物品时,我可以随时输入,并在购物前打印出来。你也可以这样做。

创建一个简单的购物清单

首先,输入 psql 命令进入数据库,然后用下面的命令创建一个表:

Create table groc (item varchar(20), comment varchar(10));

输入如下命令在清单中加入商品:

insert into groc values ('milk', 'K');
insert into groc values ('bananas', 'KW');

括号中有两个信息(逗号隔开):前面是你需要买的东西,后面字母代表你要购买的地点以及哪些东西是你每周通常都要买的(W)。

因为 psql 有历史记录,你可以按向上键在括号内编辑信息,而无需输入商品的整行信息。

在输入一小部分商品后,输入下面命令来检查前面的输入内容。

Select * from groc order by comment;

      item      | comment
----------------+---------
 ground coffee  | H
 butter         | K
 chips          | K
 steak          | K
 milk           | K
 bananas        | KW
 raisin bran    | KW
 raclette       | L
 goat cheese    | L
 onion          | P
 oranges        | P
 potatoes       | P
 spinach        | PW
 broccoli       | PW
 asparagus      | PW
 cucumber       | PW
 sugarsnap peas | PW
 salmon         | S
(18 rows)

此命令按 comment 列对结果进行排序,以便按购买地点对商品进行分组,从而使你的购物更加方便。

使用 W 来指明你每周要买的东西,当你要清除表单为下周的列表做准备时,你可以将每周的商品保留在购物清单上。输入:

delete from groc where comment not like '%W';

注意,在 PostgreSQL 中 % 表示通配符(而非星号)。所以,要保存输入内容,需要输入:

delete from groc where item like 'goat%';

不能使用 item = 'goat%',这样没用。

在购物时,用以下命令输出清单并打印或发送到你的手机:

\o groclist.txt
select * from groc order by comment;
\o

最后一个命令 \o 后面没有任何内容,将重置输出到命令行。否则,所有的输出会继续输出到你创建的杂货店购物文件 groclist.txt 中。

分析复杂的表

这个逐项列表对于数据量小的表来说没有问题,但是对于数据量大的表呢?几年前,我帮 FreieFarbe.de 的团队从 HLC 调色板中创建一个自由色的色样册。事实上,任何能想象到的打印色都可按色调、亮度、浓度(饱和度)来规定。最终结果是 HLC Color Atlas,下面是我们如何实现的。

该团队向我发送了具有颜色规范的文件,因此我可以编写可与 Scribus 配合使用的 Python 脚本,以轻松生成色样册。一个例子像这样开始:

HLC, C, M, Y, K
H010_L15_C010, 0.5, 49.1, 0.1, 84.5
H010_L15_C020, 0.0, 79.7, 15.1, 78.9
H010_L25_C010, 6.1, 38.3, 0.0, 72.5
H010_L25_C020, 0.0, 61.8, 10.6, 67.9
H010_L25_C030, 0.0, 79.5, 18.5, 62.7
H010_L25_C040, 0.4, 94.2, 17.3, 56.5
H010_L25_C050, 0.0, 100.0, 15.1, 50.6
H010_L35_C010, 6.1, 32.1, 0.0, 61.8
H010_L35_C020, 0.0, 51.7, 8.4, 57.5
H010_L35_C030, 0.0, 68.5, 17.1, 52.5
H010_L35_C040, 0.0, 81.2, 22.0, 46.2
H010_L35_C050, 0.0, 91.9, 20.4, 39.3
H010_L35_C060, 0.1, 100.0, 17.3, 31.5
H010_L45_C010, 4.3, 27.4, 0.1, 51.3

这与原始数据相比,稍有修改,原始数据用制表符分隔。我将其转换成 CSV 格式(用逗号分割值),我更喜欢其与 Python 一起使用(CSV 文也很有用,因为它可轻松导入到电子表格程序中)。

在每一行中,第一项是颜色名称,其后是其 C、M、Y 和 K 颜色值。 该文件包含 1,793 种颜色,我想要一种分析信息的方法,以了解这些值的范围。这就是 PostgreSQL 发挥作用的地方。我不想手动输入所有数据 —— 我认为输入过程中我不可能不出错,而且令人头痛。幸运的是,PostgreSQL 为此提供了一个命令。

首先用以下命令创建数据库:

Create table hlc_cmyk (color varchar(40), c decimal, m decimal, y decimal, k decimal);

然后通过以下命令引入数据:

\copy  hlc_cmyk from '/home/gregp/HLC_Atlas_CMYK_SampleData.csv' with (header, format CSV);

开头有反斜杠,是因为使用纯 copy 命令的权限仅限于 root 用户和 Postgres 的超级用户。在括号中,header 表示第一行包含标题,应忽略,CSV 表示文件格式为 CSV。请注意,在此方法中,颜色名称不需要用括号括起来。

如果操作成功,会看到 COPY NNNN,其中 N 表示插入到表中的行数。

最后,可以用下列命令查询:

select * from hlc_cmyk;

     color     |   c   |   m   |   y   |  k  
---------------+-------+-------+-------+------
 H010_L15_C010 |   0.5 |  49.1 |   0.1 | 84.5
 H010_L15_C020 |   0.0 |  79.7 |  15.1 | 78.9
 H010_L25_C010 |   6.1 |  38.3 |   0.0 | 72.5
 H010_L25_C020 |   0.0 |  61.8 |  10.6 | 67.9
 H010_L25_C030 |   0.0 |  79.5 |  18.5 | 62.7
 H010_L25_C040 |   0.4 |  94.2 |  17.3 | 56.5
 H010_L25_C050 |   0.0 | 100.0 |  15.1 | 50.6
 H010_L35_C010 |   6.1 |  32.1 |   0.0 | 61.8
 H010_L35_C020 |   0.0 |  51.7 |   8.4 | 57.5
 H010_L35_C030 |   0.0 |  68.5 |  17.1 | 52.5

所有的 1,793 行数据都是这样的。回想起来,我不能说此查询对于 HLC 和 Scribus 任务是绝对必要的,但是它减轻了我对该项目的一些担忧。

为了生成 HLC 色谱,我使用 Scribus 为色板页面中的 13,000 多种颜色自动创建了颜色图表。

我可以使用 copy 命令输出数据:

\copy hlc_cmyk to '/home/gregp/hlc_cmyk_backup.csv' with (header, format CSV);

我还可以使用 where 子句根据某些值来限制输出。

例如,以下命令将仅发送以 H10 开头的色调值。

\copy hlc_cmyk to '/home/gregp/hlc_cmyk_backup.csv' with (header, format CSV) where color like 'H10%';

备份或传输数据库或表

我在此要提到的最后一个命令是 pg_dump,它用于备份 PostgreSQL 数据库,并在 psql 控制台之外运行。 例如:

pg_dump gregp -t hlc_cmyk > hlc.out
pg_dump gregp > dball.out

第一行是导出 hlc_cmyk 表及其结构。第二行将转储 gregp 数据库中的所有表。这对于备份或传输数据库或表非常有用。

要将数据库或表传输到另一台电脑(查看 PostgreSQL 入门那篇文章获取详细信息),首先在要转入的电脑上创建一个数据库,然后执行相反的操作。

psql -d gregp -f dball.out

一步创建所有表并输入数据。

总结

在本文中,我们了解了如何使用 WHERE 参数限制操作,以及如何使用 PostgreSQL 通配符 。我们还了解了如何将大批量数据加载到表中,然后将部分或全部表数据输出到文件,甚至是将整个数据库及其所有单个表输出。


via: https://opensource.com/article/20/2/postgresql-commands

作者:Greg Pittman 选题:lujun9972 译者:Morisun029 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

安装、设置、创建和开始使用 PostgreSQL 数据库。

每个人或许都有需要在数据库中保存的东西。即使你执着于使用纸质文件或电子文件,它们也会变得很麻烦。纸质文档可能会丢失或混乱,你需要访问的电子信息可能会隐藏在段落和页面的深处。

在我从事医学工作的时候,我使用 PostgreSQL 来跟踪我的住院患者名单并提交有关住院患者的信息。我将我的每日患者名单打印在口袋里,以便快速了解患者房间、诊断或其他细节的任何变化并做出快速记录。

我以为一切没问题,直到去年我妻子决定买一辆新车,我“接手”了她以前的那辆车。她保留了汽车维修和保养服务收据的文件夹,但随着时间的流逝,它变得杂乱。与其花时间筛选所有纸条以弄清楚什么时候做了什么,我认为 PostgreSQL 将是更好的跟踪此信息的方法。

安装 PostgreSQL

自上次使用 PostgreSQL 以来已经有一段时间了,我已经忘记了如何使用它。实际上,我甚至没有在计算机上安装它。安装它是第一步。我使用 Fedora,因此在控制台中运行:

dnf list postgresql*

请注意,你无需使用 sudo 即可使用 list 选项。该命令返回了很长的软件包列表。看了眼后,我决定只需要三个:postgresql、postgresql-server 和 postgresql-docs。

为了了解下一步需要做什么,我决定查看 PostgreSQL 文档。文档参考内容非常丰富,实际上,丰富到令人生畏。幸运的是,我发现我在升级 Fedora 时曾经做过的一些笔记,希望有效地导出数据库,在新版本上重新启动 PostgreSQL,以及导入旧数据库。

设置 PostgreSQL

与大多数其他软件不同,你不能只是安装好 PostgreSQL 就开始使用它。你必须预先执行两个基本步骤:首先,你需要设置 PostgreSQL,第二,你需要启动它。你必须以 root 用户身份执行这些操作(sudo 在这里不起作用)。

要设置它,请输入:

postgresql-setup –initdb

这将确定 PostgreSQL 数据库在计算机上的位置。然后(仍为 root)输入以下两个命令:

systemctl start postgresql.service
systemctl enable postgresql.service

第一个命令为当前会话启动 PostgreSQL(如果你关闭机器,那么 PostgreSQL 也将关闭)。第二个命令使 PostgreSQL 在随后的重启中自动启动。

创建用户

PostgreSQL 正在运行,但是你仍然不能使用它,因为你还没有用户。为此,你需要切换到特殊用户 postgres。当你仍以 root 身份运行时,输入:

su postgres

由于你是以 root 的身份执行此操作的,因此无需输入密码。root 用户可以在不知道密码的情况下以任何用户身份操作;这就是使其强大而危险的原因之一。

现在你就是 postgres 了,请运行下面两个命令,如下所示创建用户(创建用户 gregp):

createuser gregp
createdb gregp

你可能会看到错误消息,如:Could not switch to /home/gregp。这只是意味着用户 postgres不能访问该目录。尽管如此,你的用户和数据库已创建。接下来,输入 exit 并按回车两次,这样就回到了原来的用户下(root)。

设置数据库

要开始使用 PostgreSQL,请在命令行输入 psql。你应该在每行左侧看到类似 gregp=> 的内容,以显示你使用的是 PostgreSQL,并且只能使用它理解的命令。你自动获得一个数据库(我的名为 gregp),它里面完全没有内容。对 PostgreSQL 来说,数据库只是一个工作空间。在空间内,你可以创建。表包含变量列表,而表中的每个变量是构成数据库的数据。

以下是我设置汽车服务数据库的方式:

CREATE TABLE autorepairs (
        date            date,
        repairs         varchar(80),
        location        varchar(80),
        cost            numeric(6,2)
);

我本可以在一行内输入,但为了更好地说明结构,并表明 PostgreSQL 不会解释制表符和换行的空白,我分成了多行。字段包含在括号中,每个变量名和数据类型与下一个变量用逗号分隔(最后一个除外),命令以分号结尾。所有命令都必须以分号结尾!

第一个变量名是 date,它的数据类型也是 date,这在 PostgreSQL 中没关系。第二个和第三个变量 repairslocation 都是 varchar(80) 类型,这意味着它们可以是最多 80 个任意字符(字母、数字等)。最后一个变量 cost 使用 numeric 类型。括号中的数字表示最多有六位数字,其中两位是小数。最初,我尝试了 real 类型,这将是一个浮点数。real 类型的问题是作为数据类型在使用时,在遇到 WHERE 子句,类似 WHERE cost = 0 或其他任何特定数字。由于 real 值有些不精确,因此特定数字将永远不会匹配。

输入数据

接下来,你可以使用 INSERT INTO 命令添加一些数据(在 PostgreSQL 中称为):

INSERT INTO autorepairs VALUES ('2017-08-11', 'airbag recall', 'dealer', 0);

请注意,括号构成了一个值的容器,它必须以正确的顺序,用逗号分隔,并在命令末尾加上分号。datevarchar(80) 类型的值必须包含在单引号中,但数字值(如 numeric)不用。作为反馈,你应该会看到:

INSERT 0 1

与常规终端会话一样,你会有输入命令的历史记录,因此,在输入后续行时,通常可以按向上箭头键来显示最后一个命令并根据需要编辑数据,从而节省大量时间。

如果出了什么问题怎么办?使用 UPDATE 更改值:

UPDATE autorepairs SET date = '2017-11-08' WHERE repairs = 'airbag recall';

或者,也许你不再需要表中的行。使用 DELETE

DELETE FROM autorepairs WHERE repairs = 'airbag recall';

这将删除整行。

最后一件事:即使我在 PostgreSQL 命令中一直使用大写字母(在大多数文档中也这么做),你也可以用小写字母输入,我也经常如此。

输出数据

如果你想展示数据,使用 SELECT

SELECT * FROM autorepairs ORDER BY date;

没有 ORDER BY 的话,行将不管你输入的内容来显示。例如,以下就是我终端中输出的我的汽车服务数据:

SELECT date, repairs FROM autorepairs ORDER BY date;

    date   |                             repairs                             
-----------+-----------------------------------------------------------------
2008-08-08 | oil change, air filter, spark plugs
2011-09-30 | 35000 service, oil change, rotate tires/balance wheels
2012-03-07 | repl battery
2012-11-14 | 45000 maint, oil/filter
2014-04-09 | 55000 maint, oil/filter, spark plugs, air/dust filters
2014-04-21 | replace 4 tires
2014-04-21 | wheel alignment
2016-06-01 | 65000 mile service, oil change
2017-05-16 | oil change, replce oil filt housing
2017-05-26 | rotate tires
2017-06-05 | air filter, cabin filter,spark plugs
2017-06-05 | brake pads and rotors, flush brakes
2017-08-11 | airbag recall
2018-07-06 | oil/filter change, fuel filter, battery svc
2018-07-06 | transmission fl, p steering fl, rear diff fl
2019-07-22 | oil & filter change, brake fluid flush, front differential flush
2019-08-20 | replace 4 tires
2019-10-09 | replace passenger taillight bulb
2019-10-25 | replace passenger taillight assembly
(19 rows)

要将此发送到文件,将输出更改为:

\o autorepairs.txt

然后再次运行 SELECT 命令。

退出 PostgreSQL

最后,在终端中退出 PostgreSQL,输入:

quit

或者它的缩写版:

\q

虽然这只是 PostgreSQL 的简要介绍,但我希望它展示了将数据库用于这样的简单任务既不困难也不费时。


via: https://opensource.com/article/19/11/getting-started-postgresql

作者:Greg Pittman 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

使用 Scribus 的 Python 脚本编写器功能,开发一个显示 RGB 色谱的 3D 立方体。

 title=

当我决定这个夏天要玩色彩游戏时,我想到通常色彩都是在色轮上描绘的。这些色彩通常都是使用色素而不是光,并且你失去了任何对颜色亮度或光度变化的感觉。

作为色轮的替代,我想在立方体表面使用一系列图形来显示 RGB 频谱。色彩的 RGB 值将在具有 X、Y、Z 轴的三维图形上展示。例如,一个平面将会保持 B(蓝色)为 0,其余的坐标轴将显示当我将 R(红色)和 G (绿色)的值从 0 绘制到 255 时发生的情况。

事实证明,使用 Scribus 及其 Python 脚本编写器 功能实现这一点并不困难。我可以创建 RGB 颜色,使矩形显示颜色,并以 2D 格式排列它们。我决定设置颜色值的间隔为 5,并让矩形按 5 个点(pt)进行绘图。因此,对于每个 2D 图形,我将使用大约 250 种颜色,立方体的一个边有 250 个点(pt),也就是 3.5 英寸。

我使用下面这段 Python 代码完成了绿 - 红图的任务:

x = 300
y = 300
r = 0
g = 0
b = 0

if scribus.newDoc(scribus.PAPER_LETTER, (0,0,0,0),scribus.PORTRAIT, 1,                  scribus.UNIT_POINTS, scribus.NOFACINGPAGES, scribus.FIRSTPAGERIGHT):
    while r < 256:
        while g < 256:
            newcolor = str(r) + '_' + str(g) + '_' + str(b)
            if newcolor == '0_0_0':
                newcolor = 'Black'
            scribus.defineColorRGB(newcolor,r, g, b)
            rect = scribus.createRect(x + g, y, 5, 5)
            scribus.setFillColor(newcolor, rect)
            scribus.setLineColor(newcolor, rect)
            g = g + 5
        g = 0
        r = r + 5
        y = y – 5

这个脚本在 300,300 位置开始绘制图形,这个位置大约是一个美国信件大小的纸张的水平中心,大概是垂直方向从顶部到底的三分之一位置;这是图像的原点,然后它沿着 X 轴(绿色值)水平构建图形,然后返回到 Y 轴,向上移动 5 个点,然后绘制下一条矩形线。

 title=

这看起来很简单;我只需要调整一下数字就可以把立方体的另一面画出来。但这不仅仅是再画两个图,一个是蓝 - 绿色,另一个是红 - 蓝色的问题。我想创建一个展开的立方体,这样我就可以打印、剪开然后折叠它,创建一个 RGB 的 3D 视图。因此,下一部分(向下的页面)的原点(黑色的角落)需要在左上角,其水平方向是绿色,垂直方向是蓝色。

“调整数字”最终或多或少变成了试错,从而得到我想要的东西。在创建了第二个图之后,我需要第三个图,它是红 - 蓝色的,原点位于左上角,红色向左递增,蓝色向下递增。

下面是最终效果图:

 title=

当然,这只是这个立方体的前半部分。我需要做一个类似的形状,除了原点应该是白色(而不是黑色)来表示高值。这是我希望自己更聪明的时候之一,因为我不仅需要做出一个类似的整体形状,还需要以镜像的方式与第一个形状交互(我认为)。有时候,尝试和错误是你唯一的朋友。

结果是这样的;我使用了一个单独的脚本,因为在一个美国信件大小的页面上没有足够的空间同时容纳这两个图案。

 title=

现在,是时候轮到打印机了!在这里,你可以直观了解彩色打印机如何处理 RGB 颜色到 CMYK 颜色的转换以及打印颜色密集空间。

接下来,朋友们,是剪切粘贴时间!我可以用胶带,但我不想改变表面的外观,所以我在切割的时候在两边留下了一些空间,这样我就可以把它们粘在里面了。根据我的经验,在复印纸上打印会产生一些不需要的皱纹,所以在我的复印纸原型完成后,我把立方体打印在了更厚的纸上,表面是哑光的。

 title=

请记住,这只是 RGB 空间边界的一个视图;更准确地说,你必须做出一个可以在中间切片的实心立方体。例如,这是一个实心 RGB 立方体在蓝色 = 120 的切片。

 title=

最后,我做这个项目很开心。如果您也想参与其中,这里有两个脚本。

这是前半部分:

#!/usr/bin/env python
# black2rgb.py
"""
Creates one-half of RGB cube with Black at origin
"""

import scribus

x = 300
y = 300
r = 0
g = 0
b = 0

if scribus.newDoc(scribus.PAPER_LETTER, (0,0,0,0),scribus.PORTRAIT, 1, scribus.UNIT_POINTS, scribus.NOFACINGPAGES, scribus.FIRSTPAGERIGHT):
    while r < 256:
        while g < 256:
            newcolor = str(r) + '_' + str(g) + '_' + str(b)
            if newcolor == '0_0_0':
                newcolor = 'Black'
            scribus.defineColorRGB(newcolor,r, g, b)
            rect = scribus.createRect(x + g, y, 5, 5)
            scribus.setFillColor(newcolor, rect)
            scribus.setLineColor(newcolor, rect)
            g = g + 5
        g = 0
        r = r + 5
        y = y - 5
       
    r = 0
    g = 0
    y = 305

    while b < 256:
        while g < 256:
            newcolor = str(r) + '_' + str(g) + '_' + str(b)
            if newcolor == '0_0_0':
                newcolor = 'Black'
            scribus.defineColorRGB(newcolor,r, g, b)
            rect = scribus.createRect(x + g, y, 5, 5)
            scribus.setFillColor(newcolor, rect)
            scribus.setLineColor(newcolor, rect)
            g = g + 5
        g = 0
        b = b + 5
        y = y + 5
       
    r = 255
    g = 0
    y = 305
    x = 39
    b = 0

    while b < 256:
        while r >= 0:
            newcolor = str(r) + '_' + str(g) + '_' + str(b)
            if newcolor == '0_0_0':
                newcolor = 'Black'
            scribus.defineColorRGB(newcolor,r, g, b)
            rect = scribus.createRect(x, y, 5, 5)
            scribus.setFillColor(newcolor, rect)
            scribus.setLineColor(newcolor, rect)
            r = r - 5
            x = x+5
        b = b + 5
        x = 39.5
        r = 255
        y = y + 5
       
scribus.setRedraw(True)
scribus.redrawAll()

后半部分:

#!/usr/bin/env python
# white2rgb.py
"""
Creates one-half of RGB cube with White at origin
"""

import scribus

x = 300
y = 300
r = 255
g = 255
b = 255

if scribus.newDoc(scribus.PAPER_LETTER, (0,0,0,0),scribus.PORTRAIT, 1, scribus.UNIT_POINTS, scribus.NOFACINGPAGES, scribus.FIRSTPAGERIGHT):
    while g >= 0:
        while r >= 0:
            newcolor = str(r) + '_' + str(g) + '_' + str(b)
            if newcolor == '255_255_255':
                newcolor = 'White'
            scribus.defineColorRGB(newcolor,r, g, b)
            rect = scribus.createRect(x + 255 - r, y, 5, 5)
            scribus.setFillColor(newcolor, rect)
            scribus.setLineColor(newcolor, rect)
            r = r - 5
        r = 255
        g = g - 5
        y = y - 5
       
    r = 255
    g = 255
    y = 305

    while b >= 0:
        while r >= 0:
            newcolor = str(r) + '_' + str(g) + '_' + str(b)
            if newcolor == '255_255_255':
                newcolor = 'White'
            scribus.defineColorRGB(newcolor,r, g, b)
            rect = scribus.createRect(x + 255 - r, y, 5, 5)
            scribus.setFillColor(newcolor, rect)
            scribus.setLineColor(newcolor, rect)
            r = r - 5
        r = 255
        b = b - 5
        y = y + 5
       
    r = 255
    g = 0
    y = 305
    x = 39
    b = 255

    while b >= 0:
        while g < 256:
            newcolor = str(r) + '_' + str(g) + '_' + str(b)
            if newcolor == '255_255_255':
                newcolor = 'White'
            scribus.defineColorRGB(newcolor,r, g, b)
            rect = scribus.createRect(x + g, y, 5, 5)
            scribus.setFillColor(newcolor, rect)
            scribus.setLineColor(newcolor, rect)
            g = g + 5
        g = 0
        b = b - 5
        y = y + 5
       
scribus.setRedraw(True)
scribus.redrawAll()

由于我创建了大量的颜色,所以当看到 Scribus 文件比我用它创建的 PDF 文件大得多的时候,我并不感到惊讶。例如,我的 Scribus SLA 文件是 3.0MB,而从中生成的 PDF 只有 70KB。


via: https://opensource.com/article/19/7/rgb-cube-python-scribus

作者:Greg Pittman 选题:lujun9972 译者:zianglei 校对:wxy

本文由 LCTT 原创编译,Linux 中国 荣誉推出

我来介绍一下我是如何使用 Python 来节省成本的。

我最近在开一辆烧 93 号汽油的车子。根据汽车制造商的说法,它只需要加 91 号汽油就可以了。然而,在美国只能买到 87 号、89 号、93 号汽油。而我家附近的汽油的物价水平是每增加一号,每加仑就要多付 30 美分,因此如果加 93 号汽油,每加仑就要多花 60 美分。为什么不能节省一些钱呢?

一开始很简单,只需要先加满 93 号汽油,然后在油量表显示油箱半满的时候,用 89 号汽油加满,就得到一整箱 91 号汽油了。但接下来就麻烦了,剩下半箱 91 号汽油加上半箱 93 号汽油,只会变成一箱 92 号汽油,再接下来呢?如果继续算下去,只会越来越混乱。这个时候 Python 就派上用场了。

我的方案是,可以根据汽油的实时状态,不断向油箱中加入 93 号汽油或者 89 号汽油,而最终目标是使油箱内汽油的号数不低于 91。我需要做的是只是通过一些算法来判断新旧汽油混合之后的号数。使用多项式方程或许也可以解决这个问题,但如果使用 Python,好像只需要进行循环就可以了。

#!/usr/bin/env python
# octane.py

o = 93.0
newgas = 93.0   # 这个变量记录上一次加入的汽油号数
i = 1
while i < 21:                   # 20 次迭代 (加油次数)
    if newgas == 89.0:          # 如果上一次加的是 89 号汽油,改加 93 号汽油
        newgas = 93.0
        o = newgas/2 + o/2      # 当油箱半满的时候就加油
    else:                       # 如果上一次加的是 93 号汽油,则改加 89 号汽油
        newgas = 89.0
        o = newgas/2 + o/2      # 当油箱半满的时候就加油
    print str(i) + ': '+ str(o)
    i += 1

在代码中,我首先将变量 o(油箱中的当前混合汽油号数)和变量 newgas(上一次加入的汽油号数)的初始值都设为 93,然后循环 20 次,也就是分别加入 89 号汽油和 93 号汽油一共 20 次,以保持混合汽油号数稳定。

1: 91.0
2: 92.0
3: 90.5
4: 91.75
5: 90.375
6: 91.6875
7: 90.34375
8: 91.671875
9: 90.3359375
10: 91.66796875
11: 90.333984375
12: 91.6669921875
13: 90.3334960938
14: 91.6667480469
15: 90.3333740234
16: 91.6666870117
17: 90.3333435059
18: 91.6666717529
19: 90.3333358765
20: 91.6666679382

从以上数据来看,只需要 10 到 15 次循环,汽油号数就比较稳定了,也相当接近 91 号汽油的目标。这种交替混合直到稳定的现象看起来很有趣,每次交替加入同等量的不同号数汽油,都会趋于稳定。实际上,即使加入的 89 号汽油和 93 号汽油的量不同,也会趋于稳定。

因此,我尝试了不同的比例,我认为加入的 93 号汽油需要比 89 号汽油更多一点。在尽量少补充新汽油的情况下,我最终计算到的结果是 89 号汽油要在油箱大约 7/12 满的时候加进去,而 93 号汽油则要在油箱 ¼ 满的时候才加进去。

我的循环将会更改成这样:

    if newgas == 89.0:            
                                 
        newgas = 93.0
        o = 3*newgas/4 + o/4      
    else:                        
        newgas = 89.0
        o = 5*newgas/12 + 7*o/12

以下是从第十次加油开始的混合汽油号数:

10: 92.5122272978
11: 91.0487992571
12: 92.5121998143
13: 91.048783225
14: 92.5121958062
15: 91.048780887

如你所见,这个调整会令混合汽油号数始终略高于 91。当然,我的油量表并没有 1/12 的刻度,但是 7/12 略小于 5/8,我可以近似地计算。

一个更简单地方案是每次都首先加满 93 号汽油,然后在油箱半满时加入 89 号汽油直到耗尽,这可能会是我的常规方案。就我个人而言,这种方法并不太好,有时甚至会产生一些麻烦。但对于长途旅行来说,这种方案会相对简便一些。有时我也会因为油价突然下跌而购买一些汽油,所以,这个方案是我可以考虑的一系列选项之一。

当然最重要的是:开车不写码,写码不开车!


via: https://opensource.com/article/18/10/python-gas-pump

作者:Greg Pittman 选题:lujun9972 译者:HankChow 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

用这些 ImageMagick 命令行图像编辑应用的技巧更好的管理你的数码照片集。

在我先前的ImageMagick 入门:使用命令行来编辑图片 文章中,我展示了如何使用 ImageMagick 的菜单栏进行图片的编辑和变换风格。在这篇续文里,我将向你展示使用这个开源的图像编辑器来查看图片的另外方法。

别样的风格

在深入 ImageMagick 的高级图片查看技巧之前,我想先分享另一个使用 convert 达到的有趣但简单的效果,在上一篇文章中我已经详细地介绍了 convert 命令,这个技巧涉及这个命令的 edgenegate 选项:

convert DSC_0027.JPG -edge 3 -negate edge3+negate.jpg

 title=

使用edgenegate 选项前后的图片对比

这些使我更喜爱编辑后的图片:海的外观,作为前景和背景的植被,特别是太阳及其在海上的反射,最后是天空。

使用 display 来查看一系列图片

假如你跟我一样是个命令行用户,你就知道 shell 为复杂任务提供了更多的灵活性和快捷方法。下面我将展示一个例子来佐证这个观点。ImageMagick 的 display 命令可以克服我在 GNOME 桌面上使用 Shotwell 图像管理器导入图片时遇到的问题。

Shotwell 会根据每张导入图片的 Exif 数据,创建以图片被生成或者拍摄时的日期为名称的目录结构。最终的效果是最上层的目录以年命名,接着的子目录是以月命名 (01、 02、 03 等等),然后是以每月的日期命名的子目录。我喜欢这种结构,因为当我想根据图片被创建或者拍摄时的日期来查找它们时将会非常方便。

但这种结构也并不是非常完美的,当我想查看最近几个月或者最近一年的所有图片时就会很麻烦。使用常规的图片查看器,我将不停地在不同层级的目录间跳转,但 ImageMagick 的 display 命令可以使得查看更加简单。例如,假如我想查看最近一年的图片,我便可以在命令行中键入下面的 display 命令:

display -resize 35% 2017/*/*/*.JPG

我可以一个月又一个月,一天又一天地遍历这一年。

现在假如我想查看某张图片,但我不确定我是在 2016 年的上半年还是在 2017 的上半年拍摄的,那么我便可以使用下面的命令来找到它:

display -resize 35% 201[6-7]/0[1-6]/*/*.JPG

这限制查看的图片拍摄于 2016 和 2017 年的一月到六月

使用 montage 来查看图片的缩略图

假如现在我要查找一张我想要编辑的图片,使用 display 的一个问题是它只会显示每张图片的文件名,而不显示其在目录结构中的位置,所以想要找到那张图片并不容易。另外,假如我很偶然地在从相机下载图片的过程中将这些图片从相机的内存里面清除了它们,结果使得下次拍摄照片的名称又从 DSC_0001.jpg 开始命名,那么当使用 display 来展示一整年的图片时,将会在这 12 个月的图片中花费很长的时间来查找它们。

这时 montage 命令便可以派上用场了。它可以将一系列的图片缩略图放在一张图片中,这样就会非常有用。例如可以使用下面的命令来完成上面的任务:

montage -label %d/%f -title 2017 -tile 5x -resize 10% -geometry +4+4 2017/0[1-4]/*/*.JPG 2017JanApr.jpg

从左到右,这个命令以标签开头,标签的形式是包含文件名(%f)和以 / 分割的目录(%d)结构,接着这个命令以目录的名称(2017)来作为标题,然后将图片排成 5 列,每个图片缩放为 10% (这个参数可以很好地匹配我的屏幕)。geometry 的设定将在每张图片的四周留白,最后指定那些图片要包括到这张合成图片中,以及一个合适的文件名称(2017JanApr.jpg)。现在图片 2017JanApr.jpg 便可以成为一个索引,使得我可以不时地使用它来查看这个时期的所有图片。

注意内存消耗

你可能会好奇为什么我在上面的合成图中只特别指定了为期 4 个月(从一月到四月)的图片。因为 montage 将会消耗大量内存,所以你需要多加注意。我的相机产生的图片每张大约有 2.5MB,我发现我的系统可以很轻松地处理 60 张图片。但一旦图片增加到 80 张,如果此时还有另外的程序(例如 Firefox 、Thunderbird)在后台工作,那么我的电脑将会死机,这似乎和内存使用相关,montage可能会占用可用 RAM 的 80% 乃至更多(你可以在此期间运行 top 命令来查看内存占用)。假如我关掉其他的程序,我便可以在我的系统死机前处理 80 张图片。

下面的命令可以让你知晓在你运行 montage 命令前你需要处理图片张数:

ls 2017/0[1-4/*/*.JPG > filelist; wc -l filelist

ls 命令生成我们搜索的文件的列表,然后通过重定向将这个列表保存在任意以名为 filelist 的文件中。接着带有 -l 选项的 wc 命令输出该列表文件共有多少行,换句话说,展示出了需要处理的文件个数。下面是我运行命令后的输出:

163 filelist

啊呀!从一月到四月我居然有 163 张图片,使用这些图片来创建一张合成图一定会使得我的系统死机的。我需要将这个列表减少点,可能只处理到 3 月份或者更早的图片。但如果我在 4 月 20 号到 30 号期间拍摄了很多照片,我想这便是问题的所在。下面的命令便可以帮助指出这个问题:

ls 2017/0[1-3]/*/*.JPG > filelist; ls 2017/04/0[1-9]/*.JPG >> filelist; ls 2017/04/1[0-9]/*.JPG >> filelist; wc -l filelist

上面一行中共有 4 个命令,它们以分号分隔。第一个命令特别指定从一月到三月期间拍摄的照片;第二个命令使用 >> 将拍摄于 4 月 1 日至 9 日的照片追加到这个列表文件中;第三个命令将拍摄于 4 月 10 日到 19 日的照片追加到列表中。最终它的显示结果为:

81 filelist

我知道假如我关掉其他的程序,处理 81 张图片是可行的。

使用 montage 来处理它们是很简单的,因为我们只需要将上面所做的处理添加到 montage 命令的后面即可:

montage -label %d/%f -title 2017 -tile 5x -resize 10% -geometry +4+4 2017/0[1-3]/*/*.JPG 2017/04/0[1-9]/*.JPG 2017/04/1[0-9]/*.JPG 2017Jan01Apr19.jpg

从左到右,montage 命令后面最后的那个文件名将会作为输出,在它之前的都是输入。这个命令将花费大约 3 分钟来运行,并生成一张大小约为 2.5MB 的图片,但我的系统只是有一点反应迟钝而已。

展示合成图片

当你第一次使用 display 查看一张巨大的合成图片时,你将看到合成图的宽度很合适,但图片的高度被压缩了,以便和屏幕相适应。不要慌,只需要左击图片,然后选择 View > Original Size 便会显示整个图片。再次点击图片便可以使菜单栏隐藏。

我希望这篇文章可以在你使用新方法查看图片时帮助你。在我的下一篇文章中,我将讨论更加复杂的图片操作技巧。

作者简介

Greg Pittman - Greg 肯塔基州路易斯维尔的一名退休的神经科医生,对计算机和程序设计有着长期的兴趣,最早可以追溯到 1960 年代的 Fortran IV 。当 Linux 和开源软件相继出现时,他开始学习更多的相关知识,并分享自己的心得。他是 Scribus 团队的成员。


via: https://opensource.com/article/17/9/imagemagick-viewing-images

作者:Greg Pittman 译者:FSSlc 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

用一些简单的脚本,可以很容易地清理文档和其它大量的 HTML 文件。但是首先你需要解析它们。

作为 Scribus 文档团队的长期成员,我要随时了解最新的源代码更新,以便对文档进行更新和补充。 我最近在刚升级到 Fedora 27 系统的计算机上使用 Subversion 进行检出操作时,对于下载该文档所需要的时间我感到很惊讶,文档由 HTML 页面和相关图像组成。 我恐怕该项目的文档看起来比项目本身大得多,并且怀疑其中的一些内容是“僵尸”文档——不再使用的 HTML 文件以及 HTML 中无法访问到的图像。

我决定为自己创建一个项目来解决这个问题。 一种方法是搜索未使用的现有图像文件。 如果我可以扫描所有 HTML 文件中的图像引用,然后将该列表与实际图像文件进行比较,那么我可能会看到不匹配的文件。

这是一个典型的图像标签:

<img src="images/edit_shapes.png" ALT="Edit examples" ALIGN=left>

我对 src= 之后的第一组引号之间的部分很感兴趣。 在寻找了一些解决方案后,我找到一个名为 BeautifulSoup 的 Python 模块。 脚本的核心部分如下所示:

soup = BeautifulSoup(all_text, 'html.parser')
match = soup.findAll("img")
if len(match) > 0:
    for m in match:
        imagelist.append(str(m))

我们可以使用这个 findAll 方法来挖出图片标签。 这是一小部分输出:

<img src="images/pdf-form-ht3.png"/><img src="images/pdf-form-ht4.png"/><img src="images/pdf-form-ht5.png"/><img src="images/pdf-form-ht6.png"/><img align="middle" alt="GSview - Advanced Options Panel" src="images/gsadv1.png" title="GSview - Advanced Options Panel"/><img align="middle" alt="Scribus External Tools Preferences" src="images/gsadv2.png" title="Scribus External Tools Preferences"/>

到现在为止还挺好。我原以为下一步就可以搞定了,但是当我在脚本中尝试了一些字符串方法时,它返回了有关标记的错误而不是字符串的错误。 我将输出保存到一个文件中,并在 KWrite 中进行编辑。 KWrite 的一个好处是你可以使用正则表达式(regex)来做“查找和替换”操作,所以我可以用 \n<img 替换 <img,这样可以看得更清楚。 KWrite 的另一个好处是,如果你用正则表达式做了一个不明智的选择,你还可以撤消。

但我认为,肯定有比这更好的东西,所以我转而使用正则表达式,或者更具体地说 Python 的 re 模块。 这个新脚本的相关部分如下所示:

match = re.findall(r'src="(.*)/>', all_text)
if len(match)>0:
    for m in match:
        imagelist.append(m)

它的一小部分输出如下所示:

images/cmcanvas.png" title="Context Menu for the document canvas" alt="Context Menu for the document canvas" /></td></tr></table><br images/eps-imp1.png" title="EPS preview in a file dialog" alt="EPS preview in a file dialog" images/eps-imp5.png" title="Colors imported from an EPS file" alt="Colors imported from an EPS file" images/eps-imp4.png" title="EPS font substitution" alt="EPS font substitution" images/eps-imp2.png" title="EPS import progress" alt="EPS import progress" images/eps-imp3.png" title="Bitmap conversion failure" alt="Bitmap conversion failure"

乍一看,它看起来与上面的输出类似,并且附带有去除图像的标签部分的好处,但是有令人费解的是还夹杂着表格标签和其他内容。 我认为这涉及到这个正则表达式 src="(.*)/>,这被称为贪婪,意味着它不一定停止在遇到 /> 的第一个实例。我应该补充一点,我也尝试过 src="(.*)",这真的没有什么更好的效果,我不是一个正则表达式专家(只是做了这个),找了各种方法来改进这一点但是并没什么用。

做了一系列的事情之后,甚至尝试了 Perl 的 HTML::Parser 模块,最终我试图将这与我为 Scribus 编写的一些脚本进行比较,这些脚本逐个字符的分析文本内容,然后采取一些行动。 为了最终目的,我终于想出了所有这些方法,并且完全不需要正则表达式或 HTML 解析器。 让我们回到展示的那个 img 标签的例子。

<img src="images/edit_shapes.png" ALT="Edit examples" ALIGN=left>

我决定回到 src= 这一块。 一种方法是等待 s 出现,然后看下一个字符是否是 r,下一个是 c,下一个是否 =。 如果是这样,那就匹配上了! 那么两个双引号之间的内容就是我所需要的。 这种方法的问题在于需要连续识别上面这样的结构。 一种查看代表一行 HTML 文本的字符串的方法是:

for c in all_text:

但是这个逻辑太乱了,以至于不能持续匹配到前面的 c,还有之前的字符,更之前的字符,更更之前的字符。

最后,我决定专注于 = 并使用索引方法,以便我可以轻松地引用字符串中的任何先前或将来的字符。 这里是搜索部分:

    index = 3
    while index < linelength:
        if (all_text[index] == '='):
            if (all_text[index-3] == 's') and (all_text[index-2] == 'r') and (all_text[index-1] == 'c'):
                imagefound(all_text, imagelist, index)
                index += 1
            else:
                index += 1
        else:
            index += 1

我用第四个字符开始搜索(索引从 0 开始),所以我在下面没有出现索引错误,并且实际上,在每一行的第四个字符之前不会有等号。 第一个测试是看字符串中是否出现了 =,如果没有,我们就会前进。 如果我们确实看到一个等号,那么我们会看前三个字符是否是 src。 如果全都匹配了,就调用函数 imagefound

def imagefound(all_text, imagelist, index):
    end = 0
    index += 2
    newimage = ''
    while end == 0:
        if (all_text[index] != '"'):
            newimage = newimage + all_text[index]
            index += 1
        else:
            newimage = newimage + '\n'
            imagelist.append(newimage)
            end = 1
            return

我们给函数发送当前索引,它代表着 =。 我们知道下一个字符将会是 ",所以我们跳过两个字符,并开始向名为 newimage 的控制字符串添加字符,直到我们发现下一个 ",此时我们完成了一次匹配。 我们将字符串加一个换行符(\n)添加到列表 imagelist 中并返回(return),请记住,在剩余的这个 HTML 字符串中可能会有更多图片标签,所以我们马上回到搜索循环中。

以下是我们的输出现在的样子:

images/text-frame-link.png
images/text-frame-unlink.png
images/gimpoptions1.png
images/gimpoptions3.png
images/gimpoptions2.png
images/fontpref3.png
images/font-subst.png
images/fontpref2.png
images/fontpref1.png
images/dtp-studio.png

啊,干净多了,而这只花费几秒钟的时间。 我本可以将索引前移 7 步来剪切 images/ 部分,但我更愿意把这个部分保存下来,以确保我没有剪切掉图像文件名的第一个字母,这很容易用 KWrite 编辑成功 —— 你甚至不需要正则表达式。 做完这些并保存文件后,下一步就是运行我编写的另一个脚本 sortlist.py

#!/usr/bin/env python
# -*- coding: utf-8  -*-
# sortlist.py

import os

imagelist = []
for line in open('/tmp/imagelist_parse4.txt').xreadlines():
    imagelist.append(line)

imagelist.sort()

outfile = open('/tmp/imagelist_parse4_sorted.txt', 'w')
outfile.writelines(imagelist)
outfile.close()

这会读取文件内容,并存储为列表,对其排序,然后另存为另一个文件。 之后,我可以做到以下几点:

ls /home/gregp/development/Scribus15x/doc/en/images/*.png > '/tmp/actual_images.txt'

然后我需要在该文件上运行 sortlist.py,因为 ls 方法的排序与 Python 不同。 我原本可以在这些文件上运行比较脚本,但我更愿意以可视方式进行操作。 最后,我成功找到了 42 个图像,这些图像没有来自文档的 HTML 引用。

这是我的完整解析脚本:

#!/usr/bin/env python
# -*- coding: utf-8  -*-
# parseimg4.py

import os

def imagefound(all_text, imagelist, index):
    end = 0
    index += 2
    newimage = ''
    while end == 0:
        if (all_text[index] != '"'):
            newimage = newimage + all_text[index]
            index += 1
        else:
            newimage = newimage + '\n'
            imagelist.append(newimage)
            end = 1
            return

htmlnames = []
imagelist = []
tempstring = ''
filenames = os.listdir('/home/gregp/development/Scribus15x/doc/en/')
for name in filenames:
    if name.endswith('.html'):
        htmlnames.append(name)
#print htmlnames
for htmlfile in htmlnames:
    all_text = open('/home/gregp/development/Scribus15x/doc/en/' + htmlfile).read()
    linelength = len(all_text)
    index = 3
    while index < linelength:
        if (all_text[index] == '='):
            if (all_text[index-3] == 's') and (all_text[index-2] == 'r') and
(all_text[index-1] == 'c'):
                imagefound(all_text, imagelist, index)
                index += 1
            else:
                index += 1
        else:
            index += 1

outfile = open('/tmp/imagelist_parse4.txt', 'w')
outfile.writelines(imagelist)
outfile.close()
imageno = len(imagelist)
print str(imageno) + " images were found and saved"

脚本名称为 parseimg4.py,这并不能真实反映我陆续编写的脚本数量(包括微调的和大改的以及丢弃并重新开始写的)。 请注意,我已经对这些目录和文件名进行了硬编码,但是很容易变得通用化,让用户输入这些信息。 同样,因为它们是工作脚本,所以我将输出发送到 /tmp 目录,所以一旦重新启动系统,它们就会消失。

这不是故事的结尾,因为下一个问题是:僵尸 HTML 文件怎么办? 任何未使用的文件都可能会引用图像,不能被前面的方法所找出。 我们有一个 menu.xml 文件作为联机手册的目录,但我还需要考虑 TOC(LCTT 译注:TOC 是 table of contents 的缩写)中列出的某些文件可能引用了不在 TOC 中的文件,是的,我确实找到了一些这样的文件。

最后我可以说,这是一个比图像搜索更简单的任务,而且开发的过程对我有很大的帮助。

关于作者

Greg Pittman 是 Kentucky 州 Louisville 市的一名退休的神经学家,从二十世纪六十年代的 Fortran IV 语言开始长期以来对计算机和编程有着浓厚的兴趣。 当 Linux 和开源软件出现的时候,Greg 深受启发,去学习更多知识,并实现最终贡献的承诺。 他是 Scribus 团队的成员。更多关于我


via: https://opensource.com/article/18/1/parsing-html-python

作者:Greg Pittman 译者:Flowsnow 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出