C-和-Python-应用教程-全-

C 和 Python 应用教程(全)

原文:C and Python Applications

协议:CC BY-NC-SA 4.0

一、Python 编程

这是两章中的第一章,在这两章中,您将回顾 Python 和 C 编程语言。尽管不需要事先了解 Python 或 C 语言,但我们假定您对计算和程序有基本的了解。

在本章中,我们将从 Python 的基础知识开始。这将包括程序中使用的项目如何存储在计算机中,基本的算术格式,处理字符串,读入用户可以在命令行输入的数据,等等。然后,我们将学习计算机上的文件访问,这将在本书结束时把我们引向工业/商业水平的计算。

如果您的计算机上还没有 Python 开发环境,您可以从 www.python.org/downloads/ 免费下载它和开发工具包。访问 Python 的另一种方式是使用 Visual Studio。同样,可以下载它的一个版本。

变量的定义

本节介绍 Python 中使用的不同类型的存储区域。我们将这些存储区域称为“变量”不同的类型可以是数字(整数或小数)、字符和不同类型的组(字符串、数组、字典、列表或元组)。

在这些示例中,您可以在命令行中输入“Python ”,这将启动 Python 环境并生成“> > >”作为提示,让您输入 Python 代码。

在 Python 中,不像 C,你不把变量定义为一个特定的类型。不同的类型有整数、浮点、字符、字符串等。当你给变量赋值时,类型被赋值。所以尝试下面的代码:

>>> a1 = 51
>>> print(type(a1))
We get the output
<class 'int'>
>>>

这里我们定义了一个名为“a1”的变量,并给它赋值整数值 51。

然后,我们用参数“type”和“a1”调用函数“print”,并得到回复“class 'int '”。“类型”是指我们要显示变量是整数、浮点、字符、字符串等。

我们现在可以通过键入“quit()”退出 Python 环境。

我们现在将从程序中执行相同的功能。

创建一个名为“typ1a.py”的文件。

然后输入下面两行 Python 代码:

a1=51
print(type(a1))

现在在命令行输入“python typ1a.py”。

你应该得到输出

<class 'int'>

这和我们的第一个例子一样。

这只是证明了两种方法的等价性。

显然,如果你想运行一个有很多行代码的程序,并且可能要运行很多次,那么把代码放在一个文件中会更有效率。

我们可以使用以下代码演示不同的数据类型存储在同一个变量中:

a1=51
print(type(a1))

a1=51.6
print(type(a1))

a1='51'
print(type(a1))

当我们运行这个时,我们得到

<class 'int'>
<class 'float'>
<class 'str'>

输入的 51 是一个整数。51.6 是浮点(十进制)类型,而“51”是字符串。

如果我们使用 print("a1 is ",type(a1)),我们可以使结果更清楚一些。

所以我们的程序现在是

a1=51
print("a1 is",type(a1))

a1=51.6
print("a1 is",type(a1))

a1='51'
print("a1 is",type(a1))

输出是

a1 is <class 'int'>
a1 is <class 'float'>
a1 is <class 'str'>

我们可以在我们的代码行前面加上“#”字符来添加注释。

a1='51' #assign string containing 51 to variable a1
print("a1 is",type(a1)) # print the type assigned to a1

下面显示了一些简单的算术运算。

以下代码保存在文件“arith1.py”中:

arith1a.py

用整数值初始化变量 v1、v2、v3 和 v4。

v1= 2
v2 = 4
v3 = 7
v4 = 8

将 v1 与 v2 相加,并将结果存储在 v5 中。

v5 = v1 + v2
print(v5)

结果是

6

您可以将添加与打印结合起来,如下所示:

print(v1+v2)

给出相同的答案:

6

现在做减法:

v6 = v4 - v3
print(v6)
giving
1

现在是乘法:

v7 = v4 * v3

print(v7)
giving
56

现在一个部门:

v8 = v4 / v1
print(v8)
giving
4.0

v10 = v3 % v2 # the % sign means show the remainder of the division
print(v10)
gives
3

用 2 的幂来提高。

v11 = v2 ** 2
print(v11)
gives
16

提高到变量 v1 中的幂。

这里 v2 包含 4,v1 包含 2。

v11 = v2 ** v1
print(v11)
gives
16

展示 Python 如何遵守 BODMAS (BIDMAS)的规则。

这里 v2 包含 4,v1 包含 2,v3 包含 7,v4 包含 8。

v11 = v1 + v2 * v4 - v3 # show BODMAS
print(v11)
gives
27

展示 Python 如何遵守普通代数规则。

v11 = (v1 + v2) * (v4 - v3)
print(v11)
gives
6

实数(浮点数)

这种类型的数字包含小数点。所以,对于下面的作业

V1 = 2
V2 = 3.5
V3 = 5.1
V4 = 6.75

我们得到

print(type(V1))
<class 'int'>
print(type(V2))
<class 'float'>
print(type(V3))
<class 'float'>
print(type(V4))
<class 'float'>

特性

在 Python 中,还可以将字符分配给位置,例如:

c1 = 'a'
print(type(c1))
produces
<class 'str'>

这意味着 c1 被归类为字符串。

现在我们知道了我们可以有哪些不同类型的变量,我们将看看如何使用它们。

读入数据

现在我们可以向运行我们程序的人显示一条消息,我们可以要求他们键入一个字符,然后阅读这个字符,并把它打印到屏幕上。本节着眼于用户如何输入程序要读取的数据。

如果我们输入命令

vara = input()

计算机等待用户输入数据。

所以如果你现在输入 r5,计算机将 r5 存储在变量 vara 中。

您可以通过键入以下命令打印 vara 的内容来检查这一点

print(vara)

哪张照片

r5

我们可以通过使用

print("data typed in is:-", vara)
giving
data typed in is:-r5

您还可以通过输入以下命令使输入命令对用户来说更加清晰

varb=input(“enter some data to be stored in varb”)

然后,我们可以再次明确地打印出内容

print("data typed in is:-", varb)
giving
data typed in is:-r5

你必须使用 int(input)来输入一个整数。

否则,它是一个字符串(一个或多个字符),例如:

n = int(input('Enter a number: '))
you enter 4
 >>> print(type(n))
<class 'int'>

# Program to check input
# type in Python

num = input ("Enter number :")
print(num)
#You could enter 5 here and it would store 5 as a string and not as a number
>>> print(num)
5
>>> print ("type of number", type(num))
type of number <class 'str'>
>>>

#entering a float number (type 'float' before the 'input' command)
n = float(input('Enter a number: '))
Enter a number: 3.8
>>> print(type(n))
<class 'float'>
>>> print(n )
3.8

既然我们可以手动将数据输入到程序中,我们将查看数据组。

数组

数组是包含许多项的存储区域。从我们上一节关于整数的内容来看,我们可以用同一个标签定义多个整数。Python 没有默认的数组类型,尽管我们有不同类型的数组。

因此,我们可以有一个名为“firstintarr”的整数数组,其中包含数字 2、3、5、7、11 和 13。每个条目称为一个“元素”,数组中的单个元素可以使用它在数组中的位置来引用。这个位置被称为“指数”数组中的元素必须是同一类型。类型显示在数组的开头。

数组机制必须导入到您的程序中,如下所示:

from array import *
firstintarr = array('i', [2,3,5,7,11])

firstintarr 定义中的“I”表示元素是整数。

我们可以使用索引引用数组的元素,例如:

v1 = firstintarr[3]
print(v1)

这输出

7

我们也可以通过在数组定义中用“f”代替“I”来定义数组中的浮点变量。

所以我们可以定义

firstfloatarr = array( 'f', [0.2,4.3,21.9,7.7])

我们现在可以写了

varfloat1 = firstfloatarr[1]
print(varfloat1)

这会将 4.3 存储到 varfloat1 中。

数组机制必须被导入到你的程序中。

所以在每个程序的开始,你需要包含代码

from array import *

一旦我们有了数组,我们就可以在数组中插入、删除、搜索或更新元素。

数组是一个容器,可以容纳固定数量的项目,这些项目应该是同一类型的。大多数数据结构利用数组来实现它们的算法。以下是理解阵列概念的重要术语:

  • 插入

  • 删除(移除)

  • 搜索

  • 更新

  • 附加

现在让我们来复习一下。

插入到数组中

以下代码位于 array3.py 文件中:

from array import *

myarr = array('i', [2,3,5,7,11])

myarr.insert(1,13) # this inserts 13 into position 1 of the array (counting from 0)

for x in myarr:
 print(x)

这输出

2
13
3
5
7
11

从数组中删除

以下代码位于源代码文件 array4.py 中:

array4.py

from array import *

myarr = array('i', [2,3,5,7,11])

myarr.remove(2) # this removes the element containing 2 from the array

for x in myarr:
 print(x)

这输出

3
5
7
11

搜索

以下代码位于 array5.py 文件中:

from array import *

myarr = array('i', [2,3,5,7,11])

print (myarr.index(3))#this finds the index of the array which contains 3

这输出

1

更新数组

以下代码位于 array6.py 文件中:

array6.py

from array import *

myarr = array('i', [2,3,5,7,11])

myarr[2] = 17) #this updates element 2 with 17

for x in myarr:
 print(x)

这输出

2
3
17
7
11

追加到数组

以下代码位于 array9a.py 文件中:

array9a.py

from array import *

myarr = array('i', [2,3,5,7,11])

for x in myarr:
 print(x)

new = int(input("Enter an integer: "))
myarr.append(new)
print(myarr)

这输出

2
3
5
7
11

输入一个整数:19

array('i', [2, 3, 5, 7, 11, 19])

本节展示了 Python 中“数组”的用法。

用线串

字符串类似于我们在上一节中讨论的字符数组。它们在引号中定义。这些可以是单引号或双引号。我们可以使用 slice 操作符([ ]和[:])来指定我们定义的字符串的一部分。与字符数组一样,我们可以使用它在字符串(索引)中的位置来指定字符串中的单个元素,其中索引从 0 开始。

我们可以使用“+”连接两个字符串,并使用“*”重复该字符串。

我们不能更新字符串的元素——它们是不可变的。这意味着一旦它们被创建,就不能被修改。

以下代码:

firststring = 'begin'
print(firststring)
gives
begin

以下代码:

one = 1
two = 2
three = one + two
print(three)
#gives
3

以下代码:

first = " first "
second= "second"
concat = first + " " + second
print(concat)
#gives
first  second
print("concat: %s" % concat)
gives
concat:  first  second
The following code is in the file tst13a.py
secondstring = "second string"
print(secondstring.index("o")) #gives
3
print(secondstring.count("s")) # count the number of s characters in string gives
2

print(secondstring[2:9]) # prints slice of string from 2 to 9 giving

cond st
print(secondstring[2:9:1]) #  The general form is [start:stop:step] giving

cond st
print(secondstring[::-1]) # Reverses the string giving

gnirts dnoces

splitup = secondstring.split(" ")
print(splitup) #gives
['second', 'string']

字符串是不可变的,所以如果我们尝试

second= "second"
second[0]="q"

我们得到

回溯(最近一次呼叫):

  File "<stdin>", line 1, in <module>

type error:“str”对象不支持项赋值

表明我们试图更新一些不可变的东西(在这个例子中是一个字符串)。

列表

列表类似于数组和字符串,因为您定义了许多元素,这些元素可以使用索引单独指定。然而,对于列表,元素可以是不同的类型,例如,字符、整数和浮点。

以下代码位于 alist7.py 文件中:

firstlist = ['k', 97 ,56.42, 64.08, 'bernard']

我们在方括号中指定元素。

然后,我们可以使用索引(从 0 开始)访问列表中的单个元素,例如:

print(firstlist[0])
gives
k
print(firstlist[1:3])
gives
[97, 56.42]

我们可以修改列表中的元素。

firstlist[3] = 'plj'
print(firstlist)
giving
['k', 97, 56.42, 'plj', 'bernard']

我们可以从列表中删除一个元素。

del firstlist[3]
print(firstlist)
giving
['k', 97, 56.42, 'bernard']

我们可以在列表中添加一个元素。

firstlist.append(453.769)
print(firstlist)
giving
['k', 97, 56.42, 'bernard', 453.769]

读取列表中的条目

以下代码保存在 alist1a.py 文件中:

alist1a.py

list1 = ['first', 'second', 'third']
list2 = [1, 2, 3, 4, 5 ]

print ("list1: ", list1)
print ("list1[0]: ", list1[0])
print ("list2: ", list2)
print ("list2[3]: ", list2[3])
print ("list2[:3]: ", list2[:3])
print ("list2[2:]: ", list2[2:])
print ("list2[1:3]: ", list2[1:3])

这输出

list1:  ['first', 'second', 'third']
list1[0]:  first
list2:  [1, 2, 3, 4, 5]
list2[3]:  4
list2[:3]:  [1, 2, 3]
list2[2:]:  [3, 4, 5]
list2[1:3]:  [2, 3]

更新列表

以下代码保存在文件 alist2a.py 中:

alist2a.py

list1 = [1, 2, 3, 4, 5 ]

print ("list1: ", list1)

list1[1] = 26 #update the second item (counting from zero)

print ("updated list1: ", list1)

这输出

list1:  [1, 2, 3, 4, 5]
updated list1:  [1, 26, 3, 4, 5]

从列表中删除元素

以下代码保存在 alist3a.py 文件中:

alist3a.py
list1 = [1,2,3,4,5,6]
print (list1)
del list1[4]
print ("Updated list1 : ", list1)

这输出

[1, 2, 3, 4, 5, 6]
Updated list1 :  [1, 2, 3, 4, 6]

追加到列表

以下代码保存在文件 alist4aa.py 中:

alist4aa.py
list2 = [10,11,12,13,14,15]
print (list2)
new = int(input("Enter an integer: "))
list2.append(new)
print(list2)

这会输出(如果您输入 489)

[10, 11, 12, 13, 14, 15]

输入一个整数:489

[10, 11, 12, 13, 14, 15, 489]

这显示了列表的用途。

字典

字典包含一个条目列表,其中一个条目充当下一个条目的键。列表是无序的,可以修改。键值关系是唯一的。字典是可变的。

创建字典

在第一个例子中,我们创建了一个空字典。第二,我们有条目。

firstdict = {}

或者

firstdict ={'1':'first','two':'second','my3':'3rd'}

添加到词典中

以下代码保存在文件 adict1a.py 中:

adict1a.py
#create the dictionary
adict1 = {'1':'first','two':'second','my3':'3rd'}

print (adict1)

print (adict1['two'])   # in the dictionary 'two' is the key to 'second'

adict1[4] = 'four' # we want to add another value called 'four' whose key is 4

print (adict1)

print (len(adict1)) #this will print the number of key-value pairs

这输出

{'1': 'first', 'two': 'second', 'my3': '3rd'}
second
{'1': 'first', 'two': 'second', 'my3': '3rd', 4: 'four'}
4

如果我们想要添加其键为 dinsdale 的值,那么我们将其指定为' dinsdale '。

因此

adict1['dinsdale'] = 'doug'
print (adict1)

输出

{'1': 'first', 'two': 'second', 'my3': '3rd', 4: 'four', 'dinsdale': 'doug'}

修订词典

以下代码保存在文件 adict2a.py 中。

这将键为“2”的值修改为“2nd”。

adict2a.py
adict1 = {'1':'first','two':'second','my3':'3rd'}

adict1['two'] = '2nd'

print(adict1)

这输出

{'1': 'first', 'two': '2nd', 'my3': '3rd'}

从字典中删除

以下代码保存在文件 adict3a.py 中:

adict3a.py
adict1 = {'1':'first','two':'second','my3':'3rd'}
print(adict1)
del adict1['two'] #this deletes the key-value pair whose key is 'two'
print(adict1)

这输出

{'1': 'first', 'two': 'second', 'my3': '3rd'}
{'1': 'first', 'my3': '3rd'}

在字典中搜索

我们想搜索字典,看看其中是否包含某个特定的关键字。在这种情况下,我们想看看' a '和' c '是否是字典中的键。

在 Python 中

>>> my_dict = {'a' : 'one', 'b' : 'two'}

>>> 'a' in my_dict
TRUE
>>> 'c' in my_dict
FALSE

以下代码保存在文件 adict5aa.py 中:

adict5aa.py
print("Enter key to be tested: ")
testkey = input()
my_dict = {'a' : 'one', 'b' : 'two'}
print (my_dict.get(testkey, "none"))

这将输出(如果在询问密钥时输入“a ”)

输入要测试的密钥:

a

一个

或输出(如果在询问密钥时输入“x”)

输入要测试的密钥:

x

没有人

我们已经看到字典能做什么。我们现在来看看元组。

元组

元组包含不可变的项目。元组中的元素可以用括号中的逗号分隔,或者用逗号分隔单个引用的元素。它们的访问方式类似于数组,元素从 0 开始编号。在这一节中,我们将研究元组的创建、连接、读取、删除和搜索。

例如,定义两个称为 firsttup 和 secondttup 的元组:

firsttup = ('a', 'b', 'c', 1, 2, 3)
secondtup = “a”, “b”, 10,  25

以下代码引用了 firsttup 的第三个元素:

firsttup[2]
gives
c

以下代码引用了 firsttup 的第三个元素:

firsttup[3]
gives
1
secondtup = "a", "b", 10,  25

以下代码引用 secondtup 的第二个元素:

secondtup[1]
gives
b

以下代码引用了 secondtup 的第三个元素:

secondtup[2]
gives
10

我们还可以使用负索引从末尾开始选择,并向后工作,例如,

secondtup[-1]

这给了

25
secondtup[-2]

这给了

10

无法修改元组。

所以如果我们有

firsttup = ('a', 'b' 'c', 1, 2, 3)
firsttup[3] = 9

我们会得到

  File "<stdin>", line 1, in <module>

TypeError: 'tuple' object does not support item assignment

创建元组

# An empty tuple

empty_tuple = ()
print (empty_tuple)
()

# Creating non-empty tuples

# One way of creation
tup = 'first', 'second'
print(tup)
('first', 'second')

# Another for doing the same
tup = ('first', 'second')
print(tup)
('first', 'second')

连接两个元组

# Code for concatenating 2 tuples

tuple1 = (0, 1, 2, 3)
tuple2 = ('first', 'second')

# Concatenating above two
print(tuple1 + tuple2)
(0, 1, 2, 3, 'first', 'second')

创建嵌套元组

# Code for creating nested tuples

tuple1 = (0, 1, 2, 3)
tuple2 = ('first', 'second')
tuple3 = (tuple1, tuple2)
print(tuple3)
gives
((0, 1, 2, 3), ('first', 'second'))

创建重复的元组

# Code to create a tuple with repetition

tuple3 = ('first',)*3
print(tuple3)
gives
('first', 'first', 'first')

将列表或字符串转换为元组

# Code for converting a list and a string into a tuple

list1 = [0, 1, 2]
print(tuple(list1))
(0, 1, 2)
print(tuple('first')) # string 'first'
('f', 'i', 'r', 's', 't')

创建单元素元组

# Creating tuple with

single element (note that we still require the comma)
t=(1,)
print(t)
gives
(1,)

读取元组

# Reading from start (index starts at zero)

tup1=(2,3,4,5,6,7)
tup[3]
gives
5

# Reading from  end (index starts at -1)
tup1[-1]
gives
7

在元组内搜索

# Search
tup1=(2,3,4,5,6,7)
print (6 in tup1) # this tests if 6 is contained in tup1
gives
True
print (9 in tup1)
gives
False

删除元组

# Deleting a complete Tuple

del tup1
print(tup1)
gives

NameError: name 'tup1' is not defined

使用元组创建变量

# define our tuple as

aTuple = (10, 20, 30, 40)
# Now we can assign each of its elements to separate variables
a, b, c, d = aTuple
print(a)
gives
10
print(b)
gives
20
print(c)
gives
30
print(d)
gives
40

我们已经在本节中介绍了不同类型变量的定义和用法。我们现在来看看“if”语句的用法。

如果是这样的话

当你的程序需要决定是执行一个操作还是另一个操作时,我们使用 if 语句。

这些相当简单。基本上,我们说

if (something is true)
        Perform a task

这是 if 的基本形式。

我们可以这样延伸

if (a condition  is true)
      Perform a task
else if it does not satisfy the above condition
      Perform a different task

下面是演示这一点的一些 Python 代码:

number = 5
if number > 3:
   print('greater than 3')

number = 5
if number > 3:
   print('greater than 3')
else:
   print('not greater than 3')

将这段代码输入程序并运行它。毫不奇怪,输出是

greater than 3

您可以修改程序,以便输入要测试的数字,但是不要忘记,对于这个代码,您需要 number = int(input ("Enter number:"))来输入一个数字。

本节已经展示了“if”语句在编程中的重要性。现在我们来看看循环。

循环(For 和 While)

当我们在一个程序中做许多计算时,用十个数字做类似的事情可能有点麻烦。我们可以通过重复类似的代码十次来完成。我们可以通过编写一段代码,然后在同一段代码中循环十次来使这变得简单一点。这被称为“for 循环”我们还将看看“while”循环。

对于循环

下面是一个 for 循环如何帮助我们的例子。

声明是

'for x in variable
     Carry out some code'

所以如果我们有一个如下的变量

forloopvar1 = [20,13,56,9]

我们可以说

for x in forloopvar1: # go through forloop1 and place each element  in x
    print(x)  #this is the only instruction within the loop

输出

20
13
56
9

Python 中的“范围”指令具有通用格式

范围(开始、停止、步进)

在哪里

“start”是索引的起始值。默认值为 0。

“停止”比要使用的最后一个索引小 1。

“步长”是指索引增加的幅度。默认值为 1。

下面是一个使用“range”指令的例子。

程序从变量 number 和 total 设置为 1 开始循环 for 循环。

在循环中,它将数字的当前值乘以累计值。然后它在数字上加 1。所以它算出 123456789*10 或“10 阶乘”(10!).

number = 1
total = 1
for x in range(10): ): #so here start is 0 (default), stop is 10-1, and step is 1
         total = total * number
        number = number + 1
print(total)

这输出

3628800

你可以用科学计算器来验证它是 10 的阶乘。

for x in range(3, 6): # starts with 3 and ends with 6-1
    print(x)

这输出

3
4
5

我们也可以有一个值的列表,而不是一个范围,如下一个程序所示。

它遍历这些值,找到值 46 的索引位置。我们可以看到 46 在位置 9(从 0 开始计数)。

forloopvar1 = [20, 13, 56, 9, 32, 19, 87, 51, 70, 46, 56]
count = 0
for x in forloopvar1:
     if x == 46:
           break
          count = count + 1
print(count)

这输出

9

While 循环

“while”循环的逻辑类似于我们的 for 循环。

在这里,我们说

'while x is true
      Carry out some code'

因此,我们可以有下面的代码,它不断地向 count 加 1,直到 count 不再小于 10。在循环中,要求用户输入整数。这些被加到一个总数中,在循环结束时打印出来。

total = 0;
number = 0
# while loop goes round 10 times
while number < 10 :

      # ask the user to enter the integer number
      n = int(input('Enter a number: '))

      total = total + n
      number = number + 1
print('Total Sum is = ', total)

因此,如果用户输入如下所示的数字,我们将得到总数:

Enter a number: 1
Enter a number: 2
Enter a number: 3
Enter a number: 4
Enter a number: 5
Enter a number: 6
Enter a number: 7
Enter a number: 8
Enter a number: 9
Enter a number: 10
Total Sum is =  55

我们在这一节已经看到了循环的重要性。我们的下一部分着眼于开关。

开关

在 C 编程中,有一个被广泛使用的指令叫做“switch”但是,因为 Python 中没有 switch 语句,所以本节将演示一些可以包含在您的程序中以执行相同功能的代码。

开关根据它接收到的变量值跳转到一段代码。例如,如果您必须为 30 多岁的人和 40 多岁的人执行不同的代码,并为 50 多岁的人执行不同的代码,我们可以有下面的代码。这里我们有“选项”中的值,它决定了我们跳转到哪个代码。

该函数的代码在文件 as switch 3 . py 中:

aswitch3.py
def switch(option):
     if option == 30:
                print("Code for people in their 30s")
     elif option == 40:
                print("Code for people in their 40s")

     elif option == 50:

               print("Code for people in their 50s")

    else:
        print("Incorrect option")
#main code in the program where you enter 30,40 or 50 and the function 'switch' is called which uses the appropriate number as shown.
 optionentered = int(input("enter your option (30, 40 or 50 : ) "))
switch(optionentered)
running this program and entering '50' gives
enter your option : 50
Code for people in their 50s

本节展示了如何在 Python 中执行切换。我们现在转到 Python 中一个重要的函数库。这叫做“numpy”

使用 Numpy 的算术运算

Numpy 是一个数学函数库,可以包含在 Python 程序中。它在操作数组、读取文本文件和处理数学公式时非常有用。Numpy 可以通过多种方式安装。一种方法是从命令行使用“pip ”,如下所示:

pip install numpy

它在操作矩阵(或具有额外维度的数组)时特别有用。到目前为止,我们看到的数组都是一维数组。在这一节中,我们将看看更多维度的数组。一维数组也可以称为“秩 1 数组”

onedarray = array('i', [10,20,30,40,50])

