分类 软件开发 下的文章

我是如何通过链接每个页面回到其原始源代码来保持我的网站开放的。

 title=

我是一个开源的超级粉丝。

我支持这项事业的一个小方法是从一开始就保持我的个人博客网站开放。我这样做的部分原因是让人们看到每个页面背后的变化历史。还因为当我开始使用 Jekyll 时,我没有找到很多开源的 Jekyll 博客可以学习。我希望保持我的网站开放并公开我的尝试和错误,可以为其他人节省很多时间。

Jekyll 的 page.path 变量

我实现这一目标的方法之一是将我发布的每一个条目链接到其原始的 MarkdownJekyll 的变量 中正好有一个需要的工具:page.path。这个变量包含每个页面的原始文件系统路径。官方的描述甚至强调了它的作用是链接回源!

在一篇文章的 Markdown 文件中打印 {{page.path }},可以得到类似这样的结果:

_posts/2021-10-10-example.md

假设该文章的源代码存在于这个路径:

https://example.com/ayushsharma-in/-/blob/master/_posts/2021-10-10-example.md

如果你在任何文章的 page.path 前加上 https://example.com/ayushsharma-in/-/blob/master/,它就会生成一个返回其源码的链接。

在 Jekyll 中,生成这个完整的链接看起来像这样:

<a href="{{ page.path | prepend: site.content.blog_source_prefix }}" target="_blank">View source</a>

就是这么简单。

Jekyll 和开放 Web

现代 Web 是一种错综复杂的多层次技术,但这并不意味着它必须让人摸不清。有了 Jekyll 的变量,你可以确保你的用户可以,了解更多关于你是如何建立你的网站的,如果他们愿意的话。

你可以在我的个人博客上看到真实的例子:滚动到底部的查看源码链接。

本文改编自 ayush sharma 的笔记,并经许可转载。


via: https://opensource.com/article/21/12/reveal-source-code-jinja2-git

作者:Ayush Sharma 选题:lujun9972 译者:geekpi 校对:wxy

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

Python 中的 Beautiful Soup 库可以很方便的从网页中提取 HTML 内容。

 title=

今天我们将讨论如何使用 Beautiful Soup 库从 HTML 页面中提取内容,之后,我们将使用它将其转换为 Python 列表或字典。

什么是 Web 刮取,为什么我需要它?

