分类 软件开发 下的文章

本教程解释了 Python 中变量的概念、它们的类型,以及如何在实际项目中使用的示例。

在 Python 中,变量是存储值的保留内存位置。

它们是个名称,可以分配一个值给它并在整个代码中引用它。使用变量使值可访问并为值提供与你的代码相关的上下文/含义。

在开始之前,我希望你已经安装了 Python 并设置了一个基本的编辑器。如果没有,请参考我的以下指南:

变量规则

  • 变量区分大小写。
  • 变量名称只能包含大写和小写字母(A–Z、a–z)、数字(0–9)和下划线(\_)。
  • 它们不能以数字开头。
  • Python 变量是 动态类型
  • Python 支持 Unicode 变量(例如 é 和 ü 等装饰字母,甚至中文、日文和阿拉伯符号)。
  • 根据 PEP 8 标准,变量名只能是小写,单词可以用下划线分隔(例如 total\_price)。

Python 变量:示例

要在 Python 中创建一个变量,我们需要使用赋值运算符(=)为其赋值。例如,下面的代码行创建了一个名为 x 的变量并为其赋值 10

x = 10

在 Python 中,变量是动态类型的,这意味着解释器可以根据分配给它的值来确定变量的数据类型。Python 支持各种类型的变量,包括整数、浮点数、字符串、布尔值和复数。

# 整型变量
age = 20

# 浮点变量
price = 4.99

# 字符串变量
name = "John Doe"

# 布尔变量
is_active = True

# 复数变量
z = 2 + 3j

变量在编程中很有用,因为它们允许我们存储和操作数据。例如,我们可以使用变量来执行数学运算、连接字符串以及根据布尔变量的值做出决策。

# 数学运算
x = 5
y = 10
z = x + y
print(z)  # 输出:15

# 字符串连接
first_name = "John"
last_name = "Doe"
full_name = first_name + " " + last_name
print(full_name)  # 输出:John Doe

A simple demonstration of variables in Python

变量也可以在现实世界的项目中用于存储和操作数据。例如,在 Web 应用中,我们可以使用变量来 存储用户输入、数据库查询以及向用户输出数据。

# 用户输入
name = input("What is your name? ")
print("Hello, " + name + "!")  # 输出: Hello, John!

# 数据库查询
import sqlite3

conn = sqlite3.connect("example.db")
cursor = conn.cursor()

cursor.execute("SELECT * FROM users WHERE id = ?", (1,))
user = cursor.fetchone()

print(user)  # 输出: (1, 'John Doe', '[email protected]')

# 输出数据给用户
balance = 100.00
print("Your current balance is ₹" + str(balance))  # 输出: Your current balance is ₹100.0

使用变量时的常见错误

在处理变量时,你可能会遇到一些常见错误。这里是其中的一些。

  • NameError:当你尝试访问尚未定义的变量时会发生此错误。例如,如果你尝试打印一个尚未赋值的变量的值,你将得到一个 NameError。以下代码给出了 NameError,因为变量 Full_name 未定义。
# NameError 演示
first_name = "John"
last_name = "Doe"
full_name = first_name + " " + last_name
print(Full_name)  # NameError

NameError example

  • TypeError:当你尝试对错误数据类型的变量进行操作时会发生此错误。例如,如果你尝试连接一个字符串和一个整数,你将得到 TypeError。下面的代码片段给出了 TypeError
# TypeError 演示
first_name = "John"
age = 10
print(first_name + age)

TypeError example in Python

  • ValueError:当你尝试将变量转换为不同的数据类型但无法进行转换时会发生此错误。例如,如果你尝试将包含字母的字符串转换为整数,你将得到 ValueError
# ValueError 演示
first_name = "John"
age = 10
print(int(first_name))

ValueError in Python

总结

了解变量对于任何 Python 开发人员来说都是必不可少的。变量允许我们存储和操作数据、执行数学运算、连接字符串等。如果你是 Python 编程的初学者,我希望本指南能够阐明这个概念。


via: https://www.debugpoint.com/python-variables/

作者:Arindam 选题:lkxed 译者:geekpi 校对:wxy

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

嗨!这周我一直在写一些 Javascript,和往常一样,当我开始一个新的前端项目时,我面临的问题是:我是否应该使用构建系统?