我们使用“import numpy”将 numpy 导入到我们的程序中,并为我们的程序分配一个链接。这里我们将链接定义为“np ”,因此整行代码是

import numpy as np

numpy 函数“shape”返回数组的维数。所以如果你的数组被定义为

b = np.array([[1,2,3],[4,5,6]])

则该数组将是一个 2×3 矩阵(两行三列),如下所示:

[[1 2 3]
 [4 5 6]]

所以如果你现在输入

print(b.shape)

你会得到

(2, 3)

作为形状。

这个函数的代码在文件 numpy1.py 中:

import numpy as np

a = np.array([1, 2, 3])   # Create a rank 1 array
print(type(a))            # Prints "<class 'numpy.ndarray'>"
print(a.shape)            # Prints "(3,)"
print(a[0], a[1], a[2])   # Prints "1 2 3"
a[0] = 5                  # Change an element of the array
print(a)                  # Prints "[5, 2, 3]"

b = np.array([[1,2,3],[4,5,6]])    # Create a rank 2 array

#1 2 3
#4 5 6
# reference elements counting from 0
# so b[1, 2] is row 1 (2nd row) column 2 (3rd column)
#so if you print b[1, 2] you get 6
print("b[1, 2] follows")
print(b[1, 2])

print(b.shape)                     # Prints "(2, 3)" 2 rows 3 columns
print(b[0, 0], b[0, 1], b[0, 2])   # Prints "1 2 3"
print(b[1, 0], b[1, 1], b[1, 2])   # Prints "4 5 6"
print(b[0, 0], b[0, 1], b[1, 0])   # Prints "1 2 4"

矩阵的正常数学表示如下所示:

img/515548_1_En_1_Figa_HTML.jpg

这就是我们在前面的代码中使用以下代码行定义的内容:

b = np.array([[1,2,3],[4,5,6]])    # Create a rank 2 array

矩阵运算可以通过看现实生活中的例子来理解。以下是为一家电脑公司工作的三个人的表格。第一张表显示了每个人一个月销售多少台笔记本电脑和多少台打印机。

店内销售

Person     Laptops     Printers
Joe         4              5
Mary        6              7
Jen         7              9

下表显示了每个人在网上销售了多少台笔记本电脑和打印机。

在线销售

Person     Laptops     Printers
Joe           6           22
Mary          21          24
Jen           41          17

这些表可以用如下所示的矩阵来表示。我们将第一个矩阵中的每一项与第二个矩阵中的相应项相加,得出第三个矩阵中显示的总数。

img/515548_1_En_1_Figb_HTML.jpg

下表显示了每人售出的笔记本电脑和打印机总数:

总销售额/总销售额

Person    Laptops     Printers
Joe         10            27
Mary        27            31
Jen         48            26

如果每个人的总销售额在下个月翻了一番,我们可以将他们当前的总销售额乘以 2,如下所示:

img/515548_1_En_1_Figc_HTML.jpg

我们现在查看他们第一个月的总额,并有另一个包含笔记本电脑和打印机成本的表。

销售总额

Person      Laptops     Printers           Cost/Item list with cost
Joe          10          27                    Laptop          200
Mary         27          31                    Printer          25
Jen          48          26

我们可以计算出每个人为公司赚了多少钱,方法是用笔记本电脑的总销量乘以成本。然后,我们将他们的打印机销售总数乘以其成本,然后将两者相加。该表及其相应的矩阵表示如下所示:

             Sales Cost
Joe         10x200 + 27x25 = 2975
Mary        27x200 + 31x25 = 3875
Jen         48x200 + 26x25 = 4775

矩阵相乘是有规律的。第一个矩阵的列数必须等于第二个矩阵的行数。

所以如果我们说一个矩阵是 2×3(两行三列),那么它可以乘以一个 3×2 或者一个 3×3 或者一个 3.4 等等。矩阵。它不能乘以 2×3 或 2×4 或 4×2 或 4×3 等。

img/515548_1_En_1_Figd_HTML.jpg

在上图的乘法中,我们看到它是(3x2) x (2x1)产生一个(3x1)矩阵。

数字计算

清单 1-1 到 1-5 是一些用矩阵展示基本 numpy 计算的程序。

添加两个 2x2 矩阵。

import numpy as np

a = np.array(([3,1],[6,4]))

b = np.array(([1,8],[4,2]))

c = a + b

print('matrix a is')
print(a)
print('matrix b is')
print(b)
print('matrix c is')
print(c)

Listing 1-1numpmat.py

这输出

matrix a is
[[3 1]
 [6 4]]
matrix b is
[[1 8]
 [4 2]]
matrix c is
[[ 4  9]
 [10  6]]

将一个 2x3 矩阵添加到另一个 2x3 矩阵中。

import numpy as np

a = np.array(([1,2,3],[4,5,6]))

b = np.array(([3,2,1],[6,5,4]))

d = a + b

c = 2*a

print('matrix a is')
print(a)
print('matrix b is')
print(b)
print('matrix d is')
print(d)
print('matrix c is')
print(c)

Listing 1-2numpmat2.py

这输出

matrix a is
[[1 2 3]
 [4 5 6]]
matrix b is
[[3 2 1]
 [6 5 4]]
matrix d is
[[ 4  4  4]
 [10 10 10]]
matrix c is
[[ 2  4  6]
 [ 8 10 12]]

将一个 2x2 矩阵添加到一个 2x2 矩阵,两个都是浮点型。

import numpy as np

a = np.array(([3.1,1.2],[6.3,4.5]))

b = np.array(([1.3,8.6],[4.9,2.8]))

c = a + b

print('matrix a is')
print(a)
print('matrix b is')
print(b)
print('matrix c is')
print(c)

Listing 1-3numpmat3.py

这输出

matrix a is
[[3.1 1.2]
 [6.3 4.5]]
matrix b is
[[1.3 8.6]
 [4.9 2.8]]
matrix c is
[[ 4.4  9.8]
 [11.2  7.3]]

将一个 3x2 矩阵乘以一个 2x1 矩阵。

import numpy as np

a = np.array(([1,2],[4,5],[6,8]))

b = np.array(([3],[6]))

c = np.matmul(a,b) #matmul is the numpy function for multiplying matrices

print('matrix a is')
print(a)
print('matrix b is')
print(b)
print('matrix c is')
print(c)

Listing 1-4numpmat4.py

这输出

matrix a is
[[1 2]
 [4 5]
 [6 8]]
matrix b is
[[3]
 [6]]
matrix c is
[[15]
 [42]
 [66]]

将一个 3×2 的矩阵乘以一个 2×3 的矩阵。

如果您是用笔和纸手动完成的,您应该这样做:

img/515548_1_En_1_Fige_HTML.jpg

注意你手工做乘法的方式,第一行 x 第一列,第一行 x 第二列,第一行 x 第三列,然后第二行,然后第三行。当然,如果你使用 numpy 的 matmul 函数,这一切都是为你做的。

import numpy as np

a = np.array(([1,2],[3,4],[5,6]))

b = np.array(([7,8,9],[10,11,12]))

c = np.matmul(a,b)

print('matrix a is')
print(a)
print('matrix b is')
print(b)
print('matrix c is')
print(c)

Listing 1-5numpmat5.py

这输出

matrix a is
[[1 2]
 [3 4]
 [5 6]]
matrix b is
[[ 7  8  9]
 [10 11 12]]
matrix c is
[[ 27  30  33]
 [ 61  68  75]
 [ 95 106 117]]

本节探讨了 Python 中重要的数字数学函数。

数学图形函数

与我们在程序中包含 numpy 库类似,我们可以包含名为“matplotlib.pyplot”的图形绘制库,这样我们就可以用代码访问图形函数库

import matplotlib.pyplot as plt

在我们的程序中,这使得 plt 成为指向 matplotlib.pyplot 的指针。

您可以使用“pip”指令安装 matplotlib

pip install matplotlib

这里的程序将绘制一张人们在考试中得分的图表。

这个函数的代码在文件 mp1a.py 中:

mp1a.py
import matplotlib.pyplot as plt
# x values:
marks = list(range(0, 100, 10)) #marks (x values) divided into equal values up to 100
print(marks) # write the values calculated by the previous instruction

这产生了

[0, 10, 20, 30, 40, 50, 60, 70, 80, 90]
 # y values:
people = [4, 7, 9, 17, 22, 25, 28, 18, 6, 2]
# label the axes
plt.xlabel('marks')
plt.ylabel('people')
plt.plot(marks, people)
plt.show()

和输出

[0, 10, 20, 30, 40, 50, 60, 70, 80, 90]

这些绘制了如图 1-1 所示的图表。

img/515548_1_En_1_Fig1_HTML.png

图 1-1

绘制(x,y)点的示例

我们从图表中看到,大多数人的分数在 30 到 70 之间,这是你所期望的。几个得了低分,只有几个得了高分。

下一个程序 mp2aa.py 在相同的轴上绘制了两个图形。这里我们画出了女性和男性的考试分数。我们用一种颜色描绘女性,用另一种颜色描绘男性。

这个函数的代码在文件 mp2aa.py 中:

mp2aa.py
import matplotlib.pyplot as plt
# x values:
marks = list(range(0,100,10)) #marks (x values) divided into equal values up to 100
# y values (number of students whose marks lie within each x range):
male = [4, 7, 9, 17, 22, 25, 28, 18, 6, 2]
female = [2, 5, 8, 13, 28, 25, 23, 20, 18, 12]
plt.xlabel('marks')
plt.ylabel('number of students')

plt.title('Comparison of male / female examination scores')
#plot the female graph
plt.plot(marks, female, label="female")
plt.plot(marks, female, "ob")
#plot the male graph
plt.plot(marks, male, label="male")
plt.plot(marks, male, "or")

plt.legend()
plt.show()

这产生了如图 1-2 所示的下图。

img/515548_1_En_1_Fig2_HTML.png

图 1-2

绘制两个图形的示例

下一个程序 ml1pj.py 在相同的轴上绘制了三个图形。这里,我们画出 y = sin(x),y = 2sin(x),y = cos(x)。我们用不同的颜色画出每一个。

这演示了 matplotlib 可用的一些数学函数。

这个函数的代码在 ml1pj.py 文件中:

ml1pj.py
import matplotlib.pyplot as plt
import numpy as np
X = np.linspace(0, 2 * np.pi, 50, endpoint=True) # set x values as multiples of pi

F1 = 2 * np.sin(X) # y = 2sin(x)
F2 = np.sin(X) #y = sin(x)
F3 = np.cos(X) #y = cos(x)

plt.plot(X, F1, color="blue", linewidth=2, linestyle="-")
plt.plot(X, F2, color="red", linewidth=2, linestyle="-")
plt.plot(X, F3, color="green", linewidth=2, linestyle="-")

plt.plot(X, F1, label="2sin(x)")
plt.plot(X, F2, label="sin(x)")
plt.plot(X, F3, label="cos(x)")

plt.legend()
plt.show()

这就产生了图 1-3 。

img/515548_1_En_1_Fig3_HTML.png

图 1-3

绘制三幅图

本节展示了 matplotlib 在 Python 中的重要性。

用户编写的函数

除了有为你定义的函数(比如在 numpy 或 Matlab 中),你还可以为自己定义函数。

函数的格式如下

def funcname(arguments)

其中 funcname 是您希望调用函数的任何内容,参数是您需要传递给函数的信息。对于不同的函数调用,参数的实际内容可能会有所不同。

如果你想返回一个值给函数的调用者,你可以使用 return 命令。

这个函数的代码在文件 tst16b.py 中:

# Define our 3 functions
def func1():
    #basic function to output a string
    print("This is from func1")

def func2(name, pretax):
    # calculates a person's salary after tax is taken
   aftertax = pretax * 0.85
   print("%s This is from func1, Your salary after tax is %f"%(name,aftertax))

def func3(first,second,third):
    # simple arithmetic calculation on the 3 numbers submitted
    return 3.5*first + second/2 - third

# call func1
func1()

#call func2
func2("Bernard", 23.78)

#call func3
x = func3(1,2,3)
print(x)

这输出

This is from func1
Bernard This is from func1, Your salary after tax is 20.213000
1.5

实际上,您将在程序中定义的函数将比以前的函数更复杂,因为在前面的情况下,在程序的主体中编写代码本身与调用函数一样容易。

文件存取

在 Python 程序中,我们可以创建、读取、写入、更新和删除文件。

我们将使用 matplotlib 中的图形绘制函数,如“数学图形函数”一节所述同样,要在 Python 程序中使用 matplotlib,我们需要在程序的开头使用下面一行代码。

以下程序 re11pjc.py 读取 pjfiley.txt 文件,该文件包含以下 11 行数据:

#pjfiley.txt
a#This is my linexxxxxxxxxxxxxx
b#second linexxxx
c#third linexx
d#fourth linexxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
e#fifth line
f#sixth
g#seventh
h#eighth
i#nineth
j#tenth
k#eleventh

程序打开文件并在“fhand”中存储一个指向它的指针。然后,它执行一个 for 循环,从文件中读取每一行,并将当前行存储在“line”中。该程序的目标是找到行首带有字符“k”的行。当它找到这个时,它打印出该行的其余部分。当遇到“#”字符时,它使用函数“line.split”来拆分行。然后,将“k”存储在“a”中,其余的行存储在“b”中。

#re11pjc.py
fhand = open('pjfiley.txt')
# read each line from the file
for line in fhand:
   if line[0]=='k': # is the first character of the current line 'k'
      a,b = line.split('#') # split the line into two parts, separated by the '#' in the line
      print(b)

fhand.close()

this prints
eleventh

因为以 k 开头的行是 k #第 11 行,所以当我们在#的任一侧拆分它时,我们将“k”存储在存储位置“a”中,将“第 11 行”存储在存储位置“b”中。

以下程序读取 Peoplex.txt 文件,该文件包含以下内容:

#Peoplex
a-Jones-37-accountant

b-Smith-42-HR

c-Allen-28-Secretary

d-Bradley-26-Programmer

e-Edwards-41-Programmer

f-King-35-S/W engineer

g-Price-39-H/W engineer

h-Roberts-52-Manager

i-Foster-44-Analyst

j-Shannon-24-Programmer

k-Lewis-27-Receptionist

这是一个包含公司员工信息的数据文件。每条生产线供一名工人使用。该行的第一个字符是唯一标识工人的参考字符。该行中的其他字段标识他们的姓名、年龄和职务。每个字段由“-”字符分隔,我们使用 line.split()函数来分隔字段。

下面的程序通读文件,查找用户指定的 ID 为的工人。当程序找到它时,它会将该行分割成单独的字段。我们可以通过连接这些字段来打印出这些数据。

#re11pjd.py
fhand = open('Peoplex.txt')

# user is asked to enter the ID character of the line they wish to read.
n = input('Enter an ID: ')

for line in fhand:
   if line[0]==n:
      a,b,c,d = line.split('-') # specified line is found so split it into 4 components
      print(b+' '+c+' '+d) # concatenate the 2nd, 3rd and 4th components

fhand.close()

如果我们输入“d ”,就会得到输出

Enter an ID: d
Bradley 26 Programmer
Amend a field in one of the lines

pjfilezi.bin 文件包含以下数据:

a-Jones-37-accountant
b-Smith-33-welder
c-Allen-28-Secretary
d-Bradley-26-Programmer

我们想修改其中一个条目的年龄和工作描述。

下面的代码可以做到这一点。我们想使用一种方法来更新文件。该方法读取文件并将每一行写入另一个文件。完成后,它会将新文件复制到原始文件中。

#re11pjdga.py
finphand = open('pjfilezi.bin','r') # input file
fouthand = open('pjfilezo.bin','w') # output file
#ask the user to enter the ID for the row to be amended.
n = input('Enter an ID: ')
#We want to update the age and job description to the following values.
age = input('Enter age: ')
desc = input('Enter job description: ')
# find the correct line from the ID entered
for line in finphand:
   if line[0]==n:
       # we have found the correct line
      a,b,c,d = line.split('-') # split the line into its 4 components.
      print(a) #ID
      print(b) #name
      print(c) #age
      print(d) #occupation
      print(b+' '+c+' '+d) #concatenate and print the 2nd, 3rd and 4th
# update
      c=age
      d=desc
      print(b+' '+c+' '+d) # print the amended line
      line=(a+'-'+b+'-'+c+'-'+d+'\n') # store the amended line
      fouthand.write(line) # write the line to the output file

   else:
     # not found the line to be amended so write this line to the output file
      fouthand.write(line)

fouthand.close()
finphand.close()

#close and reopen the files and copy the output file to the input file
file1 = open("pjfilezo.bin", "r")
file2 = open("pjfilezi.bin", "w")
l = file1.readline()
while l:
   file2.write(l)
   l = file1.readline()
file1.close()
file2.close()

如果我们运行程序,输入“c”作为 ID,然后将年龄改为 32 岁,工作描述改为焊工,我们得到

Enter an ID: c
Enter age: 32
Enter job description: welder
c
Allen
28
Secretary

Allen 28 Secretary

Allen 32 welder

下面的程序从文件中读取数据,并使用 matplotlib 绘制图表。

在程序的不同阶段打印不同的存储位置,以便用户可以监视程序正在做什么。这些显示在图表之后。以下程序从 output.txt 文件中读取以下内容:

2.4*x+7.9
0, 20

第一行是要绘制的方程(y = 2.4 * x + 7.9)。

第二行是在绘图中使用的 x 值的范围。

# readfilec2.py###################

该程序读取这两行:

import matplotlib.pyplot as plt
import numpy as np

fhand = open('output.txt','r')
lines = [' ',' ']
count = 0
#store the two lines in lines[0] and lines[1]
for line in fhand:
    lines[count] = line
    count = count + 1
print('lines[0] The first line read from output.txt')
print(lines[0])
print('lines[1] The second line read from output.txt')
print(lines[1])
#strip the newline character from each of the lines
l1 = lines[0].rstrip('\n')
l2 = lines[1].rstrip('\n')
print('l1 The first line read from output.txt with newline character removed ')
print(l1)
print('l2 The second line read from output.txt with newline character removed ')
print(l2)

Import matplot lib is required only when we use this function, try to put the above blocks of code in different modules and import all into another file and plot
def graph(formula, x_range):
# function to plot the graph
    x = np.array(x_range)
    y = eval(formula) # evaluate the formula to give the y-values
    plt.xlabel('x values')
    plt.ylabel('y values')
    plt.title('Line Graph ')
    plt.plot(x, y)
    plt.show()
#get the second line and store the two points on aint and bint
aint=int(l2[3])
#b = 0
bint=int(l2[0])
# call the graph-plotting function with the equation and range as parameters
graph(l1, range(bint, aint))

程序产生如图 1-4 所示的图形。

img/515548_1_En_1_Fig4_HTML.png

图 1-4

y = 2.4x + 7.9 的曲线图

该程序输出以下内容:

lines[0] The first line read from output.txt
2.4*x+7.9

lines[1] The second line read from output.txt
0, 20

l1 The first line read from output.txt with newline character removed
2.4*x+7.9
l2 The second line read from output.txt with newline character removed
0, 20

回归

下一个程序绘制一条回归线和回归计算中使用的四个点。

回归是一系列点对一条直线的近似。所以这里的点是(1,2),(2,5),(3,6),(4,9)。这些点的回归线已经计算为 y = 0 + 2.2x。这些信息已经存储在程序读取的三个文件中。直线和四个点被绘制到图表上来说明回归。

下面的程序从三个文件中获取数据。

第一个文件包含四个点的 x 和 y 坐标。

第二个包含点的回归线。

第三是点数。

在程序的不同阶段打印不同的存储位置,以便用户可以监视程序正在做什么。这些显示在图表之后。

下列程序读取的文件及其内容:

capm.bin
1.000000     2.000000
2.000000     5.000000
3.000000     6.000000
4.000000     9.000000

capm2.bin
0.000000+2.200000*x

capm2cnt.bin
4

该程序使用两种方法访问文件:

fhand = open('capm2.bin','r')

z = np.loadtxt("capm2cnt.bin")
#readfile7a2.py
import matplotlib.pyplot as plt
import numpy as np

fhand = open('capm2.bin','r') #file containing the calculated regression equation
# 'capm.bin' is the file containing the coordinate points
# 'capm2cnt.bin' is the # file containing the number of coordinate points

z = np.loadtxt("capm2cnt.bin") # read the number of points and store in z
print("Data read from capm2cnt.bin")
print("z follows")
print(z)
a = z # this is the number of coordinate points
zint = int(a) # convert the number to an int
print("zint follows")
print(zint)
count = 0
y = np.loadtxt("capm.bin") # read the 4 points and store in y
print("Data read from capm.bin")
print(y)
# y now contains the x and y values of the 4 points
#[[1\. 2.]
# [2\. 5.]
# [3\. 6.]
# [4\. 9.]]

#zeroise the two arrays using zint as the count of points
xvals = [0]*zint
yvals = [0]*zint

print("xvalsfirst")
print(xvals)
print("yvalsfirst")
print(yvals)
#separate the x and y values
for x in range(zint):
    a,b = y[x]
    xvals[x] = a
    yvals[x] = b

print("xvals")
print(xvals)
print("yvals")
print(yvals)

plt.plot(xvals, yvals, "ob")
# read the calculated regression equation from 'capm2.bin' (pointed to by fhand)
count = 0
for line in fhand:
    line = line.rstrip() #rstrip() strips space characters from end of string
print(line) # the calculated regression equation

# set the x values for the graph

x = np.linspace(-5,5,10)
print('x follows')
print(x)
print('line follows')
print(line)
#line is 0.000000+2.200000*x

a = 'y='
b = a + line # b is y = 0.000000+2.200000*x

print(b)
# line contains the regression equation
# The eval command carries out the function contained in 'line'. In this case it is 'y = 0.0 + 2.2*x'
# It takes the values of x from the x = np.linspace(-5,5,10) code above and calculates y values.
y= eval(line) # use the regression equation to calculate the y-values from the x-values above
print('y follows')
print(y)

plt.plot(x, y, '-r', label=b)
#Plot the regression line and the four points
plt.title(b)
plt.xlabel('x', color='#1C2833')
plt.ylabel('y', color='#1C2833')
plt.legend(loc='upper left')
plt.grid()
plt.show()

程序产生如图 1-5 所示的图形。

img/515548_1_En_1_Fig5_HTML.png

图 1-5

y = 0.0 + 2.2x 的图

从图中可以看出,所有点都靠近直线。我们可以用一个数字来告诉我们这些点离直线有多近,而不仅仅是说这些点离直线很近,离直线相当近,或者离直线不是很近。这个号码有名字。它被称为“积矩相关系数”,通常缩写为 PMCC。在前面的例子中,如果点在直线上,PMCC 就是 1。当它们接近直线时,PMCC 大约为 0.92145。我们将在本书的后面讨论 PMCC。

该程序的输出如下:

从 capm2cnt.bin 读取的数据

z follows
4.0
zint follows
4

从 capm.bin 读取的数据

[[1\. 2.]
 [2\. 5.]
 [3\. 6.]
 [4\. 9.]]
xvalsfirst
[0, 0, 0, 0]
yvalsfirst
[0, 0, 0, 0]
xvals
[1.0, 2.0, 3.0, 4.0]
yvals
[2.0, 5.0, 6.0, 9.0]
0.000000+2.200000*x
x follows
[-5\.         -3.88888889 -2.77777778 -1.66666667 -0.55555556  0.55555556
  1.66666667  2.77777778  3.88888889  5\.        ]
line follows
0.000000+2.200000*x
y=0.000000+2.200000*x
y follows
[-11\.          -8.55555556  -6.11111111  -3.66666667  -1.22222222
   1.22222222   3.66666667   6.11111111   8.55555556  11\.        ]

本节探讨了 Python 中的文件处理。

摘要

本章展示了 Python 编程的基础。它展示了不同的数据类型、它们是如何定义的以及它们的属性、执行数学和图形功能的 numpy 和 matplotlib 链接以及文件处理。

下一章将探索 C 代码的基础和使用。