答案很简单:并非每个网站都有获取内容的 API。你可能想从你最喜欢的烹饪网站上获取食谱,或者从旅游博客上获取照片。如果没有 API,提取 HTML(或者说 刮取 scraping 可能是获取内容的唯一方法。我将向你展示如何使用 Python 来获取。

并非所以网站都喜欢被刮取,有些网站可能会明确禁止。请于网站所有者确认是否同意刮取。

Python 如何刮取网站?

使用 Python 进行刮取,我们将执行三个基本步骤:

  1. 使用 requests 库获取 HTML 内容
  2. 分析 HTML 结构并识别包含我们需要内容的标签
  3. 使用 Beautiful Soup 提取标签并将数据放入 Python 列表中

安装库

首先安装我们需要的库。requests 库从网站获取 HTML 内容,Beautiful Soup 解析 HTML 并将其转换为 Python 对象。在 Python3 中安装它们,运行:

pip3 install requests beautifulsoup4

提取 HTML

在本例中,我将选择刮取网站的 Techhology 部分。如果你跳转到此页面,你会看到带有标题、摘录和发布日期的文章列表。我们的目标是创建一个包含这些信息的文章列表。

网站页面的完整 URL 是:

https://notes.ayushsharma.in/technology

我们可以使用 requests 从这个页面获取 HTML 内容:

#!/usr/bin/python3
import requests

url = 'https://notes.ayushsharma.in/technology'

data = requests.get(url)

print(data.text)

变量 data 将包含页面的 HTML 源代码。

从 HTML 中提取内容

为了从 data 中提取数据,我们需要确定哪些标签具有我们需要的内容。

如果你浏览 HTML,你会发现靠近顶部的这一段:

<div class="col">
  <a href="/2021/08/using-variables-in-jekyll-to-define-custom-content" class="post-card">
    <div class="card">
      <div class="card-body">
        <h5 class="card-title">Using variables in Jekyll to define custom content</h5>
        <small class="card-text text-muted">I recently discovered that Jekyll's config.yml can be used to define custom
          variables for reusing content. I feel like I've been living under a rock all this time. But to err over and
          over again is human.</small>
      </div>
      <div class="card-footer text-end">
        <small class="text-muted">Aug 2021</small>
      </div>
    </div>
  </a>
</div>

这是每篇文章在整个页面中重复的部分。我们可以看到 .card-title 包含文章标题,.card-text 包含摘录,.card-footer > small 包含发布日期。

让我们使用 Beautiful Soup 提取这些内容。

#!/usr/bin/python3
import requests
from bs4 import BeautifulSoup
from pprint import pprint

url = 'https://notes.ayushsharma.in/technology'
data = requests.get(url)

my_data = []

html = BeautifulSoup(data.text, 'html.parser')
articles = html.select('a.post-card')

for article in articles:

    title = article.select('.card-title')[0].get_text()
    excerpt = article.select('.card-text')[0].get_text()
    pub_date = article.select('.card-footer small')[0].get_text()

    my_data.append({"title": title, "excerpt": excerpt, "pub_date": pub_date})

pprint(my_data)

以上代码提取文章信息并将它们放入 my_data 变量中。我使用了 pprint 来美化输出,但你可以在代码中忽略它。将上面的代码保存在一个名为 fetch.py 的文件中,然后运行它:

python3 fetch.py

如果一切顺利,你应该会看到:

[{'excerpt': "I recently discovered that Jekyll's config.yml can be used to"
"define custom variables for reusing content. I feel like I've"
'been living under a rock all this time. But to err over and over'
'again is human.',
'pub_date': 'Aug 2021',
'title': 'Using variables in Jekyll to define custom content'},
{'excerpt': "In this article, I'll highlight some ideas for Jekyll"
'collections, blog category pages, responsive web-design, and'
'netlify.toml to make static website maintenance a breeze.',
'pub_date': 'Jul 2021',
'title': 'The evolution of ayushsharma.in: Jekyll, Bootstrap, Netlify,'
'static websites, and responsive design.'},
{'excerpt': "These are the top 5 lessons I've learned after 5 years of"
'Terraform-ing.',
'pub_date': 'Jul 2021',
'title': '5 key best practices for sane and usable Terraform setups'},

... (truncated)

以上是全部内容!在这 22 行代码中,我们用 Python 构建了一个网络刮取器,你可以在 我的示例仓库中找到源代码

总结

对于 Python 列表中的网站内容,我们现在可以用它做一些很酷的事情。我们可以将它作为 JSON 返回给另一个应用程序,或者使用自定义样式将其转换为 HTML。随意复制粘贴以上代码并在你最喜欢的网站上进行试验。

玩的开心,继续编码吧。

本文最初发表在作者个人博客上,经授权改编。


via: https://opensource.com/article/21/9/web-scraping-python-beautiful-soup

作者:Ayush Sharma 选题:lujun9972 译者:MjSeven 校对:wxy

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

谷歌的 UI 工具包 Flutter 在创建移动、网页和桌面的跨平台应用程序方面越来越受欢迎。

Flutter 不是一种编程语言,而是一个软件开发工具包。Dart 是 Flutter SDK 下面使用的编程语言。

Flutter 是谷歌开源的 Fuchsia OS、谷歌 STADIA 以及许多其他软件和移动应用背后的主要框架。

如果你想使用 Flutter 进行开发,本教程将帮助你在 Ubuntu 和其他类似 Linux 发行版上搭建好你的环境。

在 Ubuntu 和其他 Linux 上用 Snap 安装 Flutter

在 Linux 上安装 Flutter 最简单的方法是使用 Snap。如果你使用的是 Ubuntu,你已经有了 Snap。对于其他发行版,请确保 启用 Snap 支持

打开终端 并在终端中使用以下命令来安装 Flutter:

sudo snap install flutter --classic

你会在你的终端上看到类似这样的东西:

一旦安装完成,就是验证它的时候了。不仅仅是 Flutter 的安装,还要验证 Flutter 正常运行所需满足的每一个依赖关系。

验证 Flutter 的依赖项

为了验证 Flutter 正确工作所需的每一个依赖项,Flutter 有一个内置选项:

Flutter doctor

这个过程开始看起来像这样:

而它完成时像这样:

正如你所看到的,我们需要 Android Studio 来工作。所以让我们来安装它。我们该怎么做呢?用 Snap 在 Linux 上安装 Android Studio 也是毫不费力的。

安装并设置好 Android Studio

在终端中,使用下面的命令来安装 Android Studio:

sudo snap install android-studio --classic

安装完毕后,从我们的操作系统菜单中打开 Android Studio。

就快完成了。现在是配置 Android Studio 的时候了。

点击下一步,如果你不想让事情复杂化,就选择“ 标准 standard ”。

选择你喜欢的主题(我喜欢“ 暗色 Darcula ”的)。

确认一切正常,然后点击“ 下一步 Next ”。

最后,点击“ 完成 Finish ”按钮。

然后等待,直到下载完成。

创建一个 Hello World Flutter 应用样本

在 Android Studio 中,进入项目,选择“ 新建 Flutter 项目 New Flutter Project ”。Flutter SDK 路径会默认设置。

在这里,神奇的事情开始出现了,这是你设置你的项目名称的地方,在这个例子中,它将被称为 “hello\_world”。

让我们选择三个可用的平台。Android、iOS 和 Web。最后,点击“ 完成 Finish ”。

项目中的主文件位于 lib/main.dart,如下图所示:

选定后,擦除文件中包含的所有内容,并将其改为本示例代码:

// Copyright 2018 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Welcome to Flutter',
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Welcome to Flutter'),
        ),
        body: const Center(
          child: Text('Hello World'),
        ),
      ),
    );
  }
}