我想谈谈构建系统对我有什么吸引力,为什么我(通常)仍然不使用它们,以及一些前端 Javascript 库要求你使用构建系统时,为什么我觉得这让我感到沮丧。

我写这篇文章是因为我看到的大多数关于 JS 的文章都假定你正在使用构建系统,而对于像我这样的人来说,编写非常简单的、不需要构建系统的小型 Javascript 项目时,构建系统可能反而添加了很多麻烦。

什么是构建系统?

构建系统的思路是,你有一堆 Javascript 或 Typescript 代码,你想在把它放到你的网站上之前把它翻译成不同的 Javascript 代码。

构建系统可以做很多有用的事情,比如:

  • (出于效率的考虑)将 100 多个 JS 文件合并成一个大的捆绑文件
  • 将 Typescript 翻译成 Javascript
  • 对 Typescript 进行类型检查
  • 精简化
  • 添加 Polyfills 以支持旧的浏览器
  • 编译 JSX
  • 摇树优化 Tree Shaking (删除不使用的 JS 代码以减少文件大小)
  • 构建 CSS(像 tailwind 那样)
  • 可能还有很多其他重要的事情

正因为如此,如果你今天正在构建一个复杂的前端项目,你可能会使用 Webpack、Rollup、Esbuild、Parcel 或 Vite 等构建系统。

很多这些功能对我很有吸引力,我过去使用构建系统也是出于这样一些原因: 例如,Mess With DNS 使用 Esbuild 来翻译 Typescript,并将许多文件合并成一个大文件。

目标:轻松地对旧的小网站进行修改

我做了很多简单的小网站(之一之二之三之四),我对它们的维护精力大约为 0,而且我改变它们的频率很低。

我的目标是,如果我有一个 3、5 年前做的网站,我希望能在 20 分钟内,

  • 在一台新的电脑上从 GitHub 获取源代码
  • 做一些修改
  • 把它放到互联网上

但我对构建系统(不仅仅是 Javascript 构建系统!)的经验是,如果你有一个 5 年历史的网站,要重新构建这个网站会非常痛苦。

因为我的大多数网站都很小,所以使用构建系统的 优势 很小 —— 我并不真的需要 Typescript 或 JSX。我只要有一个 400 行的 script.js 文件就可以了。

示例:尝试构建 SQL 实验场

我的一个网站(SQL 试验场)使用了一个构建系统(它使用 Vue)。我最后一次编辑该项目是在 2 年前,是在另一台机器上。

让我们看看我今天是否还能在我的机器上轻松地构建它。首先,我们要运行 npm install。下面是我得到的输出:

$ npm install
[lots of output redacted]
npm ERR! code 1
npm ERR! path /Users/bork/work/sql-playground.wizardzines.com/node_modules/grpc
npm ERR! command failed
npm ERR! command sh /var/folders/3z/g3qrs9s96mg6r4dmzryjn3mm0000gn/T/install-b52c96ad.sh
npm ERR! CXX(target) Release/obj.target/grpc/deps/grpc/src/core/lib/surface/init.o
npm ERR!   CXX(target) Release/obj.target/grpc/deps/grpc/src/core/lib/avl/avl.o
npm ERR!   CXX(target) Release/obj.target/grpc/deps/grpc/src/core/lib/backoff/backoff.o
npm ERR!   CXX(target) Release/obj.target/grpc/deps/grpc/src/core/lib/channel/channel_args.o
npm ERR!   CXX(target) Release/obj.target/grpc/deps/grpc/src/core/lib/channel/channel_stack.o
npm ERR!   CXX(target) Release/obj.target/grpc/deps/grpc/src/core/lib/channel/channel_stack_builder.o
npm ERR!   CXX(target) Release/obj.target/grpc/deps/grpc/src/core/lib/channel/channel_trace.o
npm ERR!   CXX(target) Release/obj.target/grpc/deps/grpc/src/core/lib/channel/channelz.o

在构建 grpc 时出现了某种错误。没问题。反正我也不需要这个依赖关系,所以我可以花 5 分钟把它拆下来重建。现在我可以 npm install 了,一切正常。

现在让我们试着构建这个项目:

$ npm run build
  ?  Building for production...Error: error:0308010C:digital envelope routines::unsupported
    at new Hash (node:internal/crypto/hash:71:19)
    at Object.createHash (node:crypto:130:10)
    at module.exports (/Users/bork/work/sql-playground.wizardzines.com/node_modules/webpack/lib/util/createHash.js:135:53)
    at NormalModule._initBuildHash (/Users/bork/work/sql-playground.wizardzines.com/node_modules/webpack/lib/NormalModule.js:414:16)
    at handleParseError (/Users/bork/work/sql-playground.wizardzines.com/node_modules/webpack/lib/NormalModule.js:467:10)
    at /Users/bork/work/sql-playground.wizardzines.com/node_modules/webpack/lib/NormalModule.js:499:5
    at /Users/bork/work/sql-playground.wizardzines.com/node_modules/webpack/lib/NormalModule.js:356:12
    at /Users/bork/work/sql-playground.wizardzines.com/node_modules/loader-runner/lib/LoaderRunner.js:373:3
    at iterateNormalLoaders (/Users/bork/work/sql-playground.wizardzines.com/node_modules/loader-runner/lib/LoaderRunner.js:214:10)
    at iterateNormalLoaders (/Users/bork/work/sql-playground.wizardzines.com/node_modules/loader-runner/lib/LoaderRunner.js:221:10)
    at /Users/bork/work/sql-playground.wizardzines.com/node_modules/loader-runner/lib/LoaderRunner.js:236:3
    at runSyncOrAsync (/Users/bork/work/sql-playground.wizardzines.com/node_modules/loader-runner/lib/LoaderRunner.js:130:11)
    at iterateNormalLoaders (/Users/bork/work/sql-playground.wizardzines.com/node_modules/loader-runner/lib/LoaderRunner.js:232:2)
    at Array.<anonymous> (/Users/bork/work/sql-playground.wizardzines.com/node_modules/loader-runner/lib/LoaderRunner.js:205:4)
    at Storage.finished (/Users/bork/work/sql-playground.wizardzines.com/node_modules/enhanced-resolve/lib/CachedInputFileSystem.js:43:16)
    at /Users/bork/work/sql-playground.wizardzines.com/node_modules/enhanced-resolve/lib/CachedInputFileSystem.js:79:9

这个 Stack Overflow 的答案 建议运行 export NODE_OPTIONS=--openssl-legacy-provider 来解决这个错误。

这很有效,最后我得以 npm run build 来构建这个项目。

这其实并不坏(我只需要删除一个依赖关系和传递一个略显神秘的 Node 选项!),但我宁愿不被那些构建错误破坏。

对我来说,对于小项目来说,构建系统并不值得

对我来说,一个复杂的 Javascript 构建系统对于 500 行的小项目来说似乎并不值得 —— 它意味着放弃了在未来能够轻松更新项目的能力,以换取一些相当微小的好处。

Esbuild 似乎更稳定一些

我想为 Esbuild 大声叫好: 我 在 2021 年了解到 Esbuild,并用于一个项目,到目前为止,它确实是一种更可靠的构建 JS 项目的方式。

我刚刚尝试在一台新电脑上构建一个我最后一次改动在 8 个月前的 Esbuild 项目,结果成功了。但我不能肯定的说,两年后我是否还能轻松的建立那个项目。也许会的,我希望如此!

不使用构建系统通常是很容易的

下面是 Nginx 实验场 代码中导入所有库的部分的样子:

<script src="js/vue.global.prod.js"></script>
<script src="codemirror-5.63.0/lib/codemirror.js"></script>
<script src="codemirror-5.63.0/mode/nginx/nginx.js"></script>
<script src="codemirror-5.63.0/mode/shell/shell.js"></script>
<script src="codemirror-5.63.0/mode/javascript/javascript.js"></script>
<link rel="stylesheet" href="codemirror-5.63.0/lib/codemirror.css">
<script src="script.js "></script>

这个项目也在使用 Vue,但它只是用 <script src 来加载 Vue —— 前端没有构建过程。

一个使用 Vue 的无构建系统模板

有几个人问如何在没有构建系统的情况下开始编写 Javascript。当然,如果你想的话,你可以写原味的 JS,但我常用的框架是 Vue 3。