练习

  1. 执行与我们在 1.1.1 节的例子中对 int 值使用的相同的算术运算

    使用

    V1 = 2

    V2 = 3.5

    V3 = 5.1

    V4 = 6.75

  2. 来自第 1.1.3 节

    创建一个从 1 到 7 的数字列表。打印这个列表,然后使用 for 循环将接下来的 7 个数字(8 到 14)追加到列表中(参见“For 循环”一节)。

    创建包含元素{'a' : \one\,' b' :''two'}的字典。要求用户输入要测试的密钥。使用 for 循环测试该键是否在字典中(参见“For 循环”一节)。输出一条适当的消息,说明您是否找到了它。

    创建一个元组,元素为从 2 到 14 的偶数。使用 for 循环打印出元素(参见“For 循环”一节)。

  3. 来自第 1.1.10 节

    修改文件工人的数据文件程序,创建一个文件,在他们的名字后面也有他们的首字母,并在最后一行加上他们的工资。

二、编程

在这一章中,我们将回顾 C 编程语言。如果你的计算机上还没有 C 开发环境,你可以从微软免费下载。您可以下载他们的 Microsoft 开发工具包(SDK)。另一种访问 C 的方法是使用 Visual Studio。同样,可以下载它的一个版本。

c 程序格式

清单 2-1 是一个简单的 C 程序,它要求用户输入一个字符,读取该字符,然后将其打印到屏幕上。我们将用它来展示 C 程序的基本格式。

当你写了很多程序时,给每个程序一个相关的标题是很有帮助的。这个程序被称为 c1.2readprint.c,“c1.2”,因为它在第二章的第一部分,而“readprint”是因为它读取并打印一个字符。的”。C "对于任何 C 程序来说都是必不可少的。如果没有这个,c 程序将无法编译。编译器把你的程序转换成计算机硬件能理解的机器代码。

在下面的程序中,int main()将您的代码分隔在{和}之间(尽管我们将在后面看到,您可以在 main()部分之外编写一段单独的代码,并从 main()部分调用它。#include <stdio.h>是一个命令,告诉编译器将执行 getchar()和 putchar()的代码附加到您的可执行程序上。stdio 指的是标准输入输出库。</stdio.h>

注释写在这个程序中,显示/提醒程序中正在做什么。它们写在//之间。如此处所示,它们可以写在同一行或不同行的 C 代码之后。

代码中的“printf”告诉计算机打印每个双引号之间的内容。

getchar 和 putchar 指令读取和打印一个字符。

#include <stdio.h>
/* read and display a number */
int main () {
   char c; /* store area where the character read in will be kept */

   printf("Enter character: "); /* ask the user to enter a character */
   c = getchar(); /* read the character in */

   printf("Character entered: "); /* tell the user what character the program has read */
   putchar(c); /* write the character */

   return(0);
}

Listing 2-1c1.2readprint.c

char c;指令意味着你在程序中保留了一个位置,用来存储读入的字符。c 可以被称为你程序中的一个“变量”。在代码 c=getchar()中,=符号表示“分配给”指令说获取字符,并把它赋给变量 c,输入一个字符。你的程序应该回复你输入的字符。现在输入你的名字。会发生什么?getchar()只读取一个字符,它只会将您键入的第一个字符存储到程序的 char c 数据存储中。注意程序中的注释告诉你正在发生什么。我

将两个数相加

在清单 2-2 中,我们要求用户输入两个整数。然后我们把这些加起来,把答案打印出来。然后我们要求用户输入两个浮点数,我们将它们相加并显示答案。

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>

int main()
{
      int int_number1, int_number2, itotal; /* storage areas for the int numbers */
      float float_number1, float_number2, ftotal; /* storage areas for the float numbers */

      /* ask the user to enter two integers */

      printf("Please enter an integer number:\n ");
      scanf("%d", &int_number1); /* read integer number in */
      printf("You entered %d\n", int_number1);

      printf("Please enter another integer number: \n");
      scanf("%d", &int_number2); /* read integer number in */
      printf("You entered %d\n", int_number2);

      /* add the two numbers into ‘total’ and display the answer */

      itotal = int_number1 + int_number2; /* add two numbers */
      printf("total is %d\n", itotal);

      /* ask the user to enter two floating point (decimal) numbers */

      printf("Please enter a float number:\n ");
      scanf("%f", &float_number1); /* read decimal number in */
      printf("You entered %f\n", float_number1);

      printf("Please enter another float number: \n");
      scanf("%f", &float_number2); /* read decimal number in */
      printf("You entered %f\n", float_number2);

      /* add the two numbers into 'total' and display the answer */

      ftotal = float_number1 + float_number2; /* add the numbers */
      printf("total is %f\n", ftotal);

      return 0;
}

Listing 2-2c1.2addtwodf.c

在这个程序中,我们读入整数和浮点数。我们用 int 定义每个整数的存储,如程序开始所示,用 float 定义浮点数的存储。我们还指定了存储位置,用于在添加数字时存储总数。这是整数的 itotal 和浮点数的 ftotal。注意,我们可以在 int 命令后列出所有的存储名称,只要它们都是 int 类型。“类型”是我们区分数据的方式,例如,整数是“integer”或“int”以及字符,如“A”、“$”和“?”是“char”类型。

在这个程序中,我们使用 scanf 从屏幕上读取字符,而不是 getchar()。这是因为我们要添加的数字可以不止一个字符。scanf 和 printf 中的%d 指定要读取或写入的整数。scanf 和 printf 中的%f 指定要读取或写入的整数。在 printf 这里,要打印的答案存储在 itotal 或 ftotal 中。

两个数相乘并相除

在清单 2-3 中,我们输入两个浮点数。首先,我们将它们相乘并打印出答案;然后,我们将第一个数字除以第二个数字,并显示答案。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

/*  multiply two floating point numbers */

int main()
{
      float this_is_a_number1, this_is_a_number2, total; /* storage areas for the numbers */

      /* ask the user to enter two floating point (decimal) numbers */

      printf("Please enter a number:\n ");
      scanf("%f", &this_is_a_number1); /* read number in */
      printf("You entered %f\n", this_is_a_number1);

      printf("Please enter another number: \n");
      scanf("%f", &this_is_a_number2); /* read number in */
      printf("You entered %f\n", this_is_a_number2);

       /* multiply  the two numbers into 'total' and display the answer */

      total = this_is_a_number1 * this_is_a_number2; /* multiply the numbers */
      printf("product is %f\n", total);

      /* divide  the two numbers into 'total' and display the answer */

      total = this_is_a_number1 / this_is_a_number2; /* divide the numbers */
      printf("quotient is %f\n", total);

      return 0;
}

Listing 2-3c1.2multdiv.c

这一节涉及基本数据和算术运算。下一节着眼于在编程中使用循环的有用性。

对于循环

当我们在做两个数字的程序时,用十个数字做类似的事情会有点麻烦。我们可以通过重复类似的代码十次来完成。我们可以通过编写一段代码,然后在同一段代码中循环十次来使这变得简单一点。这被称为“for 循环”

清单 2-4 是一个 for 循环如何帮助我们的例子。

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
/* demonstrate a forloop */
main()

{
      float this_is_a_number, total; /* storage areas for the numbers */
      int i;

      total = 0;

      /* forloop goes round 10 times */
      for (i = 0;i < 10;i++)
      {
            /* ask the user to enter the floating point (decimal) number */

            printf("Please enter a number:\n ");
            scanf("%f", &this_is_a_number); /* read number in */
            total = total + this_is_a_number;

      }
      printf("Total Sum is = %f\n", total);
}

Listing 2-4c1.2for.c

for 语句的语法是

for(初始值;最终值;增量)

执行循环的代码包含在 for 语句后的}和语句后的}中。

在 for 语句中,变量 I 用作在循环过程中要递增和测试的变量。I 的初始值为 0,如 for 语句的第一部分所示;然后每次代码在循环中完成,I 就加 1(这就是 i++所做的)。在每个循环之后,进行测试以查看 I 值是否已经达到 10(这是 i<10 的部分)。当它出现时,循环停止。所以在这种情况下,循环中的代码执行了十次。在代码中,要求用户输入一个数字。在每个循环中,这个值被加到 total 中,然后最终的值被打印出来。

Do While 循环

还有另一种方法可以做与 for 循环类似的事情,但是格式稍有不同。循环说“do”,然后在{}内,再次包含一系列命令,以“while …”结束,其中“…”只是为真的条件。当条件不为真时,它退出循环。所以使用“do”循环来做和 for 循环一样的事情。do 循环中的 i++指令只是将 I 当前包含的值加 1。减去 1 只是我。

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
/* demonstrate a do loop */
main()

{
      float this_is_a_number, total; /* storage areas for the numbers */
      int i;

      total = 0;
      i = 0;
      /* do loop goes round until the value of i reaches 10 */
      do {

            printf("Please enter a number:\n ");
            scanf("%f", &this_is_a_number);
            total = total + this_is_a_number;
            i++;

      }while( i < 10);
      printf("Total Sum is = %f\n", total);
}

你应该会发现你得到的结果和你的 for 循环程序是一样的。

在了解了环路的用处之后,我们现在来看看交换机。

开关指令

C 语言中另一个有用的指令是 switch。它接受一个值,并根据该值跳转到代码中的适当位置。在清单 2-5 中,用户可以输入 1 到 5 之间的任意整数值。

switch 指令取值,如果是 1,跳转到case 1:;如果是 2,跳转到case 2:;等等。如果输入的数字不是一个从 1 到 5 的整数,它会进入default:输出一个错误信息。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
/* Example of a switch operation */
int main()
{
      int this_is_a_number; /* storage areas for the numbers */

      /* ask the user to enter integer number */

      printf("Please enter an integer between 1 and 5:\n ");
      scanf("%d", &this_is_a_number);

      /* Move to the appropriate case statement corresponding to the entered number */

      switch (this_is_a_number)
      {

      case 1:
            printf("Case1: Value is: %d", this_is_a_number);
            break;
      case 2:
            printf("Case2: Value is: %d", this_is_a_number);
            break;
      case 3:
            printf("Case3: Value is: %d", this_is_a_number);
            break;
      case 4:
            printf("Case4: Value is: %d", this_is_a_number);
            break;
      case 5:
            printf("Case5: Value is: %d", this_is_a_number);
            break;
      default:
            printf("Error Value is: %d", this_is_a_number); /* The number entered was not between 1 and 5 so report the error*/
      }
      return 0;
}

Listing 2-5c1.2swi.c

你可以做类似的事情,但是使用特定的字符而不是数字。然后,使用字符作为案例名称跳转到适当的位置,例如,如果键入 a,则跳转到案例 a:。

上一节展示了如何使用 switch 语句跳转到特定的代码段。下一节做了类似的事情,但是使用了“if”和“else”。

否则

当你的程序需要决定是执行一个操作还是另一个操作时,我们使用 if 语句。

这些相当简单。基本上,我们说(以下不是实际代码)

if (something is true)
      Perform a task

这是 if 的基本形式。

我们可以这样延伸

if (something is true)
      Perform a task
else
      Perform a different task

下面是演示这一点的一些 C 代码:

#include <stdio.h>
/* Example of an if operation */
int main()
{

     int this_is_a_number; /* storage area for the number*/
      /* ask the user to enter a specific integer */

      printf( "Please enter an integer between 1 and 10:\n " );
      scanf( "%d", &this_is_a_number );

      if (this_is_a_number <6)
            printf( "This number is less than 6;\n " );

      /* ask the user to enter another specific integer */

      printf( "Please enter an integer between 10 and 20:\n " );
      scanf( "%d", &this_is_a_number );

      if (this_is_a_number <16)
            printf( "This number is less than 16\n " );
      else
            printf( "This number is greater than 15\n " );

    return 0;
}

创建并测试您的程序。当您测试时,最好测试到每个极限,甚至输入不正确的数据。这里没有检查你是否真的进入了指定的范围。您可以自己添加一个测试。

“if then else”类型的命令有一个扩展。这就是“if then else if ”,在这里您添加了一个额外级别的 if。下面是您上一个程序的扩展来添加这个。

如果,如果

清单 2-6 与前一个清单做了相同的 if,但是它不是跟在 else 后面,而是做 else if 来测试另一个选项。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
/* Example of an if then else if operation */
int main()
{
      int this_is_a_number; /* storage area for the number*/

      /* ask the user to enter a specific integer */

      printf("Please enter an integer between 1 and 10:\n ");
      scanf("%d", &this_is_a_number);

      if (this_is_a_number < 6)
            printf("This number is less than 6;\n ");

      /* ask the user to enter another specific integer */

      printf("Please enter an integer between 10 and 20:\n ");
      scanf("%d", &this_is_a_number);

      if (this_is_a_number < 16)
      {
            printf("This number is less than 16\n ");
      }
      else if (this_is_a_number == 20)
      {
            printf("This number is 20\n ");
      }
      else
      {
            printf("This number is greater than 15\n ");
      }

      return 0;
}

Listing 2-6c1.2if.c

这里,它测试输入的数字是否小于 16。如果是,它会打印“这个数字小于 16”;否则,它将测试该数字是否等于 20。如果是,它打印出“这个数是 20”。否则,它会打印出“该数字大于 15 但不是 20”。

已经看到了“if”语句的用处,我们现在将转向数组。

数据阵列

在我们的程序中有另一种存储数据的方法,而不仅仅是在单独的位置。这些被称为“数组”它们可以定义为“int ”,其中数组的所有元素都是整数。它们可以是“char ”,其中所有元素都是字符。还有其他类型,我们稍后会看到。我们用插入方括号中的数组长度定义一个整数数组,例如,int arr[8]表示一个包含八个元素的数组。在这种情况下,“arr”是数组的名称。

清单 2-7 向我们展示了如何读入八个整数并将它们存储在一个数组中。

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
/* program to show array use */

int main()

{
      int arr1[8]; /* define an array of 8 integers */
      int i;

      /* ask the user to enter 8 integers */

      printf("enter 8 integer numbers\n");

      for (i = 0;i < 8;i++)
      {

            scanf("%d", &arr1[i]);  /* read the entered integers into arr1[i] */
      }
      /* print out the contents of the array */

      printf("Your 8 numbers are \n");

      for (i = 0;i < 8;i++)
      {
            printf("%d ", arr1[i]);
      }
      printf("\n");

}

Listing 2-7c1.2arr.c

创建这个程序并测试它。它将读取您输入的八个字符,并将它们存储在数组“arr1”中。然后它读取 arr1 并打印出其内容。

为了在数组中读写字符,我们将其定义为“char ”,注意我们在 scanf 和 printf 中使用了%c,因为%c 需要字符而%d 需要整数。

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
/* program to show character array use */

int main()

{
      char arr2[10]; /* define array of 10 characters */
      int i;

      /* ask the user to enter 10 characters */

      printf("enter 10 characters \n");

      for (i = 0;i < 10;i++)
      {
            scanf("%c", &arr2[i]); /* read each character entered into the array */
      }

      printf("Your 10 characters are \n");

      /* print out the contents of the array */

      for (i = 0;i < 10;i++)
      {
            printf("%c ", arr2[i]);
      }
      printf("\n");

}

当我们写软件解决数学问题时,数组真的很有用。我们可以扩展我们刚刚学到的想法。如果我们说我们刚刚使用的 int 数组是一维的(即一行中的数字),我们可以有一个二维数组(就像矩阵中的数字一样。)

下面是一个程序,允许你输入数据到一个二维数组。它的一部分最多可以有八个整数,另一部分最多可以有七个整数。这里定义为 int arr1[7][8]。你可以这样想象:

   1      2     3     4     5     6     7    8
   4      3     4     5     6     7     8    9
   0      4     5     6     7     8     9   10
   9      5     6     7     8     9    10   11
   3      7     8     9    10    11    12   13
   8      8     9    10    11    12    13   14
   6      9    10    11    12    13    14   15

该阵列有 7 行 8 列,可以称为 7×8 阵列(类似于数学中的 7×8 矩阵)。清单 2-8 将数据读入数组。

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>

/* example of a 2D array test*/
int main()

{
      int arr1[7][8]; /* 2D array */

      int i, j, k, l;

      /* ask the user to enter number of rows and columns to be used */

      printf("enter number of rows and columns (max 7 rows max 8 columns) \n");
      scanf("%d %d", &k, &l); /* store the number of rows and columns */

      /* test if the user has exceeded the limits for rows or columns */

      if (k > 7 || l > 8)
      {
            printf("error - max of 8 for rows or columns\n");

      }

      else
      {
            /* ask the user to enter the data for the arrays */

            printf("enter array\n");
            for (i = 0;i < k;i++)
            {
                  for (j = 0;j < l;j++)
                  {
                        scanf("%d", &arr1[i][j]);
                  }
            }

            /* print out the 2D array */

            printf("Your array is \n");
            for (i = 0;i < k;i++)
            {
                  for (j = 0;j < l;j++)
                  {
                        printf("%d ", arr1[i][j]);
                  }
                  printf("\n");

            }
      }

}

Listing 2-8c1.2arr2D.c

这个项目中有一些新的想法。除了二维数组之外,我们还有一个嵌套 for 循环的例子,如前所述。我们也看到了一些在你的程序中非常有用的东西。这被称为“数据审查”如果你看看我们数组的定义,它的第一部分有 7 个整数,第二部分有 8 个整数。如果用户试图输入八个以上的字符,就会导致程序出错而失败。我们可以通过检查用户没有为每个部分输入超过最大预期数量的整数来防止这种情况。这就是第一个“如果”语句的作用。程序的第一部分将“行数”存储到 k 中,将列数存储到 l 中。if 语句说明,如果行数大于 7 或列数大于 8,则它会输出一条错误消息并终止程序。符号“||”表示“或”

2D 阵列逐行存储。因此,如果您输入如上所示的 7x8 矩阵中的数据,并打印出第一行,那么您应该得到 1 2 3 4 5 6 7 8。您可以编写一个快速测试程序来完成这项工作。

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>

/* example of a 2D array test with extras*/
int main()

{
      int arr1[7][8]; /* 2D array */

      int i, j, k, l;

      /* ask the user to enter number of rows and columns to be used */

      printf("enter number of rows and columns (max 7 rows max 8 columns) \n");
      scanf("%d %d", &k, &l);
      if (k > 7 || l > 8)
      {
            printf("error - max of 8 for rows or columns\n");

      }

      else
      {
            printf("enter array\n");
            for (i = 0;i < k;i++)
            {
                  for (j = 0;j < l;j++)
                  {
                        scanf("%d", &arr1[i][j]);
                  }
            }
            printf("Your array is \n");
            for (i = 0;i < k;i++)
            {
                  for (j = 0;j < l;j++)
                  {
                        printf("%d ", arr1[i][j]);
                  }
                  printf("\n");

            }
      }

      /* print out the first row of the 2D array */

      printf("first row of array\n");
      for (j = 0;j < l;j++)
      {
            printf("%d ", arr1[0][j]);
      }
      printf("\n");

}

这和你的 2D 数组程序是一样的,除了在最后,它多做了一点。

for(j=0;j<k;j++)
{
      printf("%d ",arr1[0][j]);
}

这只是打印出 arr[0][0],arr[0][1],arr[0][2],arr[0][3],arr[0][4],arr[0][5],arr[0][6]和 arr[0][7]。这就是数据在 2D 数组中的存储方式。如果你想要第二行,你只需要改变 printf("%d ",arr 1[0][j]);在最后一个 for 循环中打印 f("%d ",arr 1[1][j]);。

当你写程序对矩阵进行运算时,二维数组是至关重要的。

img/515548_1_En_2_Figa_HTML.jpg

这里,我们将两个 3x2 矩阵相加,生成另一个 3x2 矩阵。正如您在前面的图表中所看到的,我们只需将相应的行和列相加,就可以在第三个矩阵的相应位置产生一个和。

清单 2-9 演示了这一点。第一个矩阵是 matarr1,第二个是 matarr2。您可以看到,这些矩阵被预定义为与前面的矩阵具有相同的值。两者之和放入 matadd。嵌套的 for 循环首先将 matadd 清零。另一个嵌套的 for 循环执行加法。

/* Matrix program */
/* add two matrices */
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{

      int matarr1[3][2] = {
      {1, 2},
      {3, 4},
      {5, 6}
      };

      int matarr2[3][2] = {
      {2, 3},
      {4, 5},
      {6, 7}
      };

       int matadd[3][2];/* matrix answer (rowxcolumn)*/
       int i,j,k;
       int r1,c1,r2,c2;/* row and col for 1st and 2nd matrices */

       r1=3;
       c1=2;
       r2=3;
       c2=2;

       for(i=0;i<r1;i++)
       {
             for(j=0;j<c2;j++)
             {
                   matadd[i][j]=0;/* clear the matrix */
             }

       }

       printf("Your first matrix is \n");
       for(i=0;i<r1;i++)
       {
             for(j=0;j<c1;j++)
             {
                   printf("%d ",matarr1[i][j]);  /* first matrix in
                   matarr1 */
             }
             printf("\n");
       }

       printf("Your second matrix is \n");
       for(i=0;i<r2;i++)
       {
             for(j=0;j<c2;j++)
             {
                   printf("%d ",matarr2[i][j]);  /* second matrix in
                   matarr2 */
             }
             printf("\n");
       }
       /* add corresponding elements of the matrices into matadd */
       for(i=0;i<r1;i++)
       {
             for(j=0;j<c2;j++)
             {
                   for(k=0;k<r2;k++)
                   {
                         matadd[i][j] = matarr1[i][j]
                         + matarr2[i][j];
                   }

             }
       }
       /* Write the solution */
       printf("Your matrix multiplication is \n");
       for(i=0;i<r1;i++)
       {
             for(j=0;j<c2;j++)
             {
                   printf("%d ",matadd[i][j]);
             }
             printf("\n");
       }

}

Listing 2-9c1.2matadd.c

下图显示了两个矩阵相乘的机制。对于两个矩阵,第一个矩阵的列数必须等于第二个矩阵的行数。对于下面的矩阵,第一个矩阵是 3x2(三行两列),第二个矩阵是 2x1(两行一列),所以这些可以相乘。查看图中的第三个矩阵,您可以看到乘法是如何工作的。

img/515548_1_En_2_Figb_HTML.jpg

清单 2-10 用两个预设矩阵 matarr1 和 matarr2 执行前面的乘法。乘法的结果保存在矩阵 matmult 中。这最初被清除为零。

/* Matrix program */
/* multiply two matrices */
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{

      int matarr1[3][2] = {
      {10, 27},
      {27, 31},
      {48, 26}
      };

      int matarr2[2][1] = {
      {200},
      {25}
      };

       int matmult[3][1]; /* matrix answer (rowxcolumn)*/
       int i,j,k;
       int r1,c1,r2,c2; /* row and col for 1st and 2nd matrices */

       r1=3;
       c1=2;
       r2=2;
       c2=1;

       for(i=0;i<r1;i++)
       {

             for(j=0;j<c2;j++)
             {
                   matmult[i][j]=0; /* clear the matrix */
             }
       }

       printf("Your first matrix is \n");
       for(i=0;i<r1;i++)
       {
             for(j=0;j<c1;j++)
             {
                   printf("%d ",matarr1[i][j]); /* first matrix in
                   matarr1 */
             }
             printf("\n");
       }

       printf("Your second matrix is \n");
       for(i=0;i<r2;i++)
       {
             for(j=0;j<c2;j++)
             {
                   printf("%d ",matarr2[i][j]); /* second matrix in
                   matarr2 */
             }
             printf("\n");
       }
       /* multiply corresponding elements of the matrices into matmult */
       for(i=0;i<r1;i++)
       {
             for(j=0;j<c2;j++)
             {
                   for(k=0;k<r2;k++)
                   {
                         matmult[i][j] = matmult[i][j] + matarr1[i][k]
                         * matarr2[k][j];
                   }
             }
       }
       /* Write the solution */
       printf("Your matrix multiplication is \n");
       for(i=0;i<r1;i++)
       {
             for(j=0;j<c2;j++)
             {
                   printf("%d ",matmult[i][j]);
             }
             printf("\n");
       }

}

Listing 2-10c1.2matmult4.c

您已经看到了扩展我们的数据定义以包含数组的重要性。

功能

有时候当你写程序的时候,你会发现你可能会在程序的不同地方写相似的代码行。如果您将这些类似的代码行放在一个单独的地方,并在需要时调用它们,您可以使这更容易做到,并且其他人也更容易理解您的代码所做的事情。这组独立的代码称为函数。如果函数每次被调用时都要做稍微不同的事情,这没关系,因为每次调用时你都可以用不同的参数来调用函数。下面的代码将演示这一点。这是一段相当琐碎的代码,但它说明了这一点。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

/* This code demonstrates what a function does */
/* The function here compares two numbers and says which is bigger */
/* The user enters three numbers and gets told which is bigger than which !*/

      void myfunction(int a,int b); /* declaration of your function and its parameters */

      int first , second, third;
main()
{

      /* ask the user to enter the three numbers to be compared */

       printf( "Please enter first integer number: " );
       scanf( "%d", &first );
       printf( "Please enter second integer number: " );
       scanf( "%d", &second );
       printf( "Please enter third integer number: " );
       scanf( "%d", &third );

      myfunction(first , second); /* compare the first with the second */
      myfunction(first , third); /* compare the first with the third */
      myfunction(second , third); /* compare the second with the third */
}
void myfunction(int a,int b)
/* the function is outside the main{} part of the program */
/* The function just compares the two parameters, a and b, and says which is greater*/
{

      if(a>b)
            printf("%d is greater than %d\n", a,b);
      else if (a<b)
            printf("%d is greater than %d\n", b,a);
      else
            printf("%d and %d are equal\n", a,b);
}

这里的函数叫做 myfunction。注意,它是在 main{}之外定义的。它是在程序开始时声明的。给这个函数两个数字,a 和 b,它比较这两个数字,然后说哪个更大。在程序的主要部分,用户被提示输入三个数字。然后,在代码的主要部分,这些被输入到对 myfunction 的调用中。

这是一段相当简单的代码,但是它展示了如何使用一个函数。

清单 2-11 也展示了函数是如何使用的。这段代码基于你在本章“数据数组”一节中编写的程序。它打印出 2D 数组的特定行。对该函数的一个调用要求该函数打印出数组的第二行,另一个调用要求它打印出第一行。

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>

/* example of a function*/
void printarow(int row, int cols, int arr[8][8]);
int main()