重要的是,这只是向你展示 Flutter 是如何工作的,如果你确信要学习这种美丽而不可思议的语言,这里有 文档 可以看到更多关于它的信息。尝试它!

最后,选择 “Chome Web” 设备,并点击“ 运行 Run ”按钮,如下图所示;并看到神奇的效果!

你可以如此快速地创建一个 Flutter 项目,真是不可思议。跟你的 Hello World 项目打个招呼吧。

最后...

如果你想在短时间内做出漂亮的移动和网页界面的贡献,Flutter 和 Dart 是完美的。

现在你知道了如何在 Ubuntu Linux 上安装 Flutter,以及如何用它创建你的第一个应用程序。我很高兴可以为你写这篇文章,希望对你有所帮助,如果你有任何问题,请通过留言或给我发邮件来告诉我,祝你好运!

本教程由 Marco Antonio Carmona Galván 提供,他是物理学和数据科学专业的学生。


via: https://itsfoss.com/install-flutter-linux/

作者:Marco Antonio Carmona Galván 选题:lujun9972 译者:wxy 校对:wxy

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

如果你曾经试图通过电子邮件或聊天来协作处理文件,并且发现自己试图描述需要修改的地方,那么你会喜欢 diffpatch 的。

我编辑过很多文本文件。有时是代码。其他时候是角色扮演游戏(RPG)、编程书籍或一般信件的书面文字。有时候,做一个修改,而能让我的协作者把我的修改和他们原来写的东西进行比较就更好了。许多人默认使用办公套件(如 LibreOffice)的注释或更改跟踪功能。不过有时更简单的工具更有意义,为此,你可以看看像 diffpatch 这样的工具的编程历史,它们为跟踪和应用共享文件的变化提供了标准化的格式。