这是我做的一个小模板,用于启动一个没有构建系统的 Vue 3 项目。它只有 2 个文件和大约 30 行的 HTML/JS。

有些库需要你使用构建系统

构建系统这些事情最近盘旋在我的脑海里,因为这周我正在用 CodeMirror 5 做一个新项目,我看到有一个新版本,CodeMirror 6。

所以我想 —— 很酷,也许我应该使用 CodeMirror 6 而不是 CodeMirror 5。但是 —— 似乎没有构建系统你就不能使用 CodeMirror 6(根据 迁移指南),所以我打算坚持使用 CodeMirror 5。

同样地,你以前可以把 Tailwind 作为一个巨大的 CSS 文件下载,但是 Tailwind 3 似乎根本不能作为一个大的 CSS 文件使用,你需要运行 Javascript 来构建它。所以我现在要继续使用 Tailwind 2。(我知道,我知道,你不应该使用大的 CSS 文件,但是它验收只有 300KB,而且我真的不希望有构建步骤)

(更正:看起来 Tailwind 在 2021 年发布了一个 独立的命令行工具,这似乎是一个不错的选择)

我不完全确定为什么有些库不提供无构建系统版本 —— 也许发布无构建系统版本会给库增加很多额外的复杂性,而维护者认为这不值得。或者,库的设计意味着由于某种原因,不可能发布无构建系统版本。

我希望有更多的无构建系统的 Javascript 技巧

到目前为止,我的主要策略是:

  • 在某个库的网站上搜索 “CDN”,找到一个单独的 Javascript 文件
  • 使用 https://unpkg.com 来查看该库是否有一个我可以使用的内置版本
  • 托管我自己的库的版本,而不是依赖可能崩溃的 CDN
  • 编写我自己的简单集成方案,而不是拉入另一个依赖关系(例如,前几天我为 Vue 编写了自己的 CodeMirror 组件)。
  • 如果我想要一个构建系统,就使用 Esbuild

还有一些看起来很有趣但我还没有研究过的东西:


via: https://jvns.ca/blog/2023/02/16/writing-javascript-without-a-build-system/

作者:Julia Evans 选题:lkxed 译者:wxy 校对:wxy

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

在这篇文章中,我将通过一些通俗易懂的例子来解释 Python 的 input() 函数的基础知识。

Python 是世界上最流行的编程语言之一,广泛应用于各种应用程序中。Python 中的一个基本概念是 input() 函数,它允许用户通过提供输入值与程序进行交互。

让我们看看 input() 函数是如何工作的,以及如何在 Python 程序中有效地使用它。

在开始之前,我希望你已经安装了 Python 并设置了一个基本的编辑器。如果没有,请参考我的以下指南:

Python Input 函数

Python 中的 input() 函数用于接受用户输入的字符串。它提示用户输入一个值并等待用户提供输入。然后将用户的输入作为字符串存储在变量中,稍后可以在程序中使用。

句法

input("your message to user")

当你运行 input() 函数时,它会向用户显示消息并 等待 输入。显示光标等待。当用户输入并按下回车键,input() 函数就会读取用户的输入。该值存储在你指定的变量中。

让我们看几个例子。

示例 1:简单的 input() 函数用法

以下代码片段接受输入并显示带有附加字符串的输出:

your_name = input("Enter your name:")
print("Hello " + your_name)

输出:

Enter your name:arindamHello arindam

Python 输入函数:一个简单的例子

示例 2:以整数和浮点数作为输入

在使用 input() 函数时,你还可以在运行时使用 int()float() 将输入转换。这是一个例子:

no_of_books = int(input("Enter total books ordered:"))
print ("Total number of books:", no_of_books)

price_of_each_book = float(input("Enter unit price:"))
print ("Total price:", no_of_books * price_of_each_book)

输出:

Enter total books ordered:5
Total number of books: 5
Enter unit price:10.1
Total price: 50.5

示例 3:连接列表

你还可以使用其他函数(例如列表)来接受一组值并将它们转换为 Python 中的 列表。这是一个接受输入并将其转换为列表的示例。然后使用另一组值并附加到第一个列表:

# 获取第一个列表的输入
list_1 = list(input("Enter numbers for list 1:"))

# 获取第二个列表的输入
list_2 = list(input("Enter some letters for list 2:"))

# 循环遍历第二个列表并添加到第一个列表
for j in list_2:
    list_1.append(j)

# 打印修改后的第一个列表
print(list_1)

输出:

Enter numbers for list 1:1234
Enter some letters for list 2:ABCD
['1', '2', '3', '4', 'A', 'B', 'C', 'D']

总结

我希望这个简单的指南通过几个示例阐明了 input() 函数。对于简单的场景,它是一个强大的功能,可以从标准输入中接受值。


via: https://www.debugpoint.com/input-function-python/

作者:Arindam 选题:lkxed 译者:geekpi 校对:wxy

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

在处理庞大的数据库时,请尝试这些方便的解决方案,以解决常见的问题。

关系型数据库 PostgreSQL(也被称为 Postgres)已经越来越流行,全球各地的企业和公共部门都在使用它。随着这种广泛的采用,数据库已经变得比以前更大了。在 Crunchy Data,我们经常与 20TB 以上的数据库打交道,而且我们现有的数据库还在继续增长。我的同事 David Christensen 和我收集了一些关于管理拥有巨大表的数据库的技巧。

大表

生产数据库通常由许多具有不同数据、大小和模式的表组成。常见的情况是,最终有一个巨大的、无序的数据库表,远远大于你数据库中的任何其他表。这个表经常存储活动日志或有时间戳的事件,而且对你的应用或用户来说是必要的。

真正的大表会因为很多原因造成挑战,但一个常见的原因是锁。对表的定期维护往往需要锁,但对大表的锁可能会使你的应用瘫痪,或导致堵塞和许多令人头痛的问题。我有一些做基本维护的技巧,比如添加列或索引,同时避免长期运行的锁。

添加索引的问题:在创建索引的过程中锁住表。如果你有一个庞大的表,这可能需要几个小时。

CREATE INDEX ON customers (last_name)

方案:使用 CREATE INDEX CONCURRENTLY 功能。这种方法将索引创建分成两部分,一部分是短暂的锁定,以创建索引,立即开始跟踪变化,但尽量减少应用阻塞,然后是完全建立该索引,之后查询可以开始使用它。

CREATE INDEX CONCURRENTLY ON customers (last_name)

添加列

在数据库的使用过程中,添加列是一个常见的请求,但是对于一个巨大的表来说,这可能是很棘手的,同样是由于锁的问题。

问题:当你添加一个新的默认值为一个函数的列时,Postgres 需要重写表。对于大表,这可能需要几个小时。

方案:将操作拆分为多条基本语句,总效果一致,但控制锁的时间。

添加列:

ALTER TABLE all_my_exes ADD COLUMN location text

添加默认值:

ALTER TABLE all_my_exes ALTER COLUMN location SET DEFAULT texas()

使用 UPDATE 来添加默认值:

UPDATE all_my_exes SET location = DEFAULT

添加约束条件

问题: 你想添加一个用于数据验证的检查约束。但是如果你使用直接的方法来添加约束,它将锁定表,同时验证表中的所有现有数据。另外,如果在验证的任何时候出现错误,它将回滚。

ALTER TABLE favorite_bands ADD CONSTRAINT name_check CHECK (name = 'Led Zeppelin')

方案:告诉 Postgres 这个约束,但不要验证它。在第二步中进行验证。这将在第一步中进行短暂的锁定,确保所有新的/修改过的行都符合约束条件,然后在另一步骤中进行验证,以确认所有现有的数据都通过约束条件。

告诉 Postgres 这个约束,但不要强制执行它:

ALTER TABLE favorite_bands ADD CONSTRAINT name_check CHECK (name = 'Led Zeppelin') NOT VALID

然后在创建后验证它:

ALTER TABLE favorite_bands VALIDATE CONSTRAINT name_check

想了解更多?

David Christensen 和我将在 3 月 9 号到 10 到在加州帕萨迪纳参加 SCaLE 的 Postgres Days。很多来自 Postgres 社区的优秀人士也会在那里。加入我们吧!


via: https://opensource.com/article/23/2/manage-large-postgres-databases