{
      int arr1[8][8];

      int i, j, rows, cols;

      /* ask the user to enter the rows and columns */

      printf("enter number of rows and columns (max 8 rows max 8 columns) \n");
      scanf("%d %d", &rows, &cols);
      if (rows > 8 || cols > 8)
      {
            printf("error - max of 8 for rows or columns\n");

      }

      else
      {
            printf("enter array\n");
            for (i = 0;i < rows;i++)
            {
                  for (j = 0;j < cols;j++)
                  {
                        scanf("%d", &arr1[i][j]);
                  }
            }
            printf("Your array is \n");
            for (i = 0;i < rows;i++)
            {
                  for (j = 0;j < cols;j++)
                  {

                        printf("%d ", arr1[i][j]);
                  }
                  printf("\n");

            }
      }
      printarow(2, cols, arr1); /* This calls to print out row 2 only(assumes that you have at least 2 rows) */
      printf("\n");
      printarow(1, cols, arr1); /* This calls to print out row 1 only */
      printf("\n");
}

void      printarow(int row, int cols, int arr[8][8])

/* this is a function which can be called from anywhere in the program */
/* and can be called as often as you want to */
/* If you need to do the same type of thing many times it saves you */
/* having to write out the same code repeatedly. All you need to */
/* is call the function */

{
      int j;
      printf("row %d is ", row);
      for (j = 0;j < cols;j++)
      {
            printf("%d ", arr[row - 1][j]);
      }

}

Listing 2-11c1.2func.c

请注意,函数中使用的数组名称不必与 main{}中使用的名称相同。在 if 指令中(行> 7 ||列> 8),||表示或。所以在这里,我们说,如果用户指定了超过 7 行或超过 8 列,那么我们打印一个错误并停止程序。在本章的最后,列出了 C 语言中常用的算术和逻辑符号。

创建并测试这个程序。该代码假设您至少有两行。您可以修改代码来多次调用 printarow。

函数可以向调用者返回值。下面的代码演示了这一点:

/* Function which returns an answer  */
/* finds the pupil in one year of the school with the highest marks */

#include <stdio.h>
double getmarks(double pupils[]);

int main()
{
      double pupil;
      /* Array with marks for class is preset in the main part of the program */
      double marks[] = { 10.6, 23.7, 67.9, 93.0, 64.2, 33.8 ,57.5 ,82.2 ,50.7 ,45.7 };

      /* Call function getmarks. The function returns the max marks which is then stored in pupil */
      pupil = getmarks(marks);
      printf("Max mark is  = %f", pupil);
      return 0;
}

double getmarks(double pupils[])
{
      int i;
      double highest;
      highest = 0;
      /* Go through all the pupils in turn and store the highest mark */
      for (i = 0; i < 6; ++i)
      {
            if (highest < pupils[i])
                  highest = pupils[i];

      }
      return highest; /* returns the value in highest to where the function was called */
}

这个函数叫做 getmarks。它返回一个值到它被调用的地方。在实际的程序中,这个函数会在程序的不同点被多次调用。这种技术既高效又让程序更容易理解。

用线串

C 中的字符串就像我们已经看过的字符数组。主要区别在于字符串末尾有一个空字符。这只是为了显示字符串的结束位置,因为我们必须做一些事情,比如比较两个字符串,或者找出字符串的长度。为了找到长度,我们在 string.h 库中为我们编写了一个函数,这个函数需要在末尾空字符。因此,如果我们将一个预置字符串定义为一个特定长度的字符数组,我们需要考虑末尾的空值。因此,如果我们的字符串中有“tokens ”,单词有六个字符,那么我们的字符串数组在其定义中必须有七个字符,以说明末尾的空字符。当我们使用 printf 打印一个字符串时,我们使用%s 来表示它是一个字符串(我们使用%d 来打印一个整数,或者使用%f 来打印一个浮点数)。

清单 2-12 是一个检查字符串长度(strlen)、复制到另一个(strcpy)、连接两个字符串(strcat)、比较两个字符串内容(strcmp)的程序。

两个字符串的连接只是将一个字符串标记到另一个字符串的末尾。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
/* Program to demonstrate string operations strlen, strcpy, strcat, strcmp */

int main() {
      char borrow[7] = { 't', 'o', 'k', 'e', 'n', 's','\0' };
      char string1[32] = "This is string1";
      char string2[16] = "This is string2";
      char string3[16];
      int  len;
      /* Print out the lengths of the strings */

      len = strlen(string1);
      printf("strlen(string1) :  %d\n", len);
      len = strlen(string2);
      printf("strlen(string2) :  %d\n", len);
      len = strlen(string3);
      printf("strlen(string3) :  %d\n", len);

      /* copy string1 into string3 */

      strcpy(string3, string1);
      printf("strcpy( string3, string1) :  %s\n", string3);
      len = strlen(string3);
      printf("strlen(string3) after copy of string1 into string3 :  %d\n", len);

      /* Compare string1 and string3 (these should be the same)*/

      if (strcmp(string1, string3) == 0)
            printf("strings are the same\n");

      /* concatenates string1 and string2 */

      strcat(string1, string2);
      printf("strcat( string1, string2):   %s\n", string1);

      /* total length of string1 after concatenation */

      len = strlen(string1);
      printf("strlen(string1) after cat of string2 onto string1 :  %d\n", len);
      printf("String as predefined quoted chars: %s\n", borrow);

      return 0;
}

Listing 2-12c1.2string.c

在 strlen 中,函数返回字符串的长度。

在 strcpy 中,该函数将命令中的第二个字符串复制到第一个字符串中。

在 strcmp 中,函数比较两个字符串的内容,如果相等则返回 0。

在 strcat 中,函数将第二个字符串标记到第一个字符串的末尾。

本节演示了 c 语言中字符串的使用。这是“结构”的一个扩展定义,如下所示。

结构

到目前为止,所使用的变量只是某一类型的单独命名的变量。另一种类型的变量是结构。这是一个包含独立变量的变量。如果您想象一个包含大学学生详细信息的文件,每个学生的详细信息可能是他们的姓名、学号,也可能是他们最后的考试分数。因此,在纸质文件中,这些文件可能是这样保存的:

id
Name
Percent

所以每个学生的档案里都会有这样一个条目。

下面是一个声明这种结构的程序。然后,它将变量名 s1 和 s2 指定为该类型的定义。然后它给每个结构赋值,然后打印出来。

/* Structure example program */
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>

/* define the structure */
struct Student {
      int id;
      char name[16];
      double percent;
};

int main() {
      /* define two data locations of type “student” */

      struct Student s1, s2;

      /* Assign values to the s1 structure */

      s1.id = 56;
      strcpy(s1.name, "Rob Smith");
      s1.percent = 67.400000;
      /* Print out structure s1 */

      printf("\nid : %d", s1.id);
      printf("\nName : %s", s1.name);
      printf("\nPercent : %lf", s1.percent);

      /* Assign values to the s2 structure */

      s2.id = 73;
      strcpy(s2.name, "Mary Gallagher");
      s2.percent = 93.800000;

      /* Print out structure s1 */

      printf("\nid : %d", s2.id);
      printf("\nName : %s", s2.name);
      printf("\nPercent : %lf", s2.percent);

      return (0);
}

这是可以扩展的,所以我们可以在一个定义中定义一个更大的数字,而不是定义单独的条目(s1 和 s2)。在下面的例子中,我们在数组 year9 中定义了五个项目。然后我们将第一个学生条目称为 year9[0],将第二个学生条目称为 year9[1],依此类推。(列表 2-13 )。

/* Structure example program (extended structure)*/
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>

/* define the structure */

struct Student {
   int id;
   char name[16];
   double percent;
};
int main() {
      int i;
   /* define 5 data locations of type “student” */
         struct Student year9[5];
         for(i=0; i<5; i++)
         {
            /* Assign values to the structure */
            printf("enter student ID\n");
            scanf("%d",&year9[i].id);
            printf("enter student name\n");
            scanf("%s",year9[i].name);
            printf("enter student percent\n");
            scanf("%lf",&year9[i].percent);
        }
        for(i=0; i<5; i++)
        {
            /* Print out structure s1 */

            printf("\nid : %d", year9[i].id);
            printf("\nName : %s", year9[i].name);
            printf("\nPercent : %lf", year9[i].percent);

        }
    return (0);
}

Listing 2-13c1.2struct.c

这种类型的结构定义在设置文件和读写文件时至关重要。你会在处理文件使用的章节中看到更多的结构。

结构广泛用于文件处理。

变量的大小

C 语言中有一个有用的函数,它可以告诉你机器上变量的字节大小。有时,不同的编译器或软件开发工具对于不同的结构有不同的大小。这个函数叫做 sizeof。您向它提供想要知道其大小的变量类型,它以字节数的形式返回答案。

如果不知道结构的大小,也可以提供一个结构作为参数(清单 2-14 )。

/* Program to illustrate the use of the sizeof command */

#include <stdio.h >
#include < limits.h >
#include < math.h >

      int main() {

      int sizeofint;
      unsigned int sizeofunsint;
      float sizeoffloat;
      double sizeofdouble;
      char sizeofchar;

      printf("storage size for int : %zd \n", sizeof(sizeofint));
      printf("storage size for uns int : %zd \n", sizeof(sizeofunsint));
      printf("storage size for float : %zd \n", sizeof(sizeoffloat));
      printf("storage size for double float: %zd \n", sizeof(sizeofdouble));
      printf("storage size for char: %zd \n", sizeof(sizeofchar));

      return(0);

}

Listing 2-14sizeof

这将打印出整型、无符号整型、浮点型和双浮点型的大小,如下所示:

storage size for int : 4
storage size for uns int : 4
storage size for float : 4
storage size for double float: 8
storage size for char: 1

Goto 命令

在某些情况下,您可能想要跳出正常的代码序列,例如,如果您发现代码序列中有错误。在这种情况下,您可以定义一个标签,并从代码序列中跳转到该标签。

Goto 在编程中不常使用,但如果您想快速退出程序,可以使用它(清单 2-15 )。

/* Demonstrate a goto statement */
/* a:, b:, c:,d:,e:, f:is a simulation of a program. We will simulate an error by setting testvalue to 2*/

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
      int i, testvalue;
      int x1,x2,x3,x4,x5,x6;

      printf("Please enter a number:\n ");
      scanf("%d", &testvalue); /* read number in */

      x1 = x2 = x3 = x4 = x5 = x6 = 0;

      a:
            x1 = 1;

      b:
            x2 = 1;
            if(testvalue == 2)
                  goto f;
      c:
            x3 = 1;
      d:
            x4 = 1;
      e:
            x5 = 1;
      f:
            x6 = 1;

      printf("x1 = %d, x2 = %d, x3 = %d, x4 = %d, x5 = %d, x6 = %d,   \n",x1,x2,x3,x4,x5,x6);
}

Listing 2-15c1.2goto.c

这会输出(如果您输入 0、3456 和 2)

Please enter a number:
 0
x1 = 1, x2 = 1, x3 = 1, x4 = 1, x5 = 1, x6 = 1,
Please enter a number:
 3456
x1 = 1, x2 = 1, x3 = 1, x4 = 1, x5 = 1, x6 = 1,
Please enter a number:
 2
x1 = 1, x2 = 1, x3 = 0, x4 = 0, x5 = 0, x6 = 1,

常见的数学和逻辑符号

以下是 C 代码中使用的数学符号及其含义的列表:

=   assign
==  equals
!=  not equal to
<   less than
>   greater than
<= less than or equal to
>=  greater than or equal to

&&  logical AND
||  logical OR
!   logical NOT

文件存取

本节是关于将数据移入和移出文件。文件访问的基本命令有 fopen(打开文件)、fclose(关闭文件)、fread(从已打开的文件中读取数据)和 fwrite(将数据写入已打开的文件)。还有一两个其他的文件命令,我们稍后会遇到。

我们将在这里看看这些技术。

我们使用指令文件fp 在程序的开始声明指针。星号表示变量是一个指针,每当我们在程序中访问这个文件时,我们就使用 fp。我们使用 fopen 命令设置指针的值。

“w”表示我们想要对文件进行写访问。fopen 中对此的可能值如下:

“r”=打开阅读

" w" =创建一个用于写入的文件

" a" =附加到文件

" r+" =读写

" w+" =创建一个可读写的文件

" a+" =打开进行读取和追加

fopen 命令返回一个指针,它存储在 fp 中。代码如下所示。

在这个程序中,我们使用命令 fwrite 来写入文件。

fwrite(&s10, sizeof(s1), 1, fp);

我们将 s10 中的数据写入 fp 指向的文件。当我们将所有数据写入文件后,我们调用

fclose(fp);

这将关闭 fp 指向的文件。

学生记录文件

本章的下一个程序展示了如何将包含不同类型数据的结构写入一个文件。该数据包含学生标识符、姓名和考试成绩。其结构如下所示:

struct student {
       int studentID;
       char name[13];
       int marks;
};

每个学生都有一个这样的结构。第一个程序创建一个包含这些数据的文件。每个学生的结构数据是在程序开始时设置的。

我们从打开文件开始。指令是

fp = fopen("students.bin", "w");

其中 students.bin 是文件名,fp 是文件指针。

我们使用几个 fwrite 调用写入文件。

我们关闭文件,然后重新打开它,以便检查我们写了什么。在我们阅读中,我们有

numread=fread(&s2, sizeof(s2), 1, fp);

其中 numread 是读取的结构数。我们期望一个结构已经被读取,如我们的 fread 中的第三个参数所示。如果是 1,那么我们打印记录。如果不是 1,我们检查错误。通过调用命令 feof(fp ),我们可以检查是否出现了意外的文件结尾。如果是这样,那么我们打印出一条适当的消息。

最后,我们关闭文件(清单 2-16 )。

/* Create the file and write records to it */

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>

/*define the structure for each student’s data */

struct student {

      int studentID;
      char name[13];
      int marks;
};

int main()
{
      int i, numread;
      FILE *fp;
      struct student s1;
      struct student s2;

      /* Preset the data for each student */

      struct student s10 = { 10,"Coster      ",15 };
      struct student s11 = { 11,"Harris      ",20 };
      struct student s12 = { 12,"Frazer      ",25 };
      struct student s13 = { 13,"Kirrane     ",30 };
      struct student s14 = { 14,"Marley      ",35 };
      struct student s15 = { 15,"OBrien      ",40 };
      struct student s16 = { 16,"Brown       ",45 };
      struct student s17 = { 17,"Tomlinson   ",50 };
      struct student s18 = { 18,"Mulcahy     ",55 };
      struct student s19 = { 19,"Coyle       ",60 };
      struct student s20 = { 20,"Baxter      ",65 };
      struct student s21 = { 21,"Weeks       ",70 };
      struct student s22 = { 22,"Owens       ",75 };
      struct student s23 = { 23,"Cannon      ",80 };
      struct student s24 = { 24,"Parker      ",85 };

      /* Open the students file */

      fp = fopen("students.bin", "w");

      /* Write details of each student to file*/
      /* from the structures defined above */

      fwrite(&s10, sizeof(s1), 1, fp);
      fwrite(&s11, sizeof(s1), 1, fp);
      fwrite(&s12, sizeof(s1), 1, fp);
      fwrite(&s13, sizeof(s1), 1, fp);
      fwrite(&s14, sizeof(s1), 1, fp);
      fwrite(&s15, sizeof(s1), 1, fp);
      fwrite(&s16, sizeof(s1), 1, fp);
      fwrite(&s17, sizeof(s1), 1, fp);
      fwrite(&s18, sizeof(s1), 1, fp);
      fwrite(&s19, sizeof(s1), 1, fp);
      fwrite(&s20, sizeof(s1), 1, fp);
      fwrite(&s21, sizeof(s1), 1, fp);
      fwrite(&s22, sizeof(s1), 1, fp);
      fwrite(&s23, sizeof(s1), 1, fp);
      fwrite(&s24, sizeof(s1), 1, fp);

      /* Close the file */

      fclose(fp);

      /* Reopen the file (at the start of the file)*/

      fopen("students.bin", "r");

      /* Read and print out all of the records on the file */

      for (i = 0;i < 15;i++)
      {

            numread = fread(&s2, sizeof(s2), 1, fp);   /* read into structure s2 */

            if (numread == 1)
            {

                 /* reference elements of structure by s2.studentID etc */

                  printf("\nstudentID : %d", s2.studentID);
                  printf("\nName : %s", s2.name);
                  printf("\nmarks : %d", s2.marks);
            }
            else {
                  /* If an error occurred on read then print out message */

                  if (feof(fp))

                        printf("Error reading students.bin : unexpected end of file fp is %p\n", fp);

                  else if (ferror(fp))
                  {
                        perror("Error reading students.bin");
                  }
            }

      }
      /* Close the file */

      fclose(fp);

}

Listing 2-16c1.2filewrite2.c

清单 2-17 从文件中读取并显示数据。我们再次打开文件,这次是只读的(open 调用中的“r”)。

下面的代码说明了这一点。

我们在 fread 中指定,我们希望将数据读入程序的结构中。这里的结构是 s2,在程序的顶部,我们有针对 filewrite 程序的结构定义。在我们对 s2 的定义中,我们将其标识为“结构化学生”类型这定义了类型,就像 int 在程序顶部的定义中定义 numread 的类型一样。

/* fileread */
/* reads from file */
/* reads and prints sequentially */

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>

/*define the structure for each student’s data */

struct student {
      int studentID;
      char name[13];
      int marks;
};

int main()
{
      FILE *fp;

      struct student s2;

      int numread, i;
      /* Open students file */

      fp = fopen("students.bin", "r");
      printf("\nAll students\n");
      for (i = 0;i < 15;i++)
      {

            /* Read each student data from file sequentially */

            fread(&s2, sizeof(s2), 1, fp);

            /* Print student ID, name and Marks for each student */

            printf("\nstudentID : %d", s2.studentID);
            printf("\n Name : %s", s2.name);
            printf("\nmarks : %d", s2.marks);

      }

      fclose(fp);

}

Listing 2-17c1.2fileread3.c

清单 2-18 显示了如何更新文件中的记录。这里我们想更新 ID 为 23 的学生的记录,并给他们的分数加 10。我们浏览文件直到找到正确的 ID。然后我们在他们的结构中加上 10 分。此时,文件指针指向下一条记录,因此我们必须将其向后移动一条记录,然后我们的 fwrite 将覆盖正确的记录。我们使用命令向后移动一条记录

fseek(fp,minusone*sizeof(s2),SEEK_CUR);

该指令的 minusone*sizeof(s2)部分表示向后移动一个记录的长度。

/* fileupdate */
/* reads and prints sequentially */
/* reads, updates and prints specific records */

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
/*define the structure for each student’s data */

struct student {
      int studentID;
      char name[13];
      int marks;
};

int main()
{
      FILE *fp;
      long int minusone = -1;
      struct student s2;

      int numread, i;
      /* Open students file */

      fp = fopen("students.bin", "r");
      printf("\nAll students\n");
      for (i = 0;i < 15;i++)
      {
            /* Read each student data from file sequentially */

            fread(&s2, sizeof(s2), 1, fp);

            /* Print student ID, name and marks for each student */

            printf("\nstudentID : %d", s2.studentID);
            printf("\n Name : %s", s2.name);
            printf("\nmarks : %d", s2.marks);

      }

      fclose(fp);

      /* Re-open the students file */

      fp = fopen("students.bin", "r+");  /* r+ is for update */
      printf("\nStudent ID=23\n");

      for (i = 0;i < 15;i++)
      {
            /* Search the file for student with ID of 23 */

            fread(&s2, sizeof(s2), 1, fp);
            if (s2.studentID == 23)
            {
                  /* Found the student. */
                  /* update their marks */
                  /* Print their updated details */

                  s2.marks = s2.marks + 10;
                  printf("\nName : %s", s2.name);
                  printf("\nmarks : %d", s2.marks);
                  fseek(fp,minusone*sizeof(s2),SEEK_CUR);
                  fwrite(&s2,sizeof(s2),1,fp);
                  break;
            }
      }
      /* Go back to the beginning of the file */

      fseek(fp, sizeof(s2), SEEK_END);
      rewind(fp);
      printf("\updated file\n");

      /* read and display the updated file*/

      for (i = 0;i < 15;i++)
      {
            fread(&s2, sizeof(s2), 1, fp);
            printf("\nName : %s", s2.name);
            printf("\nmarks : %d", s2.marks);

      }

      fclose(fp);

}

Listing 2-18c1.2fileupdate2.c

清单 2-19 显示了我们如何从文件中选择特定的记录或记录数。我们使用 fp = fopen("students.bin "," r ")打开文件;。这里的“r”表示只读。

我们从读取所有文件并打印出所有数据开始。然后我们关闭文件并重新打开它。我们想找到 ID 为 23 的学生。当我们找到它,我们把它打印出来。

我们可以调用 rewind 将文件返回到开头,而不是关闭并重新打开文件。我们用 fseek(fp,sizeof(s2),SEEK _ END);到达文件的末尾,然后倒带(FP);将文件指针移回到开头。

在这一轮档案中,我们想找到所有分数在 63 分以上的学生。我们再次设置了一个 for 循环来遍历文件中的每个结构。如果分数超过 63,我们打印出学生的名字。这次我们不打破 for 循环,因为可能不止一个学生的分数超过 63 分。

然后我们倒回到程序的开始,打印出文件中的前三个学生。这个程序显示了一些不同的选择选项。

/* fileupdate */
/* reads from file */
/* reads and prints sequentially */
/* reads and prints specific records */

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>

/*define the structure for each student’s data */

struct student {
      int studentID;
      char name[13];
      int marks;
};

int main()
{

      FILE *fp;
      long int minusone = -1;
      struct student s2;

      int numread, i;

      /* Open students file */

      fp = fopen("students.bin", "r");
      printf("\nAll students\n");
      for (i = 0;i < 15;i++)
      {
            /* Read each student data from file sequentially */

            fread(&s2, sizeof(s2), 1, fp);

            /* Print student ID, name and marks for each student */

            printf("\nstudentID : %d", s2.studentID);
            printf("\n Name : %s", s2.name);
            printf("\nmarks : %d", s2.marks);

      }

      fclose(fp);

      /* Re-open the students file */

      fp = fopen("students.bin", "r");
      printf("\nStudent ID=23\n");

      for (i = 0;i < 15;i++)
      {

            /* Search the file for student with ID of 23 */

            fread(&s2, sizeof(s2), 1, fp);
            if (s2.studentID == 23)
            {
                  /* Found the student. Print their name and marks */

                  printf("\nName : %s", s2.name);
                  printf("\nmarks : %d", s2.marks);

                  break;
            }
      }
      /* Go back to the beginning of the file */

      fseek(fp, sizeof(s2), SEEK_END);
      rewind(fp);
      printf("\nStudents marks>63\n");

      /* Find all students with marks are above 63 */

      for (i = 0;i < 15;i++)
      {
      fread(&s2, sizeof(s2), 1, fp);
            if (s2.marks > 63)
            {
                  /* Print out name of each student with marks above 63 */
                  printf("\nName : %s", s2.name);

            }
      }
      /* Go back to the beginning of the file */

      rewind(fp);

      /* Read and print out the first 3 students in the file */

      printf("\nFirst 3 students \n");

      numread = fread(&s2, sizeof(s2), 1, fp);
      if (numread == 1)
      {
            printf("\nstudentID : %d", s2.studentID);
            printf("\nName : %s", s2.name);
            printf("\nmarks : %d", s2.marks);
      }
      numread = fread(&s2, sizeof(s2), 1, fp);
      if (numread == 1)
      {

            printf("\nstudentID : %d", s2.studentID);
            printf("\nName : %s", s2.name);
            printf("\nmarks : %d", s2.marks);
      }
      numread = fread(&s2, sizeof(s2), 1, fp);
      if (numread == 1)
      {
            printf("\nstudentID : %d", s2.studentID);
            printf("\nName : %s", s2.name);
            printf("\nmarks : %d", s2.marks);
      }
      /* Close the file */

      fclose(fp);

}

Listing 2-19c1.2fileselect.c

摘要

在阅读完这一章并运行了所示的程序后,你应该对 C 如何帮助你的工作有了很好的理解。以下章节实现了第一章和此处所示的内容。

练习

  1. 以 For 循环为例,在 for 循环程序中,读入一个整数,并在 for 指令中将其用作循环的限制。测试你的修正案,给它和你原来的 for 循环程序一样的编号。

  2. 编写一个程序来扩展数据数组程序,这样就可以输入和存储两个独立的数组。然后打印出第一个数组的第一行和第二个数组的第一行。

  3. 写一个类似于从函数中返回值的程序。在这种情况下,调用函数来查找一组标记的平均值。在程序的主体部分设置一个数组,用 9 个值初始化。调用您的函数来计算这些数字的平均值。你的函数应该返回平均值,这个值存储在程序的主体部分,然后打印出来。

  4. 编写一个程序将另一个学生添加到文件中。不要只是将它添加到 c1.2filewrite2.c 程序的列表中,而是编写一个新程序将另一个学生追加到该文件中。

三、C 语言中的 SQL

本章向读者介绍了从 C 程序中访问和使用数据库语言结构化查询语言(SQL)的方法。将演示如何创建数据库文件。完成后,读者将能够创建数据库表。然后,将展示如何向表中插入数据,如何修改表中的数据,如何从表中删除数据,以及如何打印出表中保存的数据。