即使对于简单的文件,在同步两个文件时也有复杂性。一些项目被改变,另一些被保留,新的内容被添加,还有一些保持不变,但被移到文件的不同位置。如果接受所有的变化,并且用新文件替换旧文件,就很难复制变化。它也是整体不透明的。如果变化很多,就很难挑出到底发生了什么变化。

通过 diff 命令,你可以创建一个文件变化的记录,通过 patch 你可以在旧版本上“重放”这些变化,使其与新版本保持一致。

设置

假设你和我正在合作编写一个描述如何泡茶的文件。

到目前为止,文件 tea.md 包含原始的复制粘贴来的内容:

烧开水。
加热茶壶。
在茶壶中加入茶和水。
在茶壶上放置一个茶叶滤网。
浸泡 6 分钟。
将茶倒入杯中。
加入牛奶。

这似乎很合理,但总有一些优化可以做,所以你把文件发给我改进。为了澄清泡茶过程,我把文件复制为tea-revision.md,并进行编辑,最后是这样的:

在烤箱的抽屉中加热茶壶。
烧开水。
将茶叶放入茶叶滤网。
将滤网和水加入茶壶。
浸泡 6 分钟。用茶壶罩保温。
将茶倒入杯中。
可以选择加入温牛奶。

正如预期的那样,一些项目(“烧开水”和“将茶倒入杯中”)没有变化,而其他行(“加热茶壶”)则有增加。有些行是全新的,有些行是相同的,但顺序不同。

创建一个差异

diff 工具会显示两个文件之间的差异。有几种不同的方法来查看结果,但我认为最清楚的是 —unified(简写为 -u)视图,它显示哪些行被增加或减少了。以任何方式改变的行都被视为先减后增的行。默认情况下,diff 将其输出打印到终端。

diff 提供旧文件,然后是新文件:

$ diff --unified tea.md tea-revised.md 
--- tea.md      2021-11-13 10:26:25.082110219 +1300
+++ tea-revised.md      2021-11-13 10:26:32.049110664 +1300
@@ -1,7 +1,7 @@
+在烤箱的抽屉中加热茶壶。
 烧开水。
-加热茶壶。
-在茶壶中加入茶和水。
-在茶壶上放置一个茶叶滤网。
-浸泡 6 分钟。
+将茶叶放入茶叶滤网。
+将滤网和水加入茶壶。
+浸泡 6 分钟。用茶壶罩保温。
 将茶倒入杯中。
-加入牛奶。
+可以选择加入温牛奶。

行首的加号(+)表示在旧文件中增加了一些内容。行首的减号(-)表示被删除或改变的行。

用 diff 创建一个补丁

补丁文件就是将 diff —unified 命令的输出放到一个文件中。你可以用标准的 Bash 重定向来做这件事:

$ diff -u tea.md tea-revised.md > tea.patch

该文件的内容与输出到终端的内容完全相同。我喜欢在 Emacs 中查看补丁文件,它对每一行进行颜色编码,取决于它是被添加还是被减去。

 title=

用补丁应用修改

一旦我有了补丁文件,我就可以把它发给你,让你审查,并且可以选择应用到你的旧文件中。你可以用 patch 命令来应用一个补丁。

$ patch tea.md tea.patch

增加了一些行,减少了一些行,最后,你得到了一个与我的版本相同的文件:

$ cat tea.md
在烤箱的抽屉中加热茶壶。
烧开水。
将茶叶放入茶叶滤网。
将滤网和水加入茶壶。
浸泡 6 分钟。用茶壶罩保温。
将茶倒入杯中。
可以选择加入温牛奶。

你可以给一个文件打多少次补丁,这是没有限制的。你可以对我的修改进行迭代,生成一个新的补丁,然后发给我审核。发送修改内容而不是结果,可以让每个贡献者审查修改的内容,决定他们要保留或删除的内容,并准确地记录过程。

安装

在 Linux 和 macOS 上,你已经有了 diffpatch 命令。在 Windows 上,你可以通过 Cygwin 获得 diffpatch,或者使用 Chocolatey 搜索 diffutilspatch