作者:Elizabeth Garrett Christensen 选题:lkxed 译者:geekpi 校对:wxy

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

通过使用命令行让用户告诉程序要什么,可以让程序更加灵活。

 title=

在已经知道要处理什么文件和对文件进行哪些操作的情况下,编写处理文件的 C 语言程序就很容易了。如果将文件名“硬编码”在程序中,或者你的程序只以一种方式来处理文件,那么你的程序总是知道要做什么。

但是如果程序每次运行时能够对用户的输入做出反应,可以使程序更灵活。让用户告诉程序要处理什么文件,或者以不同的方式完成任务,要实现这样的功能就需要读取命令行参数。

读取命令行

一个 C 语言程序可以用如下声明开头:

int main()

这是启动 C 程序最简单的形式。但如果在圆括号中加入标准参数,你的程序就可以从命令行中读取选项了:

int main(int argc, char **argv)

argc 表示命令行中的参数个数。它总是一个至少为 1 的数。

argv 是一个二级指针,它指向一个字符串数组。这个数组中保存的是从命令行接收的各个参数。数组的第一个元素 *argv[0] 是程序的名称。**argv 数组的其它元素包含剩下的命令行参数。

下面我将写一个简单的示例程序,它能够回显通过命令行参数传递给它的选项。它跟 Linux 的 echo 命令类似,只不过我们的程序会打印出程序名。同时它还会调用 puts 函数将命令行选项按行打印输出。

#include <stdio.h>

int
main(int argc, char **argv)
{
  int i;

  printf("argc=%d\n", argc); /* debugging */

  for (i = 0; i < argc; i++) {
    puts(argv[i]);
  }

  return 0;
}

编译此程序,并在运行时提供一些命令行参数,你会看到传入的命令行参数被逐行打印出来:

$ ./echo this program can read the command line
argc=8
./echo
this
program
can
read
the
command
line

这个命令行将程序的 argc 置为 8,**argv 数组包含 8 个元素:程序名以及用户输入的 7 个单词。由于 C 语言中数组下标从 0 开始,所以这些元素的标号分别是 0 到 7。这也是在 for 循环中处理命令行参数时能够用 i < argc 作为比较条件的原因。

你也可以用这个方式实现自己的 catcp 命令。cat 命令的基本功能是显示一个或几个文件的内容。下面是一个简化版的cat 命令,它从命令行获取文件名:

#include <stdio.h>

void
copyfile(FILE *in, FILE *out)
{
  int ch;

  while ((ch = fgetc(in)) != EOF) {
    fputc(ch, out);
  }
}

int
main(int argc, char **argv)
{
  int i;
  FILE *fileptr;

  for (i = 1; i < argc; i++) {
    fileptr = fopen(argv[i], "r");

    if (fileptr != NULL) {
      copyfile(fileptr, stdout);
      fclose(fileptr);
    }
  }

  return 0;
}

这个简化版的 cat 命令从命令行读取文件名列表,然后将各个文件的内容逐字符地显示到标准输出上。假定我有一个叫做 hello.txt 的文件,其中包含数行文本内容。我能用自己实现的 cat 命令将它的内容显示出来:

$ ./cat hello.txt
Hi there!
This is a sample text file.

以这个简单程序为出发点,你也可以实现自己版本的其它 Linux 命令。比如 cp 命令,它从命令行读取两个文件名:要读取的文件和要写入的文件。

读取命令行选项

通过命令行读取文件名和其它文本固然很棒,但是如果想要程序根据用户给出的选项改变行为呢?比如 Linux 的 cat 命令就支持以下命令行选项:

  • -b 显示非空行的行号
  • -E 在行尾显示 $
  • -n 显示行号
  • -s 合并显示空行
  • -T 将制表符显示为 ^I
  • -v^xM-x 方式显示非打印字符,换行符和制表符除外

这些以一个连字符开头的单字母的选项叫做短选项。通常短选项是分开使用的,就像这样 cat -E -n。但是也可以将多个短选项合并,比如 cat -En

值得庆幸的是,所有 Linux 和 Unix 系统都包含 getopt 库。它提供了一种简单的方式来读取命令行参数。getopt 定义在头文件 unistd.h 中。你可以在程序中使用 getopt 来读取命令行短选项。