SQL 和 SQLite 综述

SQL(结构化查询语言)用于访问数据库。理解这一点最简单的方法是看一个数据库表的例子。下面显示了一个这样的表。这是我们将在本章中使用的表格。

典型的数据库表包含为同一家公司工作的大量人员的信息。一个人的数据包含在一个“行”中这一行包含这个人的 ID、他们的姓、他们的年龄和他们的职位(职业)。

|

编号

|

|

年龄

|

职业

|
| --- | --- | --- | --- |
| One hundred and twenty-three | 琼斯 | Thirty-seven | 会计师 |
| One hundred and twenty-five | 锻工 | forty-two | 小时 |
| One hundred and twenty-eight | 艾伦 | Twenty-eight | 秘书 |
| One hundred and thirty-one | 布拉德利 | Twenty-six | 节目编排者 |
| One hundred and thirty-two | 爱德华兹 | Forty-one | 节目编排者 |
| One hundred and thirty-three | 国王 | Thirty-five | 软件工程师 |
| One hundred and thirty-four | 价格 | Thirty-nine | 硬件工程师 |
| One hundred and thirty-six | 罗伯茨 | fifty-two | 经理 |
| One hundred and thirty-eight | 鼓励 | forty-four | 分析师 |
| One hundred and thirty-nine | 香农河 | Twenty-four | 节目编排者 |
| One hundred and forty-one | 刘易斯 | Twenty-seven | 接待员 |

我们将使用 sqlite3,它是一个关系数据库管理系统。这是在一个 C 库中,作为我们的 SQL 接口。如果您的计算机上没有 sqlite3,可以免费下载。

创建数据库

在 C 程序中使用以下代码创建数据库(创建和运行 C 程序在第二章中介绍):

dbcreate = sqlite3_open("test.db",&db);

在此调用之后,如果数据库成功打开,dbcreate 将包含 0,否则包含非零值。

如果数据库文件已经创建或者已经存在,那么这个命令返回它在“db”中的指针。

这将在当前目录中创建数据库文件 test.db。我们可以对数据库文件和它的一个表使用相同的名称。

我们使用 SQL 来创建表。在前面的例子中,我们将把表称为“Personnel ”,因此这个表的 SQL 命令应该是

CREATE TABLE Personnel (id INT PRIMARY KEY, surname TEXT, age INT, occupation);

在这种情况下,ID 是唯一标识个人的“主键”。因此,举例来说,如果两个人有相同的姓,那么由于他们的主键会不同,这将唯一地标识他们。

我们通过为每个人创建一个单独的“INSERT”语句来为每个人创建一行。我们表中的第一个人可以用下面的语句来定义

INSERT INTO Personnel  VALUES (123, 'Jones', 37, 'Accountant');

其中 123 是 id,“琼斯”是姓氏,“37”是年龄,“会计”是职业。

如果我们想要找到公司中所有程序员的名字,那么我们将使用一个 SELECT SQL 语句,它会说

SELECT surname FROM Personnel WHERE occupation = 'Programmer';

我们可以使用“HAVING”选项选择特定的组。在下面的例子中,我们希望选择年龄大于 25 岁的所有人。这里,命令中的“*”表示“所有人”

SELECT * FROM Personnel GROUP BY age HAVING age > 25

我们可以使用“更新”命令来修改行。在下面的例子中,我们希望将这个人的职业更新为“经理”:

UPDATE Personnel SET  occupation = 'manager' WHERE id = 123;

最后,我们可以通过在“delete”命令中指定要删除的行的 ID 来删除行,如下所示:

DELETE FROM Personnel WHERE id = 136;

我们将使用 sqlite3 标准软件。

本章中的 C 程序使用下载 sqlite3 时提供的基本 SQL 接口例程。

使用的三个主要接口例程是 sqlite3_opensqlite3_execsqlite3_close :

  1. sqlite3_open("test.db ",& db);连接到 test.db 数据库文件。这将返回一个值来表示连接是否成功。

  2. sqlite3_exec(db,sql,callback,0,& err _ msg);执行保存在“sql”参数中的 SQL 命令。“callback”参数可以是 0,也可以是 sqlite3_exec 函数返回时调用的函数名。然后,该函数可以处理 sqlite3_exec 检索到的行。&err_msg 返回任何错误消息。

  3. sqlite3 _ close(db);关闭数据库连接。

    当 sqlite3_open 返回到数据库文件的成功连接时,我们可以在文件中创建一个新表或访问一个现有的表。

  4. sqlite3_errmsg(db)给出一条错误消息。

#include <sqlite3.h>
#include <stdio.h>

这是我们在这里使用的程序需要的两个包含文件。

在程序中,char *sql 是一个命令,它设置了一个指针,指向程序存储命令(创建、选择等)的位置。).

执行完 exec 语句后,该命令的状态保存在 rc 中。然后对此进行检查,如果有错误,可以使用 err_msg 向用户报告。

SQLITE_OK 为零。

如果有错误,程序返回 1,否则返回 0。

看到这些基本的想法后,我们现在可以开始写程序了。

创建表格

如前所述,该程序创建“人员”表。

实际的数据库是文件"test.db"。我们的数据库表被添加到其中。

在清单 3-1 中,要插入的八行中每一行的数据都被编码到程序中。在后面的程序中,我们将允许用户从命令行手动输入数据。

#include <sqlite3.h>
#include <stdio.h>
int main(void)
{
sqlite3 *db;
char *err_msg = 0;

    int rc = sqlite3_open("test.db", &db);/* open the database */

    /* check the status of the database open request */

    if (rc != SQLITE_OK)
        {
        /* database cannot be opened */
        /* report the error */
        fprintf(stderr, "Cannot open database: %s\n",
        sqlite3_errmsg(db));
        sqlite3_close(db);
        return 1;
    }

    /* The database exists so we can create our table and add our 8 rows to it */
    /* The 'create table' and 8 'insert' commands can be */
    /* copied into the *sql pointer together */
    /* Each row contains ID, Name, Age and Occupation */

    char *sql = "DROP TABLE IF EXISTS Personnel;"
    "CREATE TABLE Personnel(Id INT PRIMARY KEY, Name TEXT,      Age INT, Occupation);"
    "INSERT INTO Personnel VALUES(1, 'Brown', 42,      'accountant');"
    "INSERT INTO Personnel VALUES(2, 'Jones', 27,      'programmer');"
    "INSERT INTO Personnel VALUES(3, 'White', 30,      'engineer');"
    "INSERT INTO Personnel VALUES(4, 'Green', 29,      'electrician');"
    "INSERT INTO Personnel VALUES(5, 'Smith', 35,      'manager');"
    "INSERT INTO Personnel VALUES(6, 'Black', 21,      'secretary');"
    "INSERT INTO Personnel VALUES(7, 'Allen', 41,      'cleaner');"
    "INSERT INTO Personnel VALUES(8, 'Stone', 21,      'receptionist');";

    rc = sqlite3_exec(db, sql, 0, 0, &err_msg); /* perform the create table and inserts and check if any errors */

    if (rc != SQLITE_OK )
    {
        /* an error has occurred – report it and close program */
        fprintf(stderr, "SQL error: %s\n", err_msg);
        sqlite3_free(err_msg);
        sqlite3_close(db);
        return 1;
}
sqlite3_close(db); /* close the database connection */
return 0;
}

Listing 3-1csqlinsert_datax.c

您可以使用本章“选择所有行”一节中描述的程序 Csqlselect_allx2b.c 打印整个表格。

如果我们打印出这个表,它会是这样的:

Id = 1
Name = Brown
Age = 42
Occupation = accountant

Id = 2
Name = Jones
Age = 27
Occupation = programmer

Id = 3
Name = White
Age = 30
Occupation = engineer

Id = 4
Name = Green
Age = 29
Occupation = electrician

Id = 5
Name = Smith
Age = 35
Occupation = manager

Id = 6
Name = Black
Age = 21
Occupation = secretary

Id = 7
Name = Allen
Age = 41
Occupation = cleaner

Id = 8
Name = Stone
Age = 21
Occupation = receptionist

我们将在“选择所有行”一节中看到如何编写代码来打印出表的内容

创建了表之后,我们现在将了解如何插入、修改和删除数据。

插入行

现在我们已经有了包含行的数据库表,我们可能想要添加另一行(例如,如果公司刚刚招聘了一名新员工)。

插入预设行

清单 3-2 插入一个预置行。同样,新行的数据被编码到程序中。

#include <sqlite3.h>
#include <stdio.h>
int main(void)
{
sqlite3 *db;
char *err_msg = 0;
int rc = sqlite3_open("test.db", &db); );/* open the database */

/* check the status of the database open request */

if (rc != SQLITE_OK)
{
    fprintf(stderr, "Cannot open database: %s\n",
    sqlite3_errmsg(db));
    sqlite3_close(db);
    return 1;
}

/* Insert our new row with ID=9 name=Wells, age=49 and occupation = teacher */

char *sql = "INSERT INTO Personnel VALUES(9, 'Wells', 49, 'teacher');"; /* set up the insert instruction */

rc = sqlite3_exec(db, sql, 0, 0, &err_msg); /* perform the insert */
if (rc != SQLITE_OK )
{
    /* an error has occurred – report it and close program */
    fprintf(stderr, "SQL error: %s\n", err_msg);
    sqlite3_free(err_msg);
    sqlite3_close(db);
    return 1;
}
sqlite3_close(db); /* close the database connection */
return 0;
}

Listing 3-2csqlinsert_onex.c

插入用户输入的行

清单 3-3 中显示的下一个程序插入了一个用户输入的行。提示用户输入 ID、姓名、年龄和职业。假设用户输入“12”作为 ID,“Pickford”作为姓名,“48”作为年龄,“Welder”作为职业。在程序中,使用四个用户输入的数据项将INSERT INTO Personnel VALUES( 12, 'Pickford', 48, 'Welder' );语句连接在一起。前面的 INSERT 语句中的逗号、括号和引号是单独添加的。

#include <sqlite3.h>
#include <stdio.h>
int main(void)
{
sqlite3 *db;
char *err_msg = 0;

int idin,agein; /store areas for ID and age */
char namein[13]; /store area for name */
char occupin[15]; /store area for occupation */

int rc = sqlite3_open("test.db", &db);/* open the database */
if (rc != SQLITE_OK)
{
    fprintf(stderr, "Cannot open database: %s\n",
    sqlite3_errmsg(db));
    sqlite3_close(db);
    return 1;
}

/* user is asked to enter the fields for this row */
printf("enter  id \n"); /* ID */
scanf("%d", &idin);
printf("enter name id \n"); /* NAME */
scanf("%s", &namein);
printf("enter age \n"); /* AGE */
scanf("%d", &agein);
printf("enter occupation \n"); /* OCCUPATION */
scanf("%s", &occupin);

/* The INSERT command string is set up */
char str1[200] = "INSERT INTO Personnel VALUES( ";
char str2[] = " ); ";
char str3[2];
char str4[6];
    char str5[] = ", ";
    char str6[] = "'";

    sprintf(str4, "%d", idin); /* ID value as a string */
    sprintf(str3, "%d", agein); /* age value as a string */

    /* str1 will be the string containing the complete INSERT command */

    strcat(str1,str4); /* ID */
    strcat(str1,str5); /* comma */
    strcat(str1,str6); /* quote */
    strcat(str1,namein); /* name */
    strcat(str1,str6); /* quote */
    strcat(str1,str5); /* comma */
    strcat(str1,str3); /* age */
    strcat(str1,str5); /* comma */
    strcat(str1,str6); /* quote */
    strcat(str1,occupin); /* occupation */
    strcat(str1,str6); /* quote */
    strcat(str1,str2); /* close bracket and semi-colon */

    printf(str1); /* completed string */

    /* so, for ID=12, name=Pickford, age=48 and occupation = Welder */
    /* our completed string will be :- */
    /* INSERT INTO Personnel VALUES( 12, 'Pickford', 48, 'Welder' ); */

    char *sql = str1; /* move the completed string to *sql */

    rc = sqlite3_exec(db, sql, 0, 0, &err_msg);/* execute the insert */
    if (rc != SQLITE_OK )

    {
        /* an error has occurred – report it and close program */
        fprintf(stderr, "SQL error: %s\n", err_msg);
        sqlite3_free(err_msg);
        sqlite3_close(db);
        return 1;
    }
sqlite3_close(db); /* close the database connection */
return 0;
}

Listing 3-3csqlinsert_onex2.c

如果您像前面一样使用 ID=12,姓名=皮克福德,年龄=48,职业=焊工运行前面的程序,然后使用 ID=12,姓名=罗利,年龄=34,职业=工程师再次运行它,您应该得到“ SQL 错误:唯一约束失败:人员。Id ,因为两行或更多行不能有相同的 Id。

选择行

我们现在有一个程序,如清单 3-4 所示,它将显示一个 ID 由用户指定的单行。与前面用户输入的 INSERT 类似,SELECT 字符串在代码中被拼凑在一起。在本程序中,我们使用“从 id =”的人员中选择姓名、年龄、职业;。所以我们使用 ID 来确定我们想要选择的行。

选择行预置

#include <sqlite3.h>
#include <stdio.h>

int callback(void *, int, char **, char **);

int main(void)
{
sqlite3 *db;
char *err_msg = 0;
int rc = sqlite3_open("test.db", &db); /* open the database */

/* Test the result of the ‘open’ command */
if (rc != SQLITE_OK)
{
        fprintf(stderr, "Cannot open database: %s\n",
        sqlite3_errmsg(db));
        sqlite3_close(db);
        return 1;
}
int idin,idnew;

/* ask the user to enter the ID of the row to be selected */

printf("enter current ID \n");
scanf("%d", &idin);

/* begin the construction of the SELECT string */

char str1[] = "SELECT name, age, occupation FROM Personnel WHERE id = ";

char str4[10];
char str5[] = ";";

printf("idin = %d\n", idin);
sprintf(str4, "%d", idin); /* store the entered id in str4 */

strcat(str1,str4); /* concatenate the ID into str1 above */
strcat(str1,str5); /* semi-colon */

printf("select statement is \n"); /* output string to user */
printf(str1);
printf("\n");

/* so, for ID=12 */
/* our completed string in str1 will be :- */

/* SELECT name, age, occupation FROM Personnel WHERE id = 12; */

char *sql = str1; /* move the completed string to *sql */

/* execute the SELECT */

rc = sqlite3_exec(db, sql, callback, 0, &err_msg);

/* Test the result of the 'sqlite3_exec' command */

if (rc != SQLITE_OK )
{
    /* an error has occurred – report it and close program */
        fprintf(stderr, "Failed to select data\n");
        fprintf(stderr, "SQL error: %s\n", err_msg);
        sqlite3_free(err_msg);
        sqlite3_close(db);
        return 1;
}
sqlite3_close(db);
return 0; /* close the database connection */
}

/* This function is called from sqlite3_exec to print out the data */

int callback(void *NotUsed, int argc, char **argv,
char **azColName)
{
NotUsed = 0;
for (int i = 0; i < argc; i++)
{
    printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
}
printf("\n");

return 0;
}

Listing 3-4csqlselect_onex2b.c

如果选择 id 为 1,输出将是

select statement is
SELECT name, age, occupation FROM Personnel WHERE id = 1;
Name = Brown
Age = 42
Occupation = accountant

选择所有行

下面的程序,如清单 3-5 所示,选择人员表中的所有行。这是通过在命令中说“SELECT *”来指定的。星号表示将选择所有行。

#include <sqlite3.h>
#include <stdio.h>
int callback(void *, int, char **, char **);
int main(void)
{
sqlite3 *db;
char *err_msg = 0;
int rc = sqlite3_open("test.db", &db);/* check the database */

if (rc != SQLITE_OK)
{
    /* an error has occurred – report it and close program */
    fprintf(stderr, "Cannot open database: %s\n",
    sqlite3_errmsg(db));
    sqlite3_close(db);
    return 1;
}

/* 'SELECT *'means select everything */

char *sql = "SELECT * FROM Personnel";
rc = sqlite3_exec(db, sql, callback, 0, &err_msg);/*execute the command */

if (rc != SQLITE_OK )
{
    /* an error has occurred – report it and close program */
    fprintf(stderr, "Failed to select data\n");
    fprintf(stderr, "SQL error: %s\n", err_msg);
    sqlite3_free(err_msg);
    sqlite3_close(db);
    return 1;
}
sqlite3_close(db); /* close the database connection */
return 0;
}

/* This function is called from sqlite3_exec to print out the data */
int callback(void *NotUsed, int argc, char **argv,
char **azColName)
{
NotUsed = 0;
for (int i = 0; i < argc; i++)
{
    printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
}
printf("\n");

return 0;
}

Listing 3-5csqlselect_allx2b.c

输出将是

Id = 1
Name = Brown
Age = 42
Occupation = accountant

Id = 2
Name = Jones
Age = 27
Occupation = programmer

Id = 3
Name = White
Age = 30
Occupation = engineer

Id = 4
Name = Green
Age = 29
Occupation = electrician

Id = 5
Name = Smith
Age = 35
Occupation = manager

Id = 6
Name = Black
Age = 21
Occupation = secretary

Id = 7
Name = Allen
Age = 41
Occupation = cleaner

Id = 8
Name = Stone
Age = 21
Occupation = receptionist

Id = 9
Name = Wells
Age = 50
Occupation = teacher

按年龄选择行

下面的程序选择人员表中人员年龄大于 25 岁的所有行。除了 SELECT 语句的扩展之外,它与前面的程序是相同的代码。这显示在清单 3-6 中。

#include <sqlite3.h>
#include <stdio.h>
int callback(void *, int, char **, char **);
int main(void)
{
sqlite3 *db;
char *err_msg = 0;
int rc = sqlite3_open("test.db", &db);/* check the database */

if (rc != SQLITE_OK)
{
    /* an error has occurred – report it and close program */
    fprintf(stderr, "Cannot open database: %s\n",
    sqlite3_errmsg(db));
    sqlite3_close(db);
    return 1;
}

/* The following SELECT statement uses "GROUP BY age HAVING" to restrict our selection to people with an age greater than 25 */

char *sql = "SELECT * FROM Personnel GROUP BY age HAVING age > 25";
rc = sqlite3_exec(db, sql, callback, 0, &err_msg);/*execute the command */

if (rc != SQLITE_OK )
{
    /* an error has occurred – report it and close program */
    fprintf(stderr, "Failed to select data\n");
    fprintf(stderr, "SQL error: %s\n", err_msg);
    sqlite3_free(err_msg);
    sqlite3_close(db);
    return 1;
}
sqlite3_close(db); /* close the database connection */

return 0;
}

/* This function is called from sqlite3_exec to print out the data */
int callback(void *NotUsed, int argc, char **argv,
char **azColName)
{
NotUsed = 0;
for (int i = 0; i < argc; i++)
{
    printf("%s = %s\n", azColName[i], argv[i] ?       argv[i] : "NULL");
}
printf("\n");

return 0;
}

Listing 3-6csqlselect_allx2c.c

输出将是

Id = 2
Name = Jones
Age = 27
Occupation = programmer

Id = 4
Name = Green
Age = 29
Occupation = electrician

Id = 3
Name = White
Age = 30
Occupation = engineer

Id = 5
Name = Smith
Age = 35
Occupation = manager

Id = 7
Name = Allen
Age = 41
Occupation = cleaner

Id = 1
Name = Brown
Age = 42
Occupation = accountant

Id = 9
Name = Wells
Age = 50
Occupation = teacher

修改行

在下一个程序中,用户可以修改指定的行。该程序询问用户是否想修改年龄、姓名或职业。对于这三个选项中的每一个,都编写了一组单独的代码来设置更新命令字符串。这显示在清单 3-7 中。

#include <sqlite3.h>
#include <stdio.h>
int main(void)
{
int idin,agenew,optin;
char namenew[13];
char occupnew[15];

sqlite3 *db;
char *err_msg = 0;
sqlite3_stmt *res;
int rc = sqlite3_open("test.db", &db); /* check the database */

if (rc != SQLITE_OK)
{
    /* an error has occurred – report it and close program */
    fprintf(stderr, "Cannot open database: %s\n",
    sqlite3_errmsg(db));
    sqlite3_close(db);
    return 1;
}

/* begin to construct the string */

char str3[20];
char str1[80] = "UPDATE Personnel SET ";

char str9[2];
char str15[] = ";";
char str16[] = ", ";
char str17[] = ")";
char str18[] = "\'";

printf("enter id \n");
scanf("%d", &idin);

/* The user can amend either age, name or occupation for the specified id for the row. We ask them which they want to amend */

    printf("Do you want to update age, name or occupation (1,2 or 3)\n");
    scanf("%d", &optin);
    if(optin == 1)
    {
        /* Amend the age */

        printf("enter new age \n");
        scanf("%d", &agenew);

        strcat(str1," age = "); /* add age */
        strcat(str1,str18);
        sprintf(str3, "%d", agenew); /* copy new age to str3*/
        strcat(str1,str3); /* add new age */
        strcat(str1,str18); /* add quote */
     }

     else if(optin == 2)
     {
        /* Amend the name */

        printf("enter new name \n");
        scanf("%s", namenew);
        strcat(str1," name = ");
        strcat(str1,str18);
        strcpy(str3, namenew); /* copy new name to str3*/

        strcat(str1,str3); /* add new name */
        strcat(str1,str18); /* add quote */

     }
     else
     {
        /* Amend the occupation */

        printf("enter new occupation \n");
        scanf("%s", occupnew);
        strcat(str1," Occupation = ");
        strcpy(str3,occupnew); /* copy new occupation to str3*/

        strcat(str1,str18); /* add quote */
        strcat(str1,str3); /* add new occupation */
        strcat(str1,str18); /* add quote */
     }

char str2[] = " WHERE id = ";

char str4[6];

strcat(str1,str2); /* copy 'WHERE id = ' string */
sprintf(str4, "%d", idin); /* copy id into str4 */
printf(str4);
strcat(str1,str4); /* copy id into final string */

printf(str1);

/* so, if we want to update the occupation for ID=12 */
/* our completed string will be :- */
/* UPDATE Personnel SET  Occupation = 'Programmer' WHERE id = 12 */

char *sql = str1;
rc = sqlite3_exec(db, sql, 0, 0, &err_msg); /* perform the insert */
if (rc != SQLITE_OK )
{
   /* an error has occurred – report it and close program */
   fprintf(stderr, "SQL error: %s\n", err_msg);
   sqlite3_free(err_msg);
   sqlite3_close(db);
   return 1;
}
sqlite3_close(db); /* close the database connection */
return 0;

}

Listing 3-7csqlselect_update11.c

删除行

这个程序,如清单 3-8 所示,为用户指定的 ID 删除一行。在运行这个程序之后,您可以运行“选择所有行”程序来检查删除操作是否有效。

#include <sqlite3.h>
#include <stdio.h>
int main(void)
{
sqlite3 *db;
char *err_msg = 0;
sqlite3_stmt *res;
int rc = sqlite3_open("test.db", &db); /* check the database */

if (rc != SQLITE_OK)
{
   /* failure in opening the database file */

   fprintf(stderr, "Cannot open database: %s\n",       sqlite3_errmsg(db));
   sqlite3_close(db);
   return 1;
}
int idin;

/* ask the user to enter the ID if the row to be deleted */

printf("enter  id to be deleted\n");
scanf("%d", &idin);

/* construct the DELETE string */

char str1[200] = "DELETE FROM Personnel WHERE id =  ";
char str2[] = " ); ";
char str3[2];
char str4[6];
char str5[] = ", ";
char str6[] = "'";

sprintf(str4, "%d", idin);

strcat(str1,str4); /* add the entered id to str1 above */
printf(str1); /* print completed string to user */
printf("\n");

/* so, if we want to delete the row for ID=12 */
/* our completed string will be :- */

/* DELETE FROM Personnel WHERE id =  12 */

    char *sql = str1;

rc = sqlite3_exec(db, sql, 0, 0, &err_msg); /* perform the delete */
if (rc != SQLITE_OK )
{
    /* an error has occurred – report it and close program */
    fprintf(stderr, "SQL error: %s\n", err_msg);
    sqlite3_free(err_msg);
    sqlite3_close(db);
    return 1;
}
sqlite3_close(db); /* close the database connection */
return 0;

}

Listing 3-8csqlinsert_deletexx.c

摘要

本章介绍了如何在表中插入、修改和删除行。

本章展示了我们如何使用 C 编程语言来实现 SQL 应用程序。通过这样做,读者将能够将 SQL 数据库访问添加到他们现有的 C 软件中,或者创建能够执行数据表创建以及数据的更新、删除和显示的 SQL 功能的新软件。

练习

  1. 创建一个插入多行的程序。询问用户他们想要输入多少行,然后使用这个数字创建一个 for 循环。在 for 循环中,要求用户为每一行输入数据。

  2. 使用第二章中的读取文件机制,编写一个程序来读取People3.bin文件。然后创建一个数据库表,并将文件中的每条记录作为表中的一行写入。然后打印出表格。

四、Python 中的 SQL

本章向读者介绍了如何从 Python 程序中访问和使用数据库语言“结构化查询语言”。将演示如何创建数据库文件。完成后,读者将能够创建数据库表。然后,将展示如何向表中插入数据,如何修改表中的数据,如何从表中删除数据,以及如何打印出表中保存的数据。