如果你曾经试图通过电子邮件或聊天来协作处理文件,并且发现自己需要 描述 需要修改的地方,那么你会喜欢 diffpatch。一个结构严谨的文件,如代码或以行为单位的 Markdown,很容易进行差异比较、补丁和维护。


via: https://opensource.com/article/21/11/linux-diff-patch

作者:Seth Kenlon 选题:lujun9972 译者:wxy 校对:wxy

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

下次当你为控制台输出的格式而苦恼时,请参考这篇文章及其速查表。

我写文章主要是为了给自己写文档。我在编程时非常健忘,所以我经常会写下有用的代码片段、特殊的特性,以及我使用的编程语言中的常见错误。这篇文章完全切合我最初的想法,因为它涵盖了从 C++ 控制台格式化打印时的常见用例。

像往常一样,这篇文章带有大量的例子。除非另有说明,代码片段中显示的所有类型和类都是 std 命名空间的一部分。所以当你阅读这段代码时,你必须在类型和类的前面加上using namespace std;。当然,该示例代码也可以在 GitHub 上找到。

面向对象的流

如果你曾经用过 C++ 编程,你肯定使用过 cout。当你包含 <iostream> 时,ostream 类型的 cout 对象就进入了作用域。这篇文章的重点是 cout,它可以让你打印到控制台,但这里描述的一般格式化对所有 ostream 类型的流对象都有效。ostream 对象是 basic_ostream 的一个实例,其模板参数为 char 类型。头文件 <iosfwd><iostream> 的包含层次结构的一部分,包含了常见类型的前向声明。

basic_ostream 继承于 basic_ios,该类型又继承于 ios_base。在 cppreference.com 上你可以找到一个显示不同类之间关系的类图。

ios_base 类是所有 I/O 流类的基类。basic_ios 类是一个模板类,它对常见的字符类型进行了 模板特化 specialization ,称为 ios。因此,当你在标准 I/O 的上下文中读到 ios 时,它是 basic_ioschar 类型的模板特化。

格式化流

一般来说,基于 ostream 的流有三种格式化的方法。

  1. 使用 ios_base 提供的格式标志。
  2. 在头文件 <iomanip><ios> 中定义的流修改函数。
  3. 通过调用 << 操作符的 特定重载

所有这些方法都有其优点和缺点,通常取决于使用哪种方法的情况。下面显示的例子混合使用所有这些方法。

右对齐

默认情况下,cout 占用的空间与要打印的数据所需的空间一样大。为了使这种右对齐的输出生效,你必须定义一个行允许占用的最大宽度。我使用格式标志来达到这个目的。

右对齐输出的标志和宽度调整只适用于其后的行。

cout.setf(ios::right, ios::adjustfield);
cout.width(50);
cout << "This text is right justified" << endl;
cout << "This text is left justified again" << endl;

在上面的代码中,我使用 setf 配置了右对齐的输出。我建议你将位掩码 ios::adjustfield 应用于 setf,这将使位掩码指定的所有标志在实际的 ios::right 标志被设置之前被重置,以防止发生组合碰撞。

填充空白

当使用右对齐输出时,默认情况下,空的地方会用空白字符填充。你可以通过使用 setfill 指定填充字符来改变它:

cout << right << setfill('.') << setw(30) << 500 << " pcs" << endl;
cout << right << setfill('.') << setw(30) << 3000 << " pcs" << endl;
cout << right << setfill('.') << setw(30) << 24500 << " pcs" << endl;

代码输出如下:

...........................500 pcs
..........................3000 pcs
.........................24500 pcs

组合

想象一下,你的 C++ 程序记录了你的储藏室库存。不时地,你想打印一份当前库存的清单。要做到这一点,你可以使用以下格式。

下面的代码是左对齐和右对齐输出的组合,使用点作为填充字符,可以得到一个漂亮的列表:

cout << left << setfill('.') << setw(20) << "Flour" << right << setfill('.') << setw(20) << 0.7 << " kg" << endl;
cout << left << setfill('.') << setw(20) << "Honey" << right << setfill('.') << setw(20) << 2 << " Glasses" << endl;
cout << left << setfill('.') << setw(20) << "Noodles" << right << setfill('.') << setw(20) << 800 << " g" << endl;
cout << left << setfill('.') << setw(20) << "Beer" << right << setfill('.') << setw(20) << 20 << " Bottles" << endl;

输出:

Flour...............................0.70 kg
Honey..................................2 Glasses
Noodles..............................800 g
Beer..................................20 Bottles

打印数值

当然,基于流的输出也能让你输出各种变量类型。

布尔型

boolalpha 开关可以让你把布尔型的二进制解释转换为字符串:

cout << "Boolean output without using boolalpha: " << true << " / " << false << endl;
cout << "Boolean output using boolalpha: " << boolalpha << true << " / " << false << endl;

以上几行产生的输出结果如下:

Boolean output without using boolalpha: 1 / 0
Boolean output using boolalpha: true / false

地址

如果一个整数的值应该被看作是一个地址,那么只需要把它投到 void* 就可以了,以便调用正确的重载。下面是一个例子:

unsigned long someAddress = 0x0000ABCD;
cout << "Treat as unsigned long: " << someAddress << endl;
cout << "Treat as address: " << (void*)someAddress << endl;

该代码产生了以下输出:

Treat as unsigned long: 43981
Treat as address: 0000ABCD

该代码打印出了具有正确长度的地址。一个 32 位的可执行文件产生了上述输出。

整数

打印整数是很简单的。为了演示,我使用 setfsetiosflags 来指定数字的基数。应用流修改器 hex/oct 也有同样的效果。

int myInt = 123;

cout << "Decimal: " << myInt << endl;

cout.setf(ios::hex, ios::basefield);
cout << "Hexadecimal: " << myInt << endl;

cout << "Octal: " << resetiosflags(ios::basefield) <<  setiosflags(ios::oct) << myInt << endl;

注意: 默认情况下,没有指示所使用的基数,但你可以使用 showbase 添加一个。

Decimal: 123
Hexadecimal: 7b
Octal: 173

用零填充

0000003
0000035
0000357
0003579

你可以通过指定宽度和填充字符得到类似上述的输出:

cout << setfill('0') << setw(7) << 3 << endl;
cout << setfill('0') << setw(7) << 35 << endl;
cout << setfill('0') << setw(7) << 357 << endl;
cout << setfill('0') << setw(7) << 3579 << endl;

浮点值

如果我想打印浮点数值,我可以选择“固定”和“科学”格式。此外,我还可以指定精度:

double myFloat = 1234.123456789012345;
int defaultPrecision = cout.precision(); // == 2

cout << "Default precision: " << myFloat << endl;
cout.precision(4);
cout << "Modified precision: " << myFloat << endl;
cout.setf(ios::scientific, ios::floatfield);
cout << "Modified precision & scientific format: " << myFloat << endl;
/* back to default */
cout.precision(defaultPrecision);
cout.setf(ios::fixed, ios::floatfield);
cout << "Default precision & fixed format:  " << myFloat << endl;

上面的代码产生以下输出:

Default precision: 1234.12
Modified precision: 1234.1235
Modified precision & scientific format: 1.2341e+03
Default precision & fixed format:  1234.12

时间和金钱

通过 put_money,你可以用正确的、与当地有关的格式来打印货币单位。这需要你的控制台能够输出 UTF-8 字符集。请注意,变量 specialOffering 以美分为单位存储货币价值。

long double specialOffering = 9995;

cout.imbue(locale("en_US.UTF-8"));
cout << showbase << put_money(specialOffering) << endl;
cout.imbue(locale("de_DE.UTF-8"));
cout << showbase << put_money(specialOffering) << endl;
cout.imbue(locale("ru_RU.UTF-8"));
cout  << showbase << put_money(specialOffering) << endl;

iosimbue 方法让你指定一个地区。通过命令 locale -a,你可以得到你系统中所有可用的地区标识符的列表。