与其它 Unix 系统不同的是,Linux 上的 getopt 总是保证短选项出现在命令行参数的最前面。比如,用户输入的是 cat -E file -n-E 在最前面,-n 在文件名之后。如果使用 Linux 的 getopt 来处理,程序会认为用户输入的是 cat -E -n file。这样做可以使处理过程更顺畅,因为 getopt 可以解析完所有短选项,剩下的文件名列表可以通过 **argv 来统一处理。

你可以这样使用 getopt:

#include <unistd.h>

int getopt(int argc, char **argv, char *optstring);

optstring 是由所有合法的选项字符组成的字符串。比如你的程序允许的选项是 -E-n, 那么 optstring 的值就是 "En"

通常通过在循环中调用 getopt 来解析命令行选项。每次调用时 getopt 会返回找到的下一个短选项,如果遇到无法识别的选项则返回 '?'。当没有更多短选项时它返回 -1,并且设置全局变量 optind 的值指向 **argv 中所有段选项之后的第一个元素。

下面看一个简单的例子。这个演示程序没有实现 cat 命令的所有选项,但它只是能够解析命令行。每当发现一个合法的命令行选项,它就打印出相应的提示消息。在你自己的程序中,你可能会根据这些命令行选项执行变量赋值等者其它操作。

#include <stdio.h>
#include <unistd.h>

int
main(int argc, char **argv)
{
  int i;
  int option;

  /* parse short options */

  while ((option = getopt(argc, argv, "bEnsTv")) != -1) {
    switch (option) {
    case 'b':
      puts("Put line numbers next to non-blank lines");
      break;
    case 'E':
      puts("Show the ends of lines as $");
      break;
    case 'n':
      puts("Put line numbers next to all lines");
      break;
    case 's':
      puts("Suppress printing repeated blank lines");
      break;
    case 'T':
      puts("Show tabs as ^I");
      break;
    case 'v':
      puts("Verbose");
      break;
    default:                          /* '?' */
      puts("What's that??");
    }
  }

  /* print the rest of the command line */

  puts("------------------------------");

  for (i = optind; i < argc; i++) {
    puts(argv[i]);
  }

  return 0;
}

假如你把程序编译为 args,你可以通过尝试不同的命令行参数组合,来了解程序是怎么解析短选项,以及是怎么将其它的命令行参数留下来的。最简单的例子是将所有的选项都放在最前面,就像这样:

$ ./args -b -T file1 file2
Put line numbers next to non-blank lines
Show tabs as ^I
------------------------------
file1
file2

现在试试将两个短选项合并使用的效果:

$ ./args -bT file1 file2
Put line numbers next to non-blank lines
Show tabs as ^I
------------------------------
file1
file2

如果有必要的话,getopt可以对命令行参数进行重排:

$ ./args -E file1 file2 -T
Show the ends of lines as $
Show tabs as ^I
------------------------------
file1
file2

如果用户输入了错误的短选项,getopt 会打印一条消息:

$ ./args -s -an file1 file2
Suppress printing repeated blank lines
./args: invalid option -- 'a'
What's that??
Put line numbers next to all lines
------------------------------
file1
file2

下载速查表

getopt 还有更多的功能。例如,通过设计 -s string-f file 这样的命令行语法规则,可以让短选项拥有自己的二级选项。你也可以告诉 getopt 在遇到无法识别的选项时不显示错误信息。使用 man 3 getopt 命令查看 getopt(3) 手册可以了解 getopt 的更多功能。

如果你需要 getopt()getopt_long()的使用语法和结构上的提示,可以 下载我制作的速查表。它提供了最小可行代码,并列出了你需要了解的一些全局变量的含义。速查表的一面是 getopt() 的用法,另一面是 getopt_long()的用法。


via: https://opensource.com/article/21/8/short-option-parsing-c

作者:Jim Hall 选题:lujun9972 译者:toknow-gh 校对:wxy

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

学习如何以及何时在 Lua 中使用 while 和 repeat until 循环。

控制结构是编程语言的一个重要特征,因为它们使你能够根据通常在程序运行时动态建立的条件来指导程序的流程。不同的语言提供了不同的控制,在 Lua 中,有 while 循环、for 循环和 repeat until 循环。这篇文章涵盖了 whilerepeat until 循环。由于它们的灵活性,我在一篇 单独的文章 中介绍 for 循环。