SQL 回顾

SQL(结构化查询语言)用于访问数据库。理解这一点最简单的方法是看一个数据库表的例子。下面显示了一个这样的表。

典型的数据库表包含为同一家公司工作的大量人员的信息。一个人的数据包含在一个“行”中这一行包含这个人的 ID、他们的姓、他们的年龄和他们的职位(职业)。

|

编号

|

|

最初的

|

性别

|

年龄

|

职业

|
| --- | --- | --- | --- | --- | --- |
| One hundred and twenty-three | 琼斯 | A | M | Thirty-seven | 会计师 |
| One hundred and twenty-five | 锻工 | 稀有 | F | forty-two | 小时 |
| One hundred and twenty-eight | 艾伦 | S | M | Twenty-eight | 秘书 |
| One hundred and thirty-one | 布拉德利 | J | F | Twenty-six | 节目编排者 |
| One hundred and thirty-two | 爱德华兹 | P | M | Forty-one | 节目编排者 |
| One hundred and thirty-three | 国王 | B | F | Thirty-five | 软件工程师 |
| One hundred and thirty-four | 价格 | C | M | Thirty-nine | 硬件工程师 |
| One hundred and thirty-six | 罗伯茨 | M | F | fifty-two | 经理 |
| One hundred and thirty-eight | 鼓励 | M | F | forty-four | 分析师 |
| One hundred and thirty-nine | 香农河 | M | F | Twenty-four | 节目编排者 |
| One hundred and forty-one | 刘易斯 | 稀有 | M | Twenty-seven | 接待员 |

我们将使用关系数据库管理系统 sqlite3 。这是在一个 C 库中,作为我们的 SQL 接口。

使用 Python 从命令行创建数据库。

键入“Python”

>>> import sqlite3
>>> conn = sqlite3.connect('bernard3.db')

这将在当前目录中创建数据库文件 bernard3.db。它的指针在“conn”中返回。

我们可以对数据库文件和它的一个表使用相同的名称。所以我们可以称他们为“人事”。

我们使用 SQL 来创建表。我们将把这个表称为“Personnel ”,因此这个表的 SQL 命令应该是

CREATE TABLE Personnel (id INT PRIMARY KEY, surname TEXT, initial TEXT, gender TEXT, age INT, occupation)

在这种情况下,ID 是唯一标识个人的“主键”。例如,如果两个人有相同的姓、首字母和性别,那么由于他们的 ID(主键)不同,这将唯一地标识他们。

我们将为每个人创建一行,为每个人创建一个单独的“INSERT”语句。我们表中的第一个人可以用下面的语句来定义

INSERT INTO Personnel  VALUES (123, 'Jones', 'A', 'M', 37, 'Accountant')

其中 123 是 id,“琼斯”是姓氏,“A”是首字母,“M”是性别,“37”是年龄,“会计”是职业。

如果我们想要公司里所有程序员的名字,那么一个 SQL 语句会说

SELECT surname FROM Personnel WHERE occupation = 'Programmer'

我们可以使用“HAVING”选项来选择一个特定的组,在本例中是年龄大于 25 岁的人。这里,命令中的“*”表示“所有人”

SELECT * FROM Personnel GROUP BY age HAVING age > 25

我们可以使用“ORDER BY”选项选择特定的组。

以下示例从 Personnel 表中选择所有行,并按年龄降序排列:

SELECT * FROM Personnel ORDER BY age DESC

我们可以使用“更新”命令来修改行。id 为 123 的人的职业更改为“经理”,如下图所示:

UPDATE Personnel SET  occupation = 'manager' WHERE id = 123

最后,我们可以使用“删除”命令删除一行,如下所示:

DELETE FROM Personnel WHERE id = 136;

我们将使用可免费下载的 sqlite3 标准软件。

Python 程序使用 import sqlite3,它使程序能够访问 sqlite3 库例程。

sqlite3.connect 打开数据库连接并返回连接对象。因此,如果我们有 conn = sqlite3 . connect(' personal . db '),我们可以使用 conn 来访问这些例程。在这里,我们使用 cur = conn.cursor()来设置 cur,然后 cur 将访问如下所示的 execute 命令:

cur.execute('CREATE TABLE Personnel (id INTEGER PRIMARY KEY, name TEXT, initial TEXT, gender TEXT, age INTEGER, occup TEXT)')

conn.close()关闭数据库连接。

看到这些基本的想法后,我们现在可以开始写程序了。

创建表格

如前所述,该程序创建“人员”表。

实际的数据库是文件Personnel.db。我们的数据库表被添加到其中。

清单 4-1 演示了这一点。

import sqlite3

conn = sqlite3.connect('Personnel.db') # open connection to database file Personnel.db
cur = conn.cursor() #open connection to 'cursor' which facilitates SQL
print ("Opened database successfully")

cur.execute('DROP TABLE IF EXISTS Personnel') # delete the table if it already exists
cur.execute('CREATE TABLE Personnel (id INTEGER PRIMARY KEY, name TEXT, initial TEXT, gender TEXT, age INTEGER, occup TEXT)') #create the table, specifying the items in each row

conn.close() # close the database connection

Listing 4-1pycretab.py

输出是

Opened database successfully

插入行的机制

为了插入一行(如前所述),我们设置了“INSERT INTO”命令。

在“INSERT INTO”命令中,当我们执行该命令时,我们有(?, ?, ?, ?, ?, ?)在命令的值部分之后。在此之后的括号部分,我们有将被替换到问号位置的值。因此

'INSERT INTO Personnel (id, name, initial, gender, age, occup) VALUES (?, ?, ?, ?, ?, ?)',
    (1, 'Jones', 'A', 'M', 23, 'Accountant'))

会产生

'INSERT INTO Personnel (id, name, initial, gender, age, occup) VALUES (1, 'Jones', 'A', 'M', 23, 'Accountant'))

当要求用户输入要插入、更新或删除的数据时,这是一种有用的机制,此时值后面括号中的值就是用户输入的值。

前面的方法将数据“预设”到插入命令字符串中。以后,我们将有程序允许用户在程序运行时将 ID、姓名、首字母、性别、年龄和职业插入到程序中。

创建一个表格并插入两个预设行

该程序创建表格并插入两个预设行。然后,它从表中选择所有的行,这样我们就可以看到插入的行。我们使用命令‘DROP TABLE IF EXISTS Personnel’来确保如果我们试图创建的表已经存在,那么它将被删除。否则,我们的程序将失败,并显示错误“sqlite3”。OperationalError:表人员已经存在。如清单 4-2 所示。

import sqlite3

conn = sqlite3.connect('Personnel.db') # open connection to database file Personnel.db
cur = conn.cursor()#open connection to 'cursor' which facilitates SQL

print ("Opened database successfully")

cur.execute('DROP TABLE IF EXISTS Personnel') # delete the table if it already exists

cur.execute('CREATE TABLE Personnel (id INTEGER PRIMARY KEY, name TEXT, initial TEXT, gender TEXT, age INTEGER, occup TEXT)') #create the table, specifying the items in each row
# Now Insert two rows into the table

cur.execute('INSERT INTO Personnel (id, name, initial, gender, age, occup) VALUES (?, ?, ?, ?, ?, ?)',
    (1, 'Jones', 'A', 'M', 23, 'Accountant'))
cur.execute('INSERT INTO Personnel (id, name, initial, gender, age, occup) VALUES (?, ?, ?, ?, ?, ?)',
    (2, 'Smith', 'J', 'M', 47, 'Salesman'))

print('Personnel:')
# Select everything contained in the table
cur.execute('SELECT id, name, initial, gender, age, occup FROM Personnel')
for row in cur:
     print(row) # print each row contained in the table

conn.commit() #commit these transaction so they can be seen by other programs

conn.close() #close the database connection

Listing 4-2pycretabins2.py

输出是

Opened database successfully
Personnel:
(1, 'Jones', 'A', 'M', 23, 'Accountant')
(2, 'Smith', 'J', 'M', 47, 'Salesman')

插入六个预设行

这个程序,如清单 4-3 所示,插入六个预置行。如果您在前面的示例之后运行这个程序,那么这六行应该被添加到表中。请注意,我们没有在下面的程序中创建表,因为这将删除前面插入的两行。你只需在程序中插入六条单独的 cur . execute(' INSERT INTO personal ')指令。

import sqlite3

conn = sqlite3.connect('Personnel.db') # open connection to database file Personnel.db
cur = conn.cursor()#open connection to 'cursor' which facilitates SQL

print ("Opened database successfully")

# Now Insert six rows into the table

cur.execute('INSERT INTO Personnel (id, name, initial, gender, age, occup) VALUES (?, ?, ?, ?, ?, ?)',
    (11, 'Jones', 'A', 'M', 23, 'Accountant'))
cur.execute('INSERT INTO Personnel (id, name, initial, gender, age, occup) VALUES (?, ?, ?, ?, ?, ?)',
    (12, 'Smith', 'J', 'M', 47, 'Salesman'))
cur.execute('INSERT INTO Personnel (id, name, initial, gender, age, occup) VALUES (?, ?, ?, ?, ?, ?)',
    (13, 'Zeiss', 'H', 'F', 38, 'Architect'))
cur.execute('INSERT INTO Personnel (id, name, initial, gender, age, occup) VALUES (?, ?, ?, ?, ?, ?)',
    (14, 'Blaine', 'S', 'F', 28, 'SE'))
cur.execute('INSERT INTO Personnel (id, name, initial, gender, age, occup) VALUES (?, ?, ?, ?, ?, ?)',
    (15, 'Postlethwaite', 'D', 'M', 63, 'Advisor'))
cur.execute('INSERT INTO Personnel (id, name, initial, gender, age, occup) VALUES (?, ?, ?, ?, ?, ?)',
    (16, 'Junkers', 'A', 'M', 59, 'Designer'))

print('Personnel:')
cur.execute('SELECT id, name, initial, gender, age, occup FROM Personnel') # Select everything contained in the table

for row in cur:
     print(row) # print each row contained in the table

conn.commit()#commit these transaction so they can be seen by other programs
conn.close() #close the database connection

Listing 4-3pyins6.py

输出是

Opened database successfully

People:
(1, 'Jones', 'A', 'M', 23, 'Accountant')
(2, 'Smith', 'J', 'M', 47, 'Salesman')
(11, 'Jones', 'A', 'M', 23, 'Accountant')
(12, 'Smith', 'J', 'M', 47, 'Salesman')
(13, 'Zeiss', 'H', 'F', 38, 'Architect')
(14, 'Blaine', 'S', 'F', 28, 'SE')
(15, 'Postlethwaite', 'D', 'M', 63, 'Advisor')
(16, 'Junkers', 'A', 'M', 59, 'Designer')

插入用户指定的行

现在我们已经有了包含行的数据库表,我们可能想要添加另一行(例如,如果公司刚刚招聘了一名新员工)。

我们现在有一个程序,它将插入一个单独的行,其 ID 和其他字段由用户指定。程序要求用户依次插入每个字段。如清单 4-4 所示。

#!/usr/bin/python

import sqlite3

conn = sqlite3.connect('Personnel.db') # open connection to database file Personnel.db
cur = conn.cursor()#open connection to 'cursor' which facilitates SQL
print ("Opened database successfully")
# User is asked to enter the name, ID, initial, gender, age and occupation

while True:
    namein = input('Enter an name, or quit: ') # age stored in namein

    if(namein == 'quit'): break  #exit the while loop

    idin = input('Enter ID: ') # id stored in 'idin'
    initial = input('Enter initial: ') # initial stored in 'initial'
    gender = input('Enter gender: ') # gender stored in 'gender'
    agein = input('Enter age: ') # age stored in 'agein'
    occup = input('Enter occupation: ') # occupation stored in 'occup'

# Now Insert row into the table using the values entered

    cur.execute('INSERT INTO Personnel (id, name, initial, gender, age, occup) VALUES (?, ?, ?, ?, ?, ?)',(idin, namein, initial, gender, agein, occup))
    break

print('Personnel:')
# Select everything contained in the table

cur.execute('SELECT id, name, initial, gender, age, occup FROM Personnel')
for row in cur:
    print(row) # print each row contained in the table

conn.commit()#commit these transaction so they can be seen by other programs

conn.close()#close the database connection

Listing 4-4pyuserins1.py

如果我们键入以下内容

Enter an name, or quit: Robinson
Enter ID: 21
Enter initial: C
Enter gender: F
Enter age: 31
Enter occupation: Engineer

我们得到

Personnel:
(1, 'Jones', 'A', 'M', 23, 'Accountant')
(2, 'Smith', 'J', 'M', 47, 'Salesman')
(11, 'Jones', 'A', 'M', 23, 'Accountant')
(12, 'Smith', 'J', 'M', 47, 'Salesman')
(13, 'Zeiss', 'H', 'F', 38, 'Architect')
(14, 'Blaine', 'S', 'F', 28, 'SE')
(15, 'Postlethwaite', 'D', 'M', 63, 'Advisor')
(16, 'Junkers', 'A', 'M', 59, 'Designer')
(21, 'Robinson', 'C', 'F', 31, 'Engineer')

更新一行

更新行,预设

在这个程序中,如清单 4-5 所示,我们更新表中的一行,例如,如果我们想改变他们的年龄或职位。要更新的值被编码到程序中。我们使用 UPDATE 命令并说 SET 来表示我们希望改变行中的哪一项。使用 ID 来指定我们希望更改的行是最安全的。在这里的例子中,我们使用了 name 来指定行,但是,正如您可能注意到的,因为表中有两个人同名,所以它更新了这两行。

import sqlite3

conn = sqlite3.connect('Personnel.db') # open connection to database file Personnel.db

cur = conn.cursor()#open connection to 'cursor' which facilitates SQL
# Now update the row in the table
# we want to set the age for the person named Smith to be 24

try:
                cur.execute("UPDATE Personnel SET age = 24 WHERE name = 'Smith'")
 except Error as e:
                print(e)
# Select everything contained in the table

cur.execute("SELECT * FROM Personnel")
cur.execute('SELECT id, name, initial, gender, age, occup FROM Personnel')
for row in cur:
     print(row)

conn.commit()#commit these transaction so they can be seen by other programs
cur.close()

Listing 4-5pyup1.py

输出是

(1, 'Jones', 'A', 'M', 23, 'Accountant')
(2, 'Smith', 'J', 'M', 24, 'Salesman')
(11, 'Jones', 'A', 'M', 23, 'Accountant')
(12, 'Smith', 'J', 'M', 24, 'Salesman')
(13, 'Zeiss', 'H', 'F', 38, 'Architect')
(14, 'Blaine', 'S', 'F', 28, 'SE')
(15, 'Postlethwaite', 'D', 'M', 63, 'Advisor')
(16, 'Junkers', 'A', 'M', 59, 'Designer')
(21, 'Robinson', 'C', 'F', 31, 'Engineer')

注意,在这里,我们有两个名为 Smith 的人,所以更新已经更改了这两行。这是你需要小心的事情。在这里,最安全的方法是使用 ID 而不是名称。

按用户更新行

在这个程序中,如清单 4-6 所示,我们更新了表中的一行。要更新的值由用户输入以更新年龄。

该程序使用 conn.total_changes 函数,该函数返回自连接以来对表的总更改。因此,如果只进行了一次更改(插入、更新或删除),并且函数 conn.total_changes 返回零,那么我们知道尝试的更改一定失败了。

import sqlite3

conn = sqlite3.connect('Personnel.db') # open connection to database file Personnel.db
cur = conn.cursor()#open connection to 'cursor' which facilitates SQL
# we want to set the age for the person whose name is entered into 'namein' to be the age which is entered into 'agein'

#the following is a while loop which can only be exited by the user entering "quit".
while True:
    namein = input('Enter an name, or quit: ')
    if(namein == 'quit'): break
    print(namein)
    agein = input('Enter age: ')

    # Using a while loop
    tot0 = 0
# Now update the row in the table

    try:

        cur.execute("UPDATE Personnel SET age = ? WHERE name = ?", (agein,namein,))

    except:
        print('Error in Update')

    #conn.total_changes returns total changes since connection
    # by setting tot0 to 0 before this update then only this
    # update is checked
    tot = conn.total_changes
    print(tot)
    if tot == tot0:
        print('Table not updated')
    else
    # Select everything contained in the table

       cur.execute('SELECT id, name, initial, gender, age, occup FROM Personnel')
       for row in cur:
              print(row)

conn.commit()#commit these transaction so they can be seen by other programs

cur.close()#close the database connection

Listing 4-6pyuserup1.py

输出是(假设我们输入勇克士手表作为姓名,38 作为年龄)

(1, 'Jones', 'A', 'M', 23, 'Accountant')
(2, 'Smith', 'J', 'M', 24, 'Salesman')
(11, 'Jones', 'A', 'M', 23, 'Accountant')
(12, 'Smith', 'J', 'M', 24, 'Salesman')
(13, 'Zeiss', 'H', 'F', 38, 'Architect')
(14, 'Blaine', 'S', 'F', 44, 'SE')
(15, 'Postlethwaite', 'D', 'M', 63, 'Advisor')
(16, 'Junkers', 'A', 'M', 38, 'Designer')
(21, 'Robinson', 'C', 'F', 31, 'Engineer')
Enter a name, or quit:

插入和更新行

我们可以在同一个程序中进行插入和更新。在这种情况下,插入和更新都是在代码中预设的。这显示在清单 4-7 中。

import sqlite3

conn = sqlite3.connect('Personnel.db') # open connection to database file Personnel.db

cur = conn.cursor()#open connection to 'cursor' which facilitates SQL
#insert the row, specifying the items in the row
cur.execute('INSERT INTO Personnel (id, name, initial, gender, age, occup) VALUES (?, ?, ?, ?, ?, ?)',
    (25, 'Van der Kirchoff', 'I', 'M', 34, 'plumber'))
#update a different row, specifying the changed item in the row

    cur.execute("UPDATE Personnel SET age = 28 WHERE name = 'Smith'")
# Select everything contained in the table

    cur.execute("SELECT * FROM Personnel ")
    # print each row contained in the table

    cur.execute('SELECT id, name, initial, gender, age, occup FROM Personnel')
   for row in cur:
     print(row)

conn.commit()#commit these transaction so they can be seen by other programs

cur.close()#close the database connection

Listing 4-7pyinsup.py

输出是

(1, 'Jones', 'A', 'M', 23, 'Accountant')
(2, 'Smith', 'J', 'M', 28, 'Salesman')
(11, 'Jones', 'A', 'M', 23, 'Accountant')
(12, 'Smith', 'J', 'M', 28, 'Salesman')
(13, 'Zeiss', 'H', 'F', 38, 'Architect')
(14, 'Blaine', 'S', 'F', 28, 'SE')
(15, 'Postlethwaite', 'D', 'M', 63, 'Advisor')
(16, 'Junkers', 'A', 'M', 37, 'Designer')
(21, 'Robinson', 'C', 'F', 31, 'Engineer')
(25, 'Van der Kirchoff', 'I', 'M', 34, 'plumber')

选择一行

在这个程序中,我们从表中选择一行。选择中使用的值被编码到程序中。我们使用 SELECT 命令和 WHERE 指令来指定我们感兴趣的行。按年龄选择的命令是

SELECT * FROM Personnel  WHERE age = 28

该程序如清单 4-8 所示。

import sqlite3

conn = sqlite3.connect('Personnel.db') # open connection to database file Personnel.db
cur = conn.cursor()#open connection to 'cursor' which facilitates SQL

# Select one row contained in the table where the age is 28
# If we did not have LIMIT 1 then every row which had an age of 28 would be displayed

cur.execute("SELECT * FROM Personnel  WHERE age = 28 LIMIT 1")
#print the row selected

for row in cur:
     print(row)

conn.commit()#commit these transaction so they can be seen by other programs

cur.close()#close the database connection

Listing 4-8pysel1.py

输出是

(2, 'Smith', 'J', 'M', 28, 'Salesman')

选择用户输入的行

在这个程序中,如清单 4-9 所示,我们从表中选择一行。要在选择中使用的值由用户输入。用户想要找到被命名的人的年龄。找到时会显示此信息。

import sqlite3

conn = sqlite3.connect('Personnel.db') # open connection to database file Personnel.db
cur = conn.cursor()#open connection to 'cursor' which facilitates SQL

while True:
    namein = input('Enter an name, or quit: ')
    if(namein == 'quit'): break
    print(namein)

    cur.execute('SELECT age FROM Personnel WHERE name = ? LIMIT 1', (namein, ))

    (age, ) = cur.fetchone()
    print(age)
    break
conn.commit()#commit these transaction so they can be seen by other programs

cur.close()#close the database connection

Listing 4-9

pyusersel1.py

输出是(如果我们输入名称“Zeiss”)

Enter an name, or quit: Zeiss
Zeiss
38

按年龄降序选择

按年龄选择表中的所有行,并按年龄降序排序。这是在 SQL select 语句中完成的

SELECT * FROM Personnel ORDER BY age DESC

这将按年龄降序排列整个表。清单 4-10 显示了代码。

import sqlite3

conn = sqlite3.connect('Personnel.db') # open connection to database file Personnel.db
cur = conn.cursor()#open connection to 'cursor' which facilitates SQL

cur.execute("SELECT * FROM Personnel ORDER BY age DESC")
for row in cur:
     print(row)

conn.commit()#commit these transaction so they can be seen by other programs

cur.close()#close the database connection

Listing 4-10pyselorder.py

输出是

(15, 'Postlethwaite', 'D', 'M', 63, 'Advisor')
(13, 'Zeiss', 'H', 'F', 38, 'Architect')
(16, 'Junkers', 'A', 'M', 37, 'Designer')
(25, 'Van der Kirchoff', 'I', 'M', 34, 'plumber')
(21, 'Robinson', 'C', 'F', 31, 'Engineer')
(14, 'Blaine', 'S', 'F', 28, 'SE')
(2, 'Smith', 'J', 'M', 24, 'Salesman')
(12, 'Smith', 'J', 'M', 24, 'Salesman')
(1, 'Jones', 'A', 'M', 23, 'Accountant')
(11, 'Jones', 'A', 'M', 23, 'Accountant')

用户输入的按年龄选择

这个程序,如清单 4-11 所示,从表中选择年龄大于用户指定值的人。这是通过使用 SELECT 命令中的“HAVING”来完成的

("SELECT * FROM Personnel GROUP BY age HAVING age > ?",(ageins,))

其中“年龄”是用户指定的年龄。

import sqlite3

conn = sqlite3.connect('Personnel.db') # open connection to database file Personnel.db
cur = conn.cursor()#open connection to 'cursor' which facilitates SQL

ageins = input('Enter age: ')

print(ageins)

while True:

    # Using a while loop

    cur.execute("SELECT * FROM Personnel GROUP BY age HAVING age > ?",(ageins,))

        for row in cur:
         print(row)
    break

conn.commit()#commit these transaction so they can be seen by other programs

cur.close()#close the database connection

Listing 4-11pyusersel1hav.py (user inputs age)

如果输入 24,则输出为

(14, 'Blaine', 'S', 'F', 28, 'SE')
(21, 'Robinson', 'C', 'F', 31, 'Engineer')
(25, 'Van der Kirchoff', 'I', 'M', 34, 'plumber')
(16, 'Junkers', 'A', 'M', 37, 'Designer')
(13, 'Zeiss', 'H', 'F', 38, 'Architect')
(15, 'Postlethwaite', 'D', 'M', 63, 'Advisor')

删除一行

这个程序,如清单 4-12 所示,从表中删除一行。用户输入要删除其行的人的姓名。在“删除一行”程序之后,您可以运行下一节中的“读取一个表”程序来检查它是否工作。

import sqlite3

conn = sqlite3.connect('Personnel.db') # open connection to database file Personnel.db
cur = conn.cursor()#open connection to 'cursor' which facilitates SQL
print ("Opened database successfully")

namein = input('Enter an name, or quit: ')

cur.execute('DELETE FROM Personnel WHERE name = ?',(namein,))

print('Personnel:')
cur.execute('SELECT id, name, initial, gender, age, occup FROM Personnel')
for row in cur:
     print(row)

conn.commit()#commit these transaction so they can be seen by other programs

conn.close()#close the database connection

user enters Blaine

Listing 4-12pydel1.py

输出是

Personnel:
(1, 'Jones', 'A', 'M', 23, 'Accountant')
(2, 'Smith', 'J', 'M', 24, 'Salesman')
(11, 'Jones', 'A', 'M', 23, 'Accountant')
(12, 'Smith', 'J', 'M', 24, 'Salesman')
(13, 'Zeiss', 'H', 'F', 38, 'Architect')
(15, 'Postlethwaite', 'D', 'M', 63, 'Advisor')
(16, 'Junkers', 'A', 'M', 38, 'Designer')
(21, 'Robinson', 'C', 'F', 31, 'Engineer')
 (25, 'Van der Kirchoff', 'I', 'M', 34, 'plumber')

阅读表格

这个程序,如清单 4-13 所示,读取并打印出表格中的所有行。这样做的代码行可以在本章的任何其他程序中使用。因此,如果您做了任何修改,或者插入或删除了任何行,用户可以检查更改是否有效。