$99.95
99,950€
99,950₽

(不知道出于什么原因,在我的系统上,它打印的欧元和卢布有三个小数位,对我来说看起来很奇怪,但这也许是官方的格式。)

同样的原则也适用于时间输出。函数 put_time 可以让你以相应的地区格式打印时间。此外,你可以指定时间对象的哪些部分被打印出来。

time_t now = time(nullptr);
tm localtm = *localtime(&now);


cout.imbue(locale("en_US.UTF-8"));
cout << "en_US : " << put_time(&localtm, "%c") << endl;
cout.imbue(locale("de_DE.UTF-8"));
cout << "de_DE : " << put_time(&localtm, "%c") << endl;
cout.imbue(locale("ru_RU.UTF-8"));
cout << "ru_RU : " << put_time(&localtm, "%c") << endl;

格式指定符 %c 会打印一个标准的日期和时间字符串:

en_US : Tue 02 Nov 2021 07:36:36 AM CET
de_DE : Di 02 Nov 2021 07:36:36 CET
ru_RU : Вт 02 ноя 2021 07:36:36

创建自定义的流修改器

你也可以创建你自己的流。下面的代码在应用于 ostream 对象时插入了一个预定义的字符串:

ostream& myManipulator(ostream& os) {
    string myStr = ">>>Here I am<<<";
    os << myStr;
    return os;
}

另一个例子: 如果你有重要的事情要说,就像互联网上的大多数人一样,你可以使用下面的代码在你的信息后面根据重要程度插入感叹号。重要程度被作为一个参数传递:

struct T_Importance {
     int levelOfSignificance;
};

T_Importance importance(int lvl){
    T_Importance x = {.levelOfSignificance = lvl };
    return x;
}

ostream& operator<<(ostream& __os, T_Importance t){

    for(int i = 0; i < t.levelOfSignificance; ++i){
        __os.put('!');
    }
    return __os;
}

这两个修饰符现在都可以简单地传递给 cout

cout << "My custom manipulator: " << myManipulator << endl;

cout << "I have something important to say" << importance(5) << endl;

产生以下输出:

My custom manipulator: >>>Here I am<<<

I have something important to say!!!!!

结语

下次你再纠结于控制台输出格式时,我希望你记得这篇文章及其 速查表

在 C++ 应用程序中,coutprintf 的新邻居。虽然使用 printf 仍然有效,但我可能总是喜欢使用 cout。特别是与定义在 <ios> 中的修改函数相结合,会产生漂亮的、可读的代码。


via: https://opensource.com/article/21/11/c-stdcout-cheat-sheet

作者:Stephan Avenwedde 选题:lujun9972 译者:wxy 校对:wxy

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

使用 setuptools 来向用户交付 Python 代码。

 title=

你花了几周的时间来完善你的代码。你已经对它进行了测试,并把它发送给一些亲近的开发者朋友以保证质量。你已经将所有的源代码发布在 你的个人 Git 服务器 上,并且从一些勇敢的早期使用者收到了一些有用的错误报告。现在你已经准备好将你的 Python 代码提供给全世界。

就在这时你遇到一个问题。你不知道如何交付产品。

将代码交付给它的目标用户是一件大事。这是软件开发的一个完整的分支,是 CI/CD 中的 “D”,但很多人都忘记了,至少到最后才知道。我写过关于 AutotoolsCmake 的文章,但有些语言有自己的方法来帮助你将你的代码提供给用户。对于 Python 来说,向用户提供代码的一个常见方法是使用 setuptools

安装 setuptools

安装和更新 setuptools 的最简单方法是使用 pip

$ sudo python -m pip install --upgrade setuptools

示例库

我创建了一个简单的 Python 库,名为 myhellolib,来作为需要打包的示例代码。这个库接受一个字符串,然后用大写字母打印出这个字符串。

它只有两行代码,但项目结构很重要,所以首先创建目录树:

$ mkdir -p myhellolib.git/myhellolib