条件是由一个使用运算符的表达式来定义的,运算符是你在数学课上可能认识的符号的一个花哨的术语。Lua 中有效的运算符有:

  • == 等于
  • ~=不等于
  • < 小于
  • > 大于
  • 小于或等于
  • >= 大于或等于

这些被称为关系运算符,因为它们比较两个值之间的关联。还有一些逻辑运算符,其含义与英语中的含义相同,可以纳入条件中,进一步描述你想检查的状态:

  • and
  • or

下面是一些条件的例子:

  • foo > 3:变量 foo 是否大于 3?foo 必须是 4 或更大才能满足这个条件。
  • foo >= 3foo 是否大于或等于 3?foo 必须是 3 或更大才能满足这个条件。
  • foo > 3 and bar < 1foo 是否大于 3 而 bar 小于 1?要满足这个条件,foo 变量必须在 bar 为 0 的同时为 4 或更大。
  • foo> 3 or bar < 1foo 是否大于 3?或者,bar 是否小于 1?如果 foo 是 4 或更大,或者 bar 是 0,那么这个条件就是真的。如果 foo 是 4 或更大,而 bar 是 0,会怎样?答案出现在本文的后面。

While 循环

只要满足某个条件,while 循环就会执行指令。例如,假设你正在开发一个应用来监测正在进行的僵尸末日。当没有剩余的僵尸时,就不再有僵尸末日了:

zombie = 1024

while (zombie > 0) do
  print(zombie)
  zombie = zombie-1
end

if zombie == 0 then
  print("No more zombie apocalypse!")
end

运行代码,看僵尸消失:

$ lua ./while.lua
1024
1023
[...]
3
2
1
No more zombie apocalypse!

until 循环

Lua 还有一个 repeat until 循环结构,本质上是一个带有 catch 语句的 while 循环。假设你在从事园艺工作,你想追踪还剩下什么可以收获的东西:

mytable = { "tomato", "lettuce", "brains" }
bc = 3

repeat
   print(mytable[bc])
   bc = bc - 1
until( bc == 0 )

运行代码:

$ lua ./until.lua
brains
lettuce
tomato

这很有帮助!

无限循环

一个无限循环有一个永远无法满足的条件,所以它无限地运行。这通常是一个由错误逻辑或你的程序中的意外状态引起的错误。例如,在本文的开头,我提出了一个逻辑难题。如果一个循环被设定为 foo > 3 or bar < 1 运行 ,那么当 foo 为 4 或更大而 bar 为 0 时,会发生什么?

下面是解决这个问题的代码,为了以防万一,还使用了 break 语句安全捕获:

foo = 9
bar = 0

while ( foo > 3 or bar < 1 ) do
  print(foo)
  foo = foo-1

  -- safety catch
  if foo < -800000 then
    break
  end
end

你可以安全地运行这段代码,但它确实模仿了一个意外的无限循环。有缺陷的逻辑是 or 运算符,它允许这个循环在 foo 大于 3 和 bar 小于 1 的情况下继续进行。and 运算符有不同的效果,但我让你去探索。

无限循环实际上有其用途。图形应用使用技术上的无限循环来保持应用程序窗口的开放。我们没有办法知道用户打算使用这个程序多久,所以程序无限地运行,直到用户选择退出。在这些情况下使用的简单条件显然是一个总是被满足的条件。下面是一个无限循环的例子,为了方便起见,还是内置了一个安全陷阱:

n = 0

while true do
  print(n)
  n = n+1

  if n > 100 then
  break
  end
end

条件 while true 总是被满足,因为 true 总是为真。这是比写 while 1 == 1 或类似的永远为真的简洁方式。

Lua 中的循环

从示例代码中可以看出,尽管有不同的实现方式,但循环基本上都是朝着同一个目标工作。选择一个对你来说有意义的,并且在你需要执行的处理过程中效果最好的。以防万一你需要它:终止失控循环的键盘快捷键是 Ctrl+C


via: https://opensource.com/article/23/2/lua-loops-while-repeat-until

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

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