import sqlite3

conn = sqlite3.connect('Personnel.db') # open connection to database file Personnel.db cur = conn.cursor()#open connection to 'cursor' which facilitates SQL
print ("Opened database successfully")
cur = conn.cursor()#open connection to 'cursor' which facilitates SQL

print(' Personnel:')
cur.execute('SELECT id, name, initial, gender, age, occup FROM Personnel')
for row in cur:
     print(row)

conn.commit()#commit these transaction so they can be seen by other programs
conn.close()#close the database connection

Listing 4-13pyreadtab.py

输出是

Opened database successfully
People:
(1, 'Jones', 'A', 'M', 23, 'Accountant')
(2, 'Smith', 'J', 'M', 24, 'Salesman')
(11, 'Jones', 'A', 'M', 23, 'Accountant')
(12, 'Smith', 'J', 'M', 24, 'Salesman')
(13, 'Zeiss', 'H', 'F', 38, 'Architect')
(15, 'Postlethwaite', 'D', 'M', 63, 'Advisor')
(16, 'Junkers', 'A', 'M', 37, 'Designer')
(21, 'Robinson', 'C', 'F', 31, 'Engineer')
(25, 'Van der Kirchoff', 'I', 'M', 34, 'plumber')

摘要

本章演示了如何使用 Python 编程语言创建 SQL 数据库表,然后在表中插入、修改和删除行。它还展示了如何以不同的特定顺序显示表中的数据。这将使用户能够修改他们现有的程序以包含 SQL 访问,或者为 SQL 应用程序编写新的 Python 程序。

练习

  1. 创建两个表,其 SQL 等价如下:

    创建人员表(id 整数、姓名文本、姓名首字母、性别文本、年龄整数、职业文本)

    创建表格供应(id 整数、名称文本、地址文本、类型文本)

    然后在第一个表格中插入六行,在第二个表格中插入四行。第二个表是公司列表。公司名称在“coname”中供应,他们供应的货物类型在“type”中。

  2. 修改 insert 程序, pyuserins1.py ,这样就可以在表中插入任意多的行。

五、嵌入式 Python

自 20 世纪 70 年代早期以来,C 编程语言就一直在运行,并且从那时起一直是计算机软件开发的核心。Python 语言较新,可以执行一些 C 语言不能执行的功能。因此,能够编写一个 C 程序并在其中嵌入一些 Python 代码是很有用的。这就是本章将要阐述的内容。

我们将看看可以合并到 C 程序中的以下两个级别的 Python 代码:

  • 调用一个简单的 Python 字符串。

  • 调用一个 Python 程序。

为了将这两个级别嵌入到我们的 C 程序中,我们必须初始化 Python 解释器。主函数调用是 Py_Initialize()。在 Python 序列的最后,我们调用 Py _ Finalize();。

要调用一个简单的字符串,我们使用 PyRun _ SimpleString。

为了运行一个 Python 程序,我们使用PyRun_SimpleFile.

Python 有 matplotlib 和 numpy,可以嵌入 C 程序。这些将在程序使用它们的章节中介绍。

这些列表将被标记为“列表 5-1 ”等。,关联的嵌入式 Python 程序将用扩展名“b”标记。所以在这里,它的标签应该是“清单 5-1b”。

基本机制

清单 5-1 展示了前面描述的简单字符串选项。Python 只是打印“嵌入的 Python 字符串”。

#include <stdio.h>
#include "include/Python.h"
int main()
{
     Py_Initialize();
     PyRun_SimpleString("print('Embedded Python string')");
     Py_Finalize();
     return 0;
}

Listing 5-1cpyth1.c

这个程序打印

嵌入的 Python 字符串

清单 5-2 展示了第二个嵌入选项。这里,我们调用 Python 程序 pyemb7.py。我们创建一个名为char filename[]的变量,它将保存要调用的文件名。我们定义变量 fp,它是文件指针。

#define PY_SSIZE_T_CLEAN
#include <stdio.h>
#include <conio.h>
#include "include/Python.h"

int main()
{
     char filename[] = "pyemb7.py"; /* store the python file name */
     FILE* fp; /* file pointer */

     Py_Initialize();

     fp = _Py_fopen(filename, "r"); /* store file pointer in fp */
     PyRun_SimpleFile(fp, filename);/* call the python program */

     Py_Finalize(); /* end the python link */
     return 0;
}

Listing 5-2cpyth8.c

下面的清单 5-2b 是一个简单的 Python 程序,它被称为。

print('Embedded Python program here')
print('Hello to C program')

Listing 5-2bpyemb7.py

这个程序打印

Embedded Python program here
Hello to C program

我们现在可以进展到更现实的 Python 嵌入。

画一条 2D 线

接下来的 C 程序基本上和前面的一样,只是调用了不同的 Python 程序。清单 5-3 演示了这一点。

#define PY_SSIZE_T_CLEAN
#include <stdio.h>
#include <conio.h>
#include "include/Python.h"

int main()
{
     char filename[] = "plot6a.py";
     FILE* fp;

     Py_Initialize();

     fp = _Py_fopen(filename, "r");
     PyRun_SimpleFile(fp, filename);

     Py_Finalize();
     return 0;
}

Listing 5-3cpyth17a.c

这个 Python 程序(清单 5-3b )演示了 numpy 和 matplotlib 的使用。Numpy 是一组数值化的程序,matplotlib 与绘制图形有关。Python 程序绘制直线 y = x + 3。我们使用 numpy 函数 np.arange(0,10)设置 x 值。这会创建 0 到 10 之间的等间距 x 值。我们使用 y = x + 3 来计算每个 x 值的 y 值。

然后我们调用 matplotlib 函数 plt.plot(x,y)来绘制图形。

import numpy as np
from matplotlib import pyplot as plt

x = np.arange(0,10) #return evenly spaced values between 0 and 10
y = x + 3 # formula to calculate y values for the x values given in the previous instruction
plt.title("Embedded ")  #title of graph
plt.xlabel("x axis") #x axis label
plt.ylabel("y axis") #y axis label
plt.plot(x,y) #plot the graph
plt.show()

Listing 5-3bplot6a.py

这产生了如图 5-1 所示的图形。

img/515548_1_En_5_Fig1_HTML.jpg

图 5-1

y = x + 3 的基本直线

我们现在可以在图上画两条线了。

画两条 2D 线

C 和 Python 的这种结合展示了 Python 中 matplotlib 的一些灵活性。它在同一个图形上绘制两条曲线。清单 5-4 演示了这一点。

-

Listing 5-4bmp2aa.py

#define PY_SSIZE_T_CLEAN
#include <stdio.h>
#include <conio.h>
#include "include/Python.h"

int main()
{
     char filename[] = "mp2aa.py";
     FILE* fp;

     Py_Initialize();

     fp = _Py_fopen(filename, "r");
     PyRun_SimpleFile(fp, filename);

     Py_Finalize();
     return 0;
}

Listing 5-4cpyth29.c

这个程序(清单 5-4b )绘制了两个图形。一张图显示了女性考试分数的分布情况(百分比值),另一张图显示了男性考试分数的分布情况。

我们使用 list(range(0,100,10)) 函数为两个图形创建一组 x 值(标记)。

import matplotlib.pyplot as plt
# x values:
marks = list(range(0,100,10)) #marks (x values) in range 0 to 100 in units of 10
# y values:
male = [4, 7, 9, 17, 22, 25, 28, 18, 6, 2] # number of males within each range
female = [2, 5, 8, 13, 28, 25, 23, 20, 18, 12] # number of females within each range

# x axis label and y axis label
plt.xlabel('marks')
plt.ylabel('number of students')

#title of graph
plt.title('Comparison of male / female examination scores')
#plot points and adjoining lines for both male and female
#show a key to which line is male and which is female
plt.plot(marks, female, label="female")
plt.plot(marks, female, "ob")  # ob means plot a circle character which is blue
plt.plot(marks, male, label="male")
plt.plot(marks, male, "or") # or means plot a circle character which is red

plt.legend()
plt.show()

该程序绘制出如图 5-2 所示的曲线。

img/515548_1_En_5_Fig2_HTML.jpg

图 5-2

男女考试成绩对比

这两个图表显示了男女学生的类似分布。这种一般形状称为“正态分布”

Matplotlib 还可以绘制标准三角曲线,如下所示。

绘制三角曲线

清单 5-5 中的下一个组合显示了标准的 matplotlib tan(x)函数。

-

Listing 5-5bmp5ae.py

#define PY_SSIZE_T_CLEAN
#include <stdio.h>
#include <conio.h>
#include "include/Python.h"

int main()
{
     char filename[] = "mp5ae.py";
     FILE* fp;

     Py_Initialize();

     fp = _Py_fopen(filename, "r");
     PyRun_SimpleFile(fp, filename);

     Py_Finalize();
     return 0;
}

Listing 5-5cpyth32.c

在这个程序(清单 5-5b )中,我们使用 np.arange(-2np.pi,2np.pi,0.1)给我们一组 x 值,它们是 pi 的倍数。因此,我们可以画出一个标准的三角函数。

import numpy as np
import matplotlib.pyplot as plt

# Choose evenly spaced x intervals
x = np.arange(-2*np.pi, 2*np.pi, 0.1)

# plot y = tan(x)
plt.plot(x, np.tan(x))

# Set the range of the axes
plt.axis([-2*np.pi, 2*np.pi, -2, 2])

# Include a title
plt.title('y = tan(x)')

# Optional grid-lines
plt.grid()
plt.xlabel('x values')
plt.ylabel('y values')
# Show the graph
plt.show()

该程序绘制出如图 5-3 所示的曲线。

img/515548_1_En_5_Fig3_HTML.jpg

图 5-3

标准曲线 y = tan(x)

网格图是一个 matplotlib 选项。在正切三角曲线的情况下,包含网格是有用的,因为它显示了渐近线。

我们可以允许用户输入要绘制的数据点,如下例所示。

输入要绘制的数据

下一个例子有一个更重要的 C 程序。该程序计算数学值积矩相关系数。这是对图形中 x 值和 y 值之间关系的度量。用户输入 x 和 y 值。C 程序计算这些值的 PMCC,并将其写入文件pmccfcnt.bin,将 x 和 y 值写入文件pmccf.bin。Python 程序读取这两个文件,并创建一个显示(x,y)点和 PMCC 值的图形。如果 x 和 y 值之间的关系是一条正斜率的直线,则 PMCC 为+1。如果我们得到一条负梯度的直线,那么 PMCC 就是-1。如果这些点几乎在正的直线上,那么 PMCC 大约是 0.9568。这些点离直线越远,PMCC 离 1 就越远,例如 0.7453。

下面我们来看一个例子,我们正在研究一辆汽车的价值如何在 6 年内贬值。在图 5-4 中,x 是年数,y 是以千美元计的汽车价值。

img/515548_1_En_5_Fig4_HTML.png

图 5-4

汽车折旧的 x,y 点

其曲线图如图 5-5 所示。

img/515548_1_En_5_Fig5_HTML.png

图 5-5

汽车折旧图

Value ($1000)

汽车折旧的 PMCC 是 0.996961。

PMCC 的公式是

r = Sxy/(Sx *Sy)(1)

其中 Sx=Sxx(2)

而 Sy=SYY(3)

s【xx =【x】【2】——【x】【2】/n(4)

s【YY =【和——【y】/n(5)

sxy =xy-(∑x∑y)/n(6)

∑x 表示所有 x 值的总和。

∑y 表示所有 y 值的总和。

∑x 2 表示所有 x 值的平方,然后求和。

∑y 2 表示所有 y 值的平方,然后求和。

∑xy 的意思是将每个 x,y 对相乘,然后求和。

在我们的六个公式中使用这些值,我们得到

x= 2.5+3.0+3.5+4.0+4.5+5.0+5.5+6.0 = 34

和= 11.5+10.6+9.2+7.8+6.1+4.7+3.9+1.8 = 55.6

xy= 2.5 * 11.5+3.0 * 10.6+3.5 * 9.2+4.0 * 7.8+4.5 * 6.1+5.0 * 4.7+5.5 * 3.9+6.0 * 1.8

= 28.75 + 31.8 + 32.2 + 31.2 + 27.45 + 23.5 + 21.45 + 10.8

= 207.15

x【2】= 2.5+3.0+3.52+4

= 6.25 + 9 + 12.25 + 16 + 20.25 + 25 + 30.25 + 36

= 155

【2】= 11.5+2+10.6+2+9.2+2+7

= 132.25 + 112.36 + 84.64 + 60.84 + 37.21 + 22.09 + 15.21 + 3.24

= 467.84

根据∑x 和∑y 的值,我们得到

$$ \overline{x} $$= * x/8 = 34/8 = 4.25

$$ \overline{y} $$= y/8 = 55.6/8 = 6.95

根据∑x 2 、∑y 2 和∑xy 的值,我们得到

s【xx =【x】——【x】/n

= 155–342/8 = 10.5

s【YY =【和——【y】/n

= 467.84–55.62/8 = 81.42

sxy =xy-(∑x∑y)/n

= 207.15 – 34*55.6 / 8 = –29.15

所以我们现在可以写

Sx=Sxx =3.24037

S =s【YY =9.0233

对 PMCC 使用这些值

r = Sxy/(Sx *Sy)

=–29.15/(3.24037 * 9.0233)

=–0.996961

因此,汽车折旧问题的乘积矩相关系数的值是–0.996961。这非常接近于-1,这将是完美的负相关。

如果您不熟悉前面的术语,∑是希腊字母“sigma”因此,在下面的程序中,我们称∑x 为“sigmax ”,之前使用的其他术语也是如此。

在程序中,我们使用 sigmax,sigmay,sigmaxsquared,sigmaysquared,xbar,ybar,sigmaxy。

在下面的程序中,如清单 5-6 所示,要求用户输入 x,y 对中的数据点。运行此程序时,请输入以下几点:

/*product moment correlation coefficient */
#define _CRT_SECURE_NO_WARNINGS
#define PY_SSIZE_T_CLEAN
#include <stdio.h>
#include <math.h>
#include <conio.h>
#include "include/Python.h"

main()
{
     double xpoints[10], ypoints[10];
     double sigmax, sigmay, sigmaxsquared, sigmaysquared, xbar, ybar, sigmaxy;
     double sxy, sxx, syy, sx, sy, r;
     int i, points;
     double fltcnt;
char filename[] = "searchpj3b.py"; /* python program to be called */
     FILE* fp2;

     FILE *fp;
          FILE *fp3;

     fp=fopen("pmccf2.bin","w"); /* file to store (x,y) values */
     fp3=fopen("pmccfcnt2.bin","w"); /* file to PMCC value */

     /* User enters number of points in scatter graph */
     /* with a maximum of 10 */

     printf("enter number of points (max 10 ) \n");
     scanf("%d", &points);
     if (points > 10)
     {
          /* User set number of points to be greater than 10 */
          /* Flag an error */

          printf("error - max of 10 points\n");

     }
     else
     {
          fprintf(fp3,"%d\n",points);

          /* set store areas to zero */
          sigmax = 0;
          sigmay = 0;
          sigmaxy = 0;
          sigmaxsquared = 0;
          sigmaysquared = 0;

          /* User enters points for scatter graph */
          for (i = 0;i < points;i++)
          {
               printf("enter point (x and y separated by space) \n");
               scanf("%lf %lf", &xpoints[i], &ypoints[i]);
               /* totals incremented by x and y points */
               sigmax = sigmax + xpoints[i];
               sigmay = sigmay + ypoints[i];
               sigmaxy = sigmaxy + xpoints[i] * ypoints[i];
               sigmaxsquared = sigmaxsquared + pow(xpoints[i], 2);
               sigmaysquared = sigmaysquared + pow(ypoints[i], 2);
          }

          /*print points and write them to file */

          printf("points are \n");
          for (i = 0;i < points;i++)
          {
               printf(" \n");
               printf("%lf %lf", xpoints[i], ypoints[i]);
               fprintf(fp,"%lf\t%lf\n",xpoints[i], ypoints[i]);
          }
          printf(" \n");
          fltcnt = points;

          /* variables in PMCC formula calculated */

          xbar = sigmax / fltcnt;
          ybar = sigmay / fltcnt;

          syy = (1 / fltcnt)*sigmaysquared - ybar * ybar;

          sxx = (1 / fltcnt)*sigmaxsquared - xbar * xbar;
          sx = sqrt(sxx);
          sy = sqrt(syy);
          sxy = (1 / fltcnt)*sigmaxy - xbar * ybar;

          /* PMCC value calculated */

          r = sxy / (sx*sy);
          printf("r is %lf", r);
          fprintf(fp3,"%lf\n",r);
     }
     fclose(fp);

     fclose(fp3);

     /* Call python program to print the graph */

      Py_Initialize();

     fp2 = _Py_fopen(filename, "r");
     PyRun_SimpleFile(fp2, filename);

     Py_Finalize();

}

Listing 5-6pmccf3.c

x values    y values

1.000000     2.000000
2.000000     3.000000
3.000000     5.000000
4.000000     9.000000
5.000000     10.000000
6.000000     13.000000

Python 程序(清单 5-6b )读取数据文件并创建图表。

import matplotlib.pyplot as plt
import numpy as np

#if there are 8 entered coordinates then this will be the arrays
#xvals = [0,1,2,3,4,5,6,7]
#yvals = [0,1,2,3,4,5,6,7]
#xvals = [0]*8
#yvals = [0]*8

# Read data from pmccf.bin file

y = np.loadtxt("pmccf.bin")
print("Data read from pmccf.bin")
print("y = ",y)

# Read data from pmccfcnt.bin file

z = np.loadtxt("pmccfcnt.bin")
print("Data read from pmccfcnt.bin")
print("z = ",z)
a,b = z # a is no. of coords entered, b is PMCC value

#zint is the number of coordinates entered
zint = int(a)
print("number of coordinates entered = ", zint)

print("PMCC = ", b)
float_b = b;
string_b = str(float_b)

# Set up the arrays for the graph

xvals = [0]*zint #length of array is num. of coords entered
yvals = [0]*zint #length of array is num. of coords entered

# set up the x and y arrays from the values entered
for x in range(zint):
    a,b = y[x]
    xvals[x] = a
    yvals[x] = b

# Print the x and y values to the user

print("xvals = ",xvals)
print("yvals = ",yvals)

# Display the graph

plt.xlabel('x values')
plt.ylabel('y values')
plt.title('PMCC Test Graph')
plt.text(1.0, 10, 'PMCC =')
plt.text(2.0, 10, string_b)

plt.plot(xvals, yvals, "ob")
plt.show()

Listing 5-6bsearchpj3b.py

这将输出到命令行

enter number of points (max 10 )
6
enter point (x and y separated by space)
1 2
enter point (x and y separated by space)
2 3
enter point (x and y separated by space)
3 5

enter point (x and y separated by space)
4 9
enter point (x and y separated by space)
5 10
enter point (x and y separated by space)
6 13
points are

1.000000 2.000000
2.000000 3.000000
3.000000 5.000000
4.000000 9.000000
5.000000 10.000000
6.000000 13.000000
r is 0.986227
Data read from pmccf.bin
y =  [[ 1\.  2.]
 [ 2\.  3.]
 [ 3\.  5.]
 [ 4\.  9.]
 [ 5\. 10.]
 [ 6\. 13.]]
Data read from pmccfcnt.bin
z =  [6\.       0.986227]
number of coordinates entered =  6
PMCC =  0.986227
xvals =  [1.0, 2.0, 3.0, 4.0, 5.0, 6.0]
yvals =  [2.0, 3.0, 5.0, 9.0, 10.0, 13.0]

这产生了如图 5-6 所示的图形。

img/515548_1_En_5_Fig6_HTML.jpg

图 5-6

PMCC 测试图

在这种情况下,计算的 PMCC 是 0.986227,非常接近+1,这将是完美的正相关,其中所有点都正好位于一条直线上。

接下来,我们将研究一种寻找物体质心的机制。

2D 质心图

这里,我们想找出由 2D U 形曲线(y = x**2)和直线 y = 4 围成的区域的质心在哪里。如果你做了一个如图 5-7 所示的形状,你用木头或熟石膏做了一个实心的形状,然后把它平放,你应该可以把手指放在重心点的下方,让它在手指上保持平衡。

如果你看下面的图,你可以看到它是关于 x = 0 的线对称的,所以你会期望质心位于那条线上。同样,看着这个物体,你可以看到在形状的顶部有更多的物质,所以你会认为质心在 x = 0 线的上部。我们可以通过使用随机数发生器机制找到它的确切位置。

计算机上的随机数生成器可以生成我们指定范围内的随机数。这里,我们希望我们的数字在曲线 y = x2 之上,在直线 y = 4 之下。我们可以生成 x =–2 和 x = +2 之间的数字以及 y = 0 和 y = 4 之间的 y 值。这给了我们围绕曲线的正方形内的数字。这是 x =–2 和 x = +2 以及 y =0 和 y = 4 之间的平方。当我们为一个点生成 x 和 y 值时,我们需要检查该点是否位于曲线上方,或者用数学术语来说,y > x2。我们为了 3500 点一直这样做。如果 x 和 y 值在曲线内,那么我们把 x 值加在一起,把 y 值加在一起。这样做 3500 次后,我们把 x 总数除以 3500,y 总数除以 3500。结果就是质心的坐标。我们把所有的 3500 个点写入一个输出文件,还有计算出的质心。

然后,我们将控制权交给 Python 程序,该程序读取这两个文件,并将生成的所有点绘制在一个图形上。每个点都是蓝色的,质心点是红色的。如图 5-7 所示。

img/515548_1_En_5_Fig7_HTML.jpg

图 5-7

我们想要找到质心的形状

C 程序是 cofm5a.c(清单 5-7 ),内嵌的 Python 程序是 searchpj4.py(清单 5-7b )。

#In this program "Centre of Mass" is abbreviated to cofm.

import matplotlib.pyplot as plt
import numpy as np
fhand = open('cofm5a.bin','r') #file of (x,y) values created by Cofm5a.c

count = 0
#if there are 8 entered coordinates then this will be the arrays
#xvals = [0,1,2,3,4,5,6,7]
#yvals = [0,1,2,3,4,5,6,7]
#xvals = [0]*8
#yvals = [0]*8

y = np.loadtxt("cofm5a.bin") # read x,y values

print("Data read from cofm5a.bin")
print(y)

z = np.loadtxt("cofm5acnt.bin") # read count of points, xcofm and ycofm
print("Data read from cofm5acnt.bin")
print(z)
a,p,j = z # split the 3 z values into separate variables
zint = int(a)
print("zint is " ,zint) # total number of points
string_p = str(p)
print("string_p is ",string_p ) # x value of c of m
string_j = str(j)
print("string_j is ",string_j ) # y value of c of m

xvals = [0]*zint
yvals = [0]*zint
# store the x and y coordinates into xvals[] and yvals[]
for x in range(zint-1):
    a,b = y[x]
    xvals[x] = a
    yvals[x] = b

plt.xlabel('x values')
plt.ylabel('y values')
plt.title(' CofM Graph (red spot is Centre of Mass)')
plt.plot(xvals, yvals, "ob")
plt.plot(p, j, "or")
plt.show()

Listing 5-7bsearchpj4.py

/*     cofm5a.c
     Centre of Mass Calculation.
In this program “Centre of Mass” is abbreviated to cofm.
     Calculates c of m for 2D shape y = x^² between y = 0 and y = 4 */
#define _CRT_SECURE_NO_WARNINGS
#define PY_SSIZE_T_CLEAN
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <time.h>

#include <conio.h>
#include "include/Python.h"

double randfunc();
main()
{

            int  I,outcount;
            float area,total,count;
            FILE *fptr;
            char filename[] = "searchpj4.py"; /* python program name */
            FILE* fp2;

            FILE *fp3;

            time_t t;

            /*  Local Arrays */
      double x, y,xout[3500],yout[3500],xcofm,ycofm;

            /* file cofm5a.bin contains all points inside the curve */
            /* file cofm5acnt.bin contains total number of points inside the curve and the x and y position of the centre of mass*/

            fptr=fopen("cofm5a.bin","w");
            fp3=fopen("cofm5acnt.bin","w");

            /* Initializes random number generator */
            srand((unsigned) time(&t));

            /* clears arrays to zero */
        for( I = 0; I<3500;I++)
            {
                    xout[I] = 0.0;
                    yout[I] = 0.0;

            }
            /* set x and y cofm accumulators to zero */
            xcofm=0.0;
            ycofm=0.0;

            total = 0.0;
            count = 0.0;
            outcount = 0;
     for( I = 1;I<= 3500;I++)
     {

                    /* get x values between -2 and +2 */
                    /* get y values between 0 and +4 */
            x = randfunc()*4.0-2.0;
            y = randfunc()*4.0;

                    /* If the generated x and y values are above */
                    /* the curve y=x² then add 1 to count */
                    /* and update the x and y cofm values */

              if(y>pow(x,2))
              {
                    xcofm=xcofm+x;
                    ycofm=ycofm+y;

                    total = total+1;
                    outcount = outcount +1;
                    xout[outcount] = x;
                    yout[outcount] = y;
             }
             count = count+1;

        }

            area=(total/count)*16;/* area is part of the square which is 4x4 or 16 sq units */
            printf("total is %f count is %f\n",total,count);

            xcofm=xcofm/total;
            ycofm=ycofm/total;

            printf("area is %lf\n",area);
            printf("cofm is %lf,%lf",xcofm,ycofm);

            /*  Plot the data */

            if(outcount >= 2700)
                    outcount = 2700;

            fprintf(fp3,"%d\t%lf\t%lf\n",outcount,xcofm,ycofm);

     for(I = 1; I<=outcount-1;I++)
                    fprintf(fptr,"%lf %lf\n",xout[I],yout[I]);
            fclose(fptr);
fclose(fp3);

/* Call python program to read the file and produce the diagram showing the position of the centre of mass */

Py_Initialize();

            fp2 = _Py_fopen(filename, "r");
            PyRun_SimpleFile(fp2, filename);

            Py_Finalize();

}