为了确认这个项目是一个可导入的库(即 Python “模块”),在代码目录中创建一个空文件 __init__.py,同时创建一个包含代码的文件:

$ touch myhellolib.git/myhellolib/__init__.py
$ touch myhellolib.git/myhellolib/myhellolib.py

myhellolib.py 文件中,输入简单的 Python 代码:

def greeter(s):
    print(s.upper())

这就是写好的库。

测试它

在打包之前,测试一下你的库。创建一个 myhellolib.git/test.py 文件并输入以下代码:

import myhellolib.myhellolib as hello
hello.greeter("Hello Opensource.com.")

运行该脚本:

$ cd myhellolib.git
$ python ./test.py
HELLO OPENSOURCE.COM

它可以工作,所以现在你可以把它打包了。

Setuptools

要用 setuptools 打包一个项目,你必须创建一个 .toml 文件,将 setuptools 作为构建系统。将这段文字放在项目目录下的 myhellolib.toml 文件中。

[build-system]
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta"

接下来,创建一个名为 setup.py 的文件,包含项目的元数据:

from setuptools import setup

setup(
   name='myhellolib',
   version='0.0.1',
   packages=['myhellolib'],
   install_requires=[
      'requests',
      'importlib; python_version == "3.8"',
   ],
)

不管你信不信,这就是 setuptools 需要的所有设置。你的项目已经可以进行打包。

打包 Python

要创建你的 Python 包,你需要一个构建器。一个常见的工具是 build,你可以用 pip 安装它:

$ python -m pip install build --user

构建你的项目:

$ python -m build

过了一会儿,构建完成了,在你的项目文件夹中出现了一个新的目录,叫做 dist。这个文件夹包含一个 .tar.gz 和一个 .whl 文件。

这是你的第一个 Python 包! 下面是包的内容:

$ tar --list --file dist/myhellolib-0.0.1.tar.gz
myhellolib-0.0.1/
myhellolib-0.0.1/PKG-INFO
myhellolib-0.0.1/myhellolib/
myhellolib-0.0.1/myhellolib/__init__.py
myhellolib-0.0.1/myhellolib/myhellolib.py
myhellolib-0.0.1/myhellolib.egg-info/
myhellolib-0.0.1/myhellolib.egg-info/PKG-INFO
myhellolib-0.0.1/myhellolib.egg-info/SOURCES.txt
myhellolib-0.0.1/myhellolib.egg-info/dependency_links.txt
myhellolib-0.0.1/myhellolib.egg-info/requires.txt
myhellolib-0.0.1/myhellolib.egg-info/top_level.txt
myhellolib-0.0.1/setup.cfg
myhellolib-0.0.1/setup.py

$ unzip -l dist/myhellolib-0.0.1-py3-none-any.whl 
Archive:  dist/myhellolib-0.0.1-py3-none-any.whl
Name
----
myhellolib/__init__.py
myhellolib/myhellolib.py
myhellolib-0.0.1.dist-info/METADATA
myhellolib-0.0.1.dist-info/WHEEL
myhellolib-0.0.1.dist-info/top_level.txt
myhellolib-0.0.1.dist-info/RECORD
-------
6 files

让它可用

现在你知道了打包你的 Python 包是多么容易,你可以使用 Git 钩子、GitLab Web 钩子、Jenkins 或类似的自动化工具来自动完成这个过程。你甚至可以把你的项目上传到 PyPi,这个流行的 Python 模块仓库。一旦它在 PyPi 上,用户就可以用 pip 来安装它,就像你在这篇文章中安装 setuptoolsbuild 一样!

当你坐下来开发一个应用或库时,打包并不是你首先想到的事情,但它是编程的一个重要方面。Python 开发者在程序员如何向世界提供他们的工作方面花了很多心思,没有比 setuptools 更容易的了。试用它,使用它,并继续用 Python 编码!


via: https://opensource.com/article/21/11/packaging-python-setuptools

作者:Seth Kenlon 选题:lujun9972 译者:geekpi 校对:wxy

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