double randfunc()
{
            /* get a random number 0 to 1 */
            double ans;

            ans=rand()%1000;
            ans=ans/1000;

            return ans; /* return the random number to the caller */

}

Listing 5-7cofm5a.c

这段代码向命令行输出以下内容:

total is 2331.000000 count is 3500.000000
area is 10.656000
cofm is 0.006847,2.359818Data read from cofm5a.bin
[[-1.548  3.268]
 [-0.872  2.716]
 [-0.16   0.068]
 ...
 [ 0.136  2.972]
 [ 0.18   0.172]
 [ 1.484  3.744]]
Data read from cofm5acnt.bin
[2.331000e+03 6.847000e-03 2.359818e+00]
zint is  2331
string_p is  0.006847
string_j is  2.359818

这段代码产生了下图,如图 5-8 所示。

img/515548_1_En_5_Fig8_HTML.jpg

图 5-8

质心图

质心用红色的点表示。你可以猜测这可能是正确的,因为物体的形状关于 x = 0 的线是对称的,光斑看起来在那条线上,在形状的顶部有更多的蓝点,而红点在形状的顶部。

这个嵌入式 Python 程序展示了 C 和 Python 的优点,以及嵌入式技术的实用性。

我们现在来看看直方图,因为它们在经济学的许多领域都很有用。

直方图

直方图是一种表示数据分布的图形方式。它们在外观上类似于条形图,但它们将频率密度显示为分布而不是频率。

图表中的条形被称为“条柱”

以下程序显示了显示数字分布的直方图:

1,2,3,4,45,66,67,68,69,70,84.88,91,94

该程序将这些值分成十个组。我们在程序中指定需要 10 个面元,这个值用于调用绘制直方图的函数 plt.hist。对该函数调用的回复告诉您每个箱子中有多少物品。然后我们可以把它打印出来,我们看到值是

4\. 0\. 0\. 0\. 1\. 0\. 1\. 4\. 0\. 3.

因此,第一个容器中有四个项目(前面值列表中的 1,2,3,4),接下来的三个容器中没有项目,下一个容器中有一个项目,依此类推。

在清单 5-8 中,我们绘制了直方图,如图 5-9 所示。

import matplotlib.pyplot as plt

values = [1,2,3,4,45,66,67,68,69,70,84.88,91,94]
# draw a histogram with 10 bins of the `values’ data

number_of_bins = 10

n = plt.hist(values, number_of_bins, facecolor='blue')

print(n[0]) # counts in each bin

# plot the histogram

plt.title('test histogram')
plt.xlabel('preset numbers between 1 and 100')
plt.ylabel('Number in each bin')
plt.show()
This outputs
[4\. 0\. 0\. 0\. 1\. 0\. 1\. 4\. 0\. 3.]

Listing 5-8plot2b.py

img/515548_1_En_5_Fig9_HTML.jpg

图 5-9

测试直方图

本章末尾的练习中使用了这种直方图机制。

我们的最后一个例子展示了如何将一张图片导入我们的程序。

导入图片

Python 有“Image”、“ImageDraw”、“ImageFont”,可以从“PIL”导入,嵌入 C 程序。Python 程序可以使用它们来读取文件中的照片图像并打印出来。然后我们可以用文字覆盖一些图片。清单 5-9 演示了这一点。

#define PY_SSIZE_T_CLEAN
#include <stdio.h>
#include <conio.h>
#include "include/Python.h"

int main()
{
     char filename[] = "embim9.py";
     FILE* fp;

     Py_Initialize();

     fp = _Py_fopen(filename, "r");
     PyRun_SimpleFile(fp, filename);

     Py_Finalize();
     return 0;
}

Listing 5-9cpythim1.c

Python 程序(清单 5-9b )读取包含罗马图片的文件并绘制图像。然后,它会在图片顶部写下标题“罗马 2016”。如图 5-10 所示。

from PIL import Image, ImageDraw, ImageFont
#Open image using Image module
im = Image.open("5Rome.jpg") #photograph of Rome

myFont = ImageFont.truetype(r'C:\Users\System-Pc\Desktop\arial.ttf', 80)
#The 'r' character before the path, in the code above, is necessary if the path contains backslashes, #as the backslash could be interpreted as an escape character.
d1 = ImageDraw.Draw(im)
# Print text "Rome 2016" at the top of the picture
d1.text((28, 36), "Rome 2016", font=myFont, fill=(255, 0, 0))

#Show final Image
im.show()

Listing 5-9bembim9.py

img/515548_1_En_5_Fig10_HTML.png

图 5-10

导入的图片

摘要

本章演示了如何将 Python 代码和完整的 Python 程序嵌入到 C 程序中。我们已经展示了,如果您需要执行 Python 比 c 语言更能胜任的任务,这是多么有价值。

锻炼

  1. 编写一个 C 程序,读入学生在一次考试中获得的 20 个用户输入的分数(0 到 100)。将这 20 个标记写入文件。编写一个 Python 程序来读取该文件,并从这 20 个值中创建一个直方图。从 C 程序调用 Python 程序。

六、套接字

套接字系统允许两个或多个程序通过互联网相互通信。如果需要,可以在程序之间双向发送消息。文件可以被传输,我们可以让一个套接字同时与其他几个套接字通信(这被称为“多线程”)。

近距离观察插座

两个套接字使用预定义的序列进行通信的术语称为“握手”这些预定义的序列被称为“协议”服务器和客户端都必须遵守相同的协议;否则,它们不会起作用。这有点像两个说不同语言的人,都不知道对方的语言。他们的交流很快就会中断。

一个相当常用的协议是 TCP/IP,它是“传输控制协议/互联网协议”的缩写该协议在互联网上使用。每台使用互联网的设备都有自己的“IP 地址”这是一个唯一的地址。您可以使用命令行工具“ipconfig”找到您使用的计算机的 IP 地址。IP 地址具有相同的格式。一个地址可能是“123.456.7.89”。地址总是具有相同的模式,由句号(句点)分隔的四个数字组成。这种类型的 IP 地址称为 IPv4,地址为 32 位。有一个新版本的 IP 地址叫做 IPv6,它的地址是 128 位。这里我们将使用 IPv4。

Python 中的 socket 系统是使用我们熟悉的“ import 指令来访问的。在这种情况下,我们使用

import socket

使用套接字系统的程序被分为“服务器或“客户端一般来说,服务器负责通信,客户端要求连接到服务器。一旦建立了连接,消息就可以从服务器发送到客户机,也可以从客户机发送到服务器。

图 6-1 显示了服务器和客户端的主要代码调用。

img/515548_1_En_6_Fig1_HTML.png

图 6-1

服务器-客户机机制

在每一端,程序发出“ socket 命令,启动程序中的套接字机制。

在服务器端,下一个命令是“ bind ”。这就建立了作为服务器发布“绑定”的程序。IP 地址和端口号在 bind 命令中给出。

服务器的下一个命令是“ listen ”。在这里,服务器等待来自客户端程序的连接命令。

在客户端,在初始的“ socket 命令之后,它发出“ connect 命令来与服务器建立连接。然后它调用“ connect 连接到特定的服务器(由 IP 地址和端口号标识)。

服务器现在发出一个“ accept 命令来接受来自客户端的连接命令。

既然连接已经建立,服务器和客户机就可以相互发送和接收消息了。

当服务器和客户端之间的所有业务完成后,客户端向服务器发送“ close 命令,服务器和客户端之间的连接结束。

通过互联网发送的信息通常使用 UTF-8。

UTF-8 是 Unicode 的一部分,Unicode 是一种国际标准,用于为每个字符分配一个唯一的编号。它类似于 ASCII。它使用 1、2、3 或 4 个字节进行编码(通常只有 1 个字节)。

消息中的数据通常是字符串格式,使用。encode()命令。如果服务器程序将它发送给客户机程序,那么客户机将使用。解码()。

我们现在可以看看不同类型的插座。

基本客户端-服务器

这是一个基本的单向的,服务器和客户端的组合。如上图所述,有一个基本的连接和通信。服务器启动并等待来自客户端的连接。客户端启动并连接到服务器。服务器向客户端发送消息。客户端接收并打印这条消息。则连接终止。

如果您的客户机和服务器在同一台机器上,那么您可以使用命令“gethostname”来获取它的标识符,然后使用它的端口号作为它的完整地址。如果你的服务器和客户端在不同的机器上,那么你不能使用“gethostame”,但是你必须使用它们的 IP 地址。

这里,我们的服务器和客户机在同一台机器上,所以我们可以使用“gethostame”。如果我们想找到我们的 IP 地址,我们可以使用命令 socket.gethostbyname(hostname)。

列表 6-1 是服务器。

# socket server

import socket               # load socket module

skt = socket.socket()         # Create a socket (refer to it as skt in this program)
hostname = socket.gethostname()  # Get local host machine name
print(hostname)  #print the host machine name
port = 12357                # Reserve a port (same as client port)
skt.bind((hostname, port))        # Bind the host name and port (establish server status)

skt.listen(5)                 # wait for a client to connect.
while True:
   con, accaddr = skt.accept()     # Accept connection with client.
                                     # 'con' is the open connection between the server and client,  'accaddr' is the IP address and port number
   print('con is ', con) #print values to the user
   print('accaddr is ', accaddr) #print values to the user

   print('Received connection from', accaddr) #print values to the user

   message = "Got your connection"
   con.send(message.encode()) #send message to client to confirm connect

   con.close()                # Close connection
   break

The server outputs the following:
user-PC
con is  <socket.socket fd=448, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('123.456.7.89, 12357), raddr=(123.456.7.89', 63730)>
accaddr is  ('123.456.7.89', 63730)
Received connection from ('123.456.7.89', 63730)
(where the "123.456.7.89" characters are the IP addresses).

Listing 6-1socser10cx.py

第二个程序,清单 6-2 ,是客户端。

# socket client

import socket               # load socket module

skt = socket.socket()         # Create a socket
hostname = socket.gethostname() # Get local machine name
port = 12357                # Reserve a port (must be same as server)

skt.connect((hostname, port)) # connect to the server
data = skt.recv(1024) # receive data from server
             print('data is ',data.decode()) #print the line of data received
skt.close()                     # Close connection

Listing 6-2soccli10bx.py

客户端输出以下内容:

数据得到了你的连接

这显示了一个基本的套接字操作。我们现在来看看套接字之间的文件传输。

服务器-客户端对发送-接收文件

以下服务器-客户端对发送/接收文件。

服务器读取文件“pjfile1.txt”的每一行,并将每一行分别发送给客户端。

“pjfile1.txt”包含

This is my line
second line
third line
fourth line
last line

客户端读取从服务器收到的每一行,打印出来,并写入输出文件“pjrecfile”。

清单 6-3 显示了文件发送的服务器。

# send file to client

import socket                   # Import socket module

port = 3000                     # Reserve a port for your service.
s = socket.socket()             # Create a socket object
host = socket.gethostname()     # Get local machine name
s.bind((host, port))            # Bind to the port
s.listen(5)                     # Now wait for client connection.

print ('Server running')

while True:
    conn, addr = s.accept()     # Establish connection with client.
    print ('Got connection from', addr)
    data = conn.recv(1024) #receive connection from client
    print('Server received', bytes.decode(data)) #print out the data received

    filename='pjfile1.txt'  #the name of the file to be opened
    file = open(filename,'rb') #open the file we wish to send
# read each line in the file and send it to the client
line = file.read(1024)
    while (line):
       conn.send(line) #send the line to the client
       print('Server received ',bytes.decode(line)) #tell user the line that has been received by client
       line = file.read(1024) #read the next line
    file.close() #finished sending so close the file

    print('Finished sending')
    conn.send(str.encode('Thank you for connecting')) #send final message to client
    conn.close() #close the connection
    break

Listing 6-3socserfile.py

连接后,服务器输出

Server received Client connected
Sent  This is my line
second line
third line
fourth line
last line

Finished sending

清单 6-4 中显示的下一个程序是读取和打印文件的相关客户端程序。然后,它创建一个输出文件,并将读取的文件写入其中。

# Socket Client Program
# receive file from server
# and write it to a new file

import socket                   # Import socket module

s = socket.socket()             # Create a socket object
host = socket.gethostname()     # Get local machine name
port = 3000                     # Set the port for your service.

s.connect((host, port)) # connect to server
s.send(str.encode("Client connected")) #send connect confirmation to user
# open the output file (pjrecfile)
with open('pjrecfile', 'wb') as file:
    print ('file opened')
    while True:
        print('receiving data from server')
        data = s.recv(1024) # receive each line of data from server (at most 1024 bytes)
        print('data is ',bytes.decode(data)) #print the line of data received
        if not data:
            break
        # write data to the file

        file.write(data)

file.close() # close the output file
print('Received file from server')
print('Written to output file pjrecfile')
s.close() #close the connection
print('connection closed')

Listing 6-4socclifile.py

连接后,客户端输出

file opened
receiving data from server
data is  This is my line
second line
third line
fourth line
last line

receiving data from server
data is  Thank you for connecting
receiving data from server
data is
Received file from server
Written to output file pjrecfile
connection closed

pjrecfile 包含

This is my line
second line
third line
fourth line
last line
Thank you for connecting

这展示了一种使用套接字进行文件传输的方法。到目前为止,我们只看到了一个服务器与一个客户机通信。我们现在将对此进行扩展。

线程程序

套接字的线程系统可以让几个客户端程序同时连接到一个服务器。客户端可以连接和断开任意多次。服务器循环等待客户端连接。清单 6-5 显示了线程服务器的代码。

import socket     # Import socket module
from _thread import * # thread software
import os

mypid = os.getpid()# get Process ID (pid)for this program
print('Server My pid is', mypid) #print the pid in case the user wants to 'taskkill' this program

ServerSocket = socket.socket()
host = socket.gethostname()     # Get local machine name

port = 1234
ThreadCount = 0     #count of how many clients have connected
ServerSocket.bind((host, port))

print('Waiting for Client connect')
ServerSocket.listen(5) #wait for a client connection

# Function for each connected client
def threadcli (cliconn):
    cliconn.sendall(str.encode('Connect'))
    while True:
        data = cliconn.recv(2048) #receive message from client
        reply = 'Server replies: ' + data.decode() #set up reply
        if not data:
            break
        cliconn.sendall(str.encode(reply)) #send the reply to the client
    cliconn.close()

# wait for a connection from a new client

while True:
    Cli, addr = ServerSocket.accept()
    print('Connected to: ' + addr [0] + ':' + str(addr [1]))
    print(Cli) # show server and client addresses
    start_new_thread(threadcli, (Cli, )) #function imported from  '_thread'
                              # This calls local function 'threadcli'
                              # Each threaded client stays within its own
                              # 'threadcli' function
        ThreadCount += 1 #add 1 to number of connected clients
    print('Thread Number: ' + str(ThreadCount))
ServerSocket.close()

Listing 6-5socserthreadgx2.py

连接后,服务器输出

Server My pid is 6296
Waiting for Client connection
Connected to: ..................................(IP address and Port number)
Thread Number: 1

第二个程序是客户端。当客户端想要结束连接时,用户输入“windup ”,连接就会关闭。清单 6-6 显示了线程客户端的代码。

import socket
import os

mypid = os.getpid() # get Process ID (pid)for this program
print('Client My pid is', mypid) #print the pid

ClientSocket = socket.socket()
host = socket.gethostname()     # Get local machine name

port = 1234

print('Waiting for connection')
ClientSocket.connect((host, port)) #connect to Server

Response = ClientSocket.recv(1024)
print(ClientSocket) # show server and client addresses
while True:
    Input = input('Enter message: ') #ask user to enter their message
    ClientSocket.send(str.encode(Input)) #send message to socket
    Response = ClientSocket.recv(1024) #get response from server

    if( Response.decode()) =='Server replies: windup':
        # if client wants to disconnect from server, the user types 'windup'
        break
    print(Response.decode())
ClientSocket.close() #close the client

Listing 6-6socclithreadgx2.py

连接后,客户端输出

Client My pid is 2248
Waiting for connection
Enter message: hello from client1
Server replies: hello from client1
Enter message: windup

上面显示了多个客户端连接到一台服务器。由于客户端可以连接、断开连接,然后重新连接,这可能会导致冗余服务器出现问题。我们现在来看看这个。

关闭线程服务器

大多数套接字客户端和服务器可以定期关闭。但是,在线程套接字的情况下,服务器可能会无限期地运行,因为许多客户端可以在一段时间内连接和断开。

如果我们确定所有的客户端都已经与服务器断开连接,并且我们想要关闭服务器,我们可以使用命令行指令“tasklist”和“taskkill”。

“tasklist”命令为我们提供了当前正在运行的任务列表。一个例子如下所示:

C:\Users\user\AppData\Local\Programs\Python\Python37>tasklist
Image Name            PID  Session Name       Session#    Memory Usage
=================== ======== ================ =========== ============
System Idle Process      0 Services            0                    8 K
System                   4 Services            0                   36 K
Registry               100 Services            0               51,384 K
smss.exe               352 Services            0                  648 K
csrss.exe              516 Services            0                2,744 K
wininit.exe            628 Services            0                3,852 K
services.exe           700 Services            0                6,616 K
lsass.exe              708 Services            0               14,688 K
svchost.exe            904 Services            0               22,944 K
fontdrvhost.exe        932 Services            0                1,568 K
svchost.exe             68 Services            0               14,284 K
cmd.exe              12004 Console             3                5,044 K
conhost.exe           9936 Console             3               19,564 K
UserOOBEBroker.exe   11604 Console             3                9,040 K
notepad.exe           1448 Console             3               44,144 K
notepad.exe          10452 Console             3               41,768 K
python.exe            6576 Console             3               10,596 K
cmd.exe               7104 Console             3                5,048 K
conhost.exe           1332 Console             3               19,580 K
cmd.exe              10344 Console             3                5,284 K
conhost.exe           6120 Console             3               19,512 K
Microsoft.Photos.exe   772 Console             3                8,356 K
RuntimeBroker.exe    12072 Console             3               31,176 K
tasklist.exe          4944 Console             3                9,836 K
C:\Users\user\AppData\Local\Programs\Python\Python37>

我们看到我们的 python.exe 计划在这个列表中。我们可以使用“taskkill”关闭它,如下所示:

C:\Users\user\AppData\Local\Programs\Python\Python37> taskkill /F /IM python.exe

SUCCESS: The process "python.exe" with PID 6576 has been terminated.

C:\Users\user\AppData\Local\Programs\Python\Python37>

OR WE CAN USE THE PID TO TERMINATE IT'

如果有多个 python.exe 在运行,我们需要确保终止的是正确的一个。每个正在运行的程序都有一个唯一的进程 ID 或 PID。我们可以使用 pid 来确保终止正确的那个。这里,我们展示了一个包含两个 python.exe 程序的任务列表。

C:\Users\user\AppData\Local\Programs\Python\Python37>tasklist

Image Name             PID  Session Name      Session#    Memory Usage
=================== ======== ================ =========== ============
System Idle Process      0 Services            0                   8 K
System                   4 Services            0                  36 K
Registry               100 Services            0              52,328 K
smss.exe               352 Services            0                 648 K
csrss.exe              516 Services            0                2,752 K
wininit.exe            628 Services            0                3,852 K
services.exe           700 Services            0                6,600 K
lsass.exe              708 Services            0               14,768 K
svchost.exe            904 Services            0               22,944 K
fontdrvhost.exe        932 Services            0                1,568 K
svchost.exe             68 Services            0               14,268 K
svchost.exe           1056 Services            0               19,188 K
svchost.exe           1084 Services            0               25,116 K
svchost.exe           1120 Services            0               23,404 K
svchost.exe           1288 Services            0               27,984 K
svchost.exe           1344 Services            0               66,208 K
notepad.exe           1448 Console             3               44,144 K
notepad.exe          10452 Console             3               41,800 K
cmd.exe               7104 Console             3                5,052 K
conhost.exe           1332 Console             3               19,620 K
cmd.exe              10344 Console             3                5,284 K
conhost.exe           6120 Console             3               19,956 K
Microsoft.Photos.exe   772 Console             3                8,356 K
RuntimeBroker.exe    12072 Console             3               31,196 K
svchost.exe          12704 Services            0                9,592 K
audiodg.exe           4716 Services            0               11,748 K
smartscreen.exe      12000 Console             3               22,928 K
python.exe            5492 Console             3               10,548 K
python.exe           16648 Console             6               11,100 K
tasklist.exe          2448 Console             3                9,824 K
If we know that it is the one with pid of 5492, we can type
C:\Users\user\AppData\Local\Programs\Python\Python37>taskkill /F /PID 5492

并接收

SUCCESS: The process with PID 5492 has been terminated.

C:\Users\user\AppData\Local\Programs\Python\Python37>

如果你有两个或更多的 python.exe 程序在运行(每个都有不同的 pid),你可能不知道该杀哪个。如果我们知道我们可能想从命令行终止我们的进程,我们能做的就是让程序在启动时打印出它的 pid。它可以使用包含在操作系统库中的 getpid() Python 指令找到它的 pid。

因此,在我们的计划中,我们将包括

import os
mypid = os.getpid()
print('My pid is', mypid)

因此,指令 os.getpid()会返回变量“mypid”中的程序 pid,然后程序会将它打印出来。然后,用户可以像前面一样使用相关 pid 来使用“taskkill”命令,例如:

C:\Users\user\AppData\Local\Programs\Python\Python37>python Socclithreadg.py
My pid is 4120
Waiting for connection
<socket.socket fd=420, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('123.456.7.89'), raddr=('123.456.7.89')>
Enter message:

然后在另一个窗口,我们将使用 taskkill

C:\Users\user\AppData\Local\Programs\Python\Python37>taskkill /F /pid 4120 /T

并接收

SUCCESS: The process with PID 4120 (child process of PID 16520) has been terminated.

C:\Users\user\AppData\Local\Programs\Python\Python37>
This section has shown how to close a server. This mechanism should only be used when necessary. We will now look at “chat” programs.

聊天程序

聊天套接字程序有双向发送和接收。服务器仍然启动连接过程,但是对话是双向的(因此称为“聊天”)。

清单 6-7 中显示的第一个程序是服务器。

# Server Side Script
# Socket Server Program

import time, socket, sys

server_port = 1000

server_socket = socket.socket()
host_name = socket.gethostname()
server_socket.bind((host_name ,server_port))

server_socket.listen(1) #look for client connect
print ("Server is loaded")
connection_socket, address = server_socket.accept() # accept client connect
while True:
    sentence = connection_socket.recv(2048).decode() #receive incoming message
    print('>> ',sentence) # print the message to the user
    message = input(">> ") #ask the user to input a reply
    connection_socket.send(message.encode()) #send reply
    if(message == 'windup'):
       connection_socket.close() # a 'windup' message means the user wants to disconnect
       break

Listing 6-7socsert2x.py

来自服务器的输出(检查客户端输出以查看双向聊天):

Server is loaded
>>  hello from client
>> hello from server
>>  windup
>> windup

清单 6-8 中显示的第二个程序是客户端。当客户想要结束聊天时,用户输入“结束”,聊天就结束了。

# Client Side Script
# Socket Client Program

import time, socket, sys

server_name = socket.gethostname()
server_port = 1000

client_socket = socket.socket()
host_name = socket.gethostname()

client_socket.connect((server_name,server_port)) #connect to the server
while True:
    sentence = input(">> ") #input your message
    client_socket.send(sentence.encode())#send your message
    message = client_socket.recv(2048) #receive reply
    print (">> ", message.decode()) #print the reply
    if(sentence == 'windup'):
       client_socket.close() # a 'windup' command means the user wants to disconnect
       break

Listing 6-8socclit2x.py

来自客户端的输出(检查服务器输出以查看双向聊天):

>> hello from client
>>  hello from server
>> windup
>>  windup
This has demonstrated two-way send and receive sockets.
The chapter has shown the fundamentals of sockets and the variety of types of communication we can use sockets for.

摘要

本章说明了套接字服务器和客户机如何交互,以及它们如何在不同的组合中使用。

锻炼

  1. 为服务器和客户端编写聊天程序。然后再写两个,与前两个的唯一区别是端口号。在不同的窗口上运行所有四个程序。检查具有相同端口号的服务器-客户端对是否可以相互聊天(即,具有一个端口号的服务器不应该与具有不同端口号的客户端聊天)。
posted @ 2024-08-09 17:40  绝不原创的飞龙  阅读(4)  评论(0编辑  收藏  举报