2.3网安学习第二阶段第三周回顾(个人学习记录使用)

本周重点

①C语言结构体的介绍

②Python的数据类型

③Python语法实例(难点)

1、SCC强连通

2、Dijkstra最短路径

3、HEAP堆

4、HASH哈希算法

④Python操作文件

1、普通文件操作

2、操作CSV文件

⑤Python操作数据库

⑥Socket套接字

本周主要内容

DAY1 Python基础语法

①C语言结构体的介绍

1、结构体定义
struct point {
  int x;
  int y;
} p1, p2;
​
#include <stdio.h>
struct point {
  int x;
  int y;
}p2;
struct rect {
  struct point pt1;
  struct point pt2;
};
int main() {
  struct rect screen;
  struct point p1;
  p1.x = 1;
  p1.y = 1;
  screen.pt1 = p1;
  screen.pt2.x = 5;
  screen.pt2.y = 3;
  printf("%d\n", screen.pt1.x);
  printf("%d\n", screen.pt1.y);
  printf("%d\n", screen.pt2.x);
  printf("%d\n", screen.pt2.y);
}
2、函数返回结构体
struct point {
  int x;
  int y;
}p2;
struct rect {
  struct point pt1;
  struct point pt2;
};
struct point makepoint(int x, int y)
{
  struct point temp;
  temp.x = x;
  temp.y = y;
  return temp;
}
int main() {
  struct rect screen;
  struct point p1,p3;
  p1 = makepoint(1,1);
  p3 = makepoint(10,3);
  screen.pt1 = p1;
  screen.pt2.x = 5;
  screen.pt2.y = 3;
  printf("%d\n", screen.pt1.x);
  printf("%d\n", screen.pt1.y);
  printf("%d\n", screen.pt2.x);
  printf("%d\n", screen.pt2.y);
}
3、函数传入结构体
struct point addpoint(struct point p1, struct point p2){
  p1.x += p2.x;
  p1.y += p2.y;
  return p1;
}
4、指针接收结构体(用的最多)
int main()
{
  struct point origin, *pp;
  origin = makepoint(10, 10);
  pp = &origin;
  printf("%d\n", (*pp).x);
  printf("%d\n", pp->y);
}

②Python的数据类型

简介

一门解释型语言(编译型,解释型,混合),python是属于解释型,python里面没有真正意义上的基本数据类型,所有的数据都是对象。

python的解释器,CPython是用C语言开发的;
1、整型INT
struct pInt {
    int ob_size;
    int ob_digit[];
};

例如:

ob_size:存放的是数组的长度,如果是负数,代表的是一个负数;
ob_digit[]:存放的是每一位的数字的大小;
ob_size = 1;
ob_digit[0] = 3;
表示成python里面的数字是:3
ob_size = -1;
ob_digit[0] = 3;
表示成python里面的数字是:-3
ob_size = 2;
ob_digit[0] = 1;
ob_digit[1] = 4;
表示成python里面的数字是:1 + 4 * 2^30 = 4294967297
如果是两个数相加 1 + 3:
struct pInt p1, p2;
p1.ob_size =1;
p1.ob_digit[0] =1 ;
p2.ob_size =1;
p2.ob_digit[0] =3 ;
p1.ob_digit[0] + p2._digit[0] = 4;

优缺点:

优点:不用考虑溢出得到问题;缺点:性能低;
2、浮点Float
type(1.345)
<class 'float'>
f1[] = {3, 1, 4, 1, 5, 9, 2, 6, 1, 7, 1, 1, 3, 3, 3, 3}
f2[] = {2, 1, 7, 8, 1, 3, 5, 9, 10}
3、字符串String
struct str {
    int len;
    char *s;
}

例如:

"asdsadsa"
char[] = {'a', 's', 'd', 's', 'a', 'd' , 's' , 'a'};
4、函数

黄金比例

image-20240108151427856

image-20240108151439598

性质

image-20240108151634187

image-20240108151715699

函数可以当作参数传给另一个函数

def golden_update(guess):
    return 1 + 1/guess
def square_close_to(guess):
    return approx_eq(guess * guess, guess+1);
def approx_eq(x, y):
    return abs(x - y) < 0.00000001
def improve(update, close, guess):
    while not close(guess):
        guess = update(guess)
    return guess
phi = improve(golden_update, square_close_to, 1)
print(phi)

平方根

性质

求:sqrt(a)
是否足够接近:(x^2 - a) < 0.0001
更新:x = (x + (a/x))/2

函数里面可以定义其它函数,里面得函数可以引用外面函数定义的变量

def average(x, y):
    return (x + y) / 2;
def sqrt(a):
    def sqrt_update(x):
        return average(x, a/x)
    def sqrt_close(x):
        return approx_eq(x * x, a);
    return improve(sqrt_update, sqrt_close, 1)

补充:

任意数开任意次方;
函数可以当作另一个函数的返回值
5、集合SETS

集合特性:

  1. 无序;

  2. 不能重复。

基本操作:

#申明:
example = set()
#添加
example.add(42)
example.add(42)
example.add(True)
example.add('asdsdadsa')
#删除
example.remove(True)
#集合的并集
example2 = example.union(example1)
example2 = example.intersection(example1)
#判断是否存在
if 42 in example:
    print("YES")

集合的分配律(练习):

image-20240108143816739

6、列表List

列表:

  1. 有序的;

  2. 可以重复的。

#申明和添加
lt = list()
lt.append(10)
lt.append('asdsa')
lt.append(50)
lt.append(True)
lt.append(10)
#数据结构,底层也是结构体,列表的元素可能隔的“比较远”。
lt2 = [1000000000000000000000000000000000000000000000000000000,3 ,8, 100]
print(type(lt2))
print(id(lt2[0]))
print(id(lt2[1]))
#访问元素
lt3 = [1, "aa", True, [3, 4, 5]]
print(lt3[3][1])
#简写
lt4 = [1, 2, 3, 4]
square2 = [i**2 for i in lt4]
7、字典DIC

是key-value结构的:

post = {"user_id": 209, "message": "hello", "age":18, "location":(180, 45)}
post2 = dict(name = "yyy", age=19, height="170", weight=150)
print(type(post['location']))
#print(post2['fff']), 会报错;
if 'fff' in post2:
    print(print(post2['fff']))
else:
    print('fff不在里面')
for i in post2:
    print(post2[i])
for key in post2.keys():
    value = post2[key]
    print(key, "=", value)

Graph

[[1, 2], [2, 0], [0, 1]]
通过字典代替:
3: [1, 2]
1: [2, 3]
2: [3, 1]
8、元组TUPLE

做线性代数的一些基本运算。

申明:

#方式
list_eg = [1, 2, 3, 4, "a", "b", 'c', True, 3.1415926]
tuple_eg = (1, 2, 3, 4, "a", "b", 'c', True, 3.1415926)
#tulpe是不能删除和添加元素的,不能改变元素的大小;
#list可以添加或者删除;
#速度:tuple申明的速度要快
list_time = timeit.timeit(stmt="[1,2,3,4,5]", number=10000000)
tuple_time = timeit.timeit(stmt="(1,2,3,4,5)", number=10000000)
print(list_time)
print(tuple_time)
#大小,同样的元素个数,tuple所占的内存要小
print(len(list_eg))
print(len(tuple_eg))
print(sys.getsizeof(list_eg))
print(sys.getsizeof(tuple_eg))
9、类对象Class

对象:类似结构体;

实例:具体占用内存空间的指针;

简单用法:

class Person:
    pass
user1 = Person()
user1.first_name = "Dave"
user1.last_name = "Bowman"
print(user1.first_name)
print(id(user1))
user2 = Person()
user2.first_name = "Frank"
user2.last_name = "Bowman"
print(user2.first_name)
print(id(user2))

用例2:

class Stu:
    def __init__(self, full_name, birthday):
        self.name = full_name
        self.birthday = birthday
        name_pieces = full_name.split(" ")
        self.first_name = name_pieces[0]
        self.last_name = name_pieces[1]
    def age(self):
        today = datetime.date(2077,12,5)
        yyyy = int(self.birthday[0:4])
        mm = int(self.birthday[4:6])
        dd = int(self.birthday[6:8])
        dob = datetime.date(yyyy, mm, dd)
        age_in_days = (today - dob).days
        age_in_years = age_in_days/365
        return int(age_in_years)
students1 = Stu("Frank Bowman", "20231112")
print(students1.first_name)
print(students1.name)
print(students1.last_name)
print(students1.age())
#__init__是构造器,调用Stu(..)会直接调用【__init__】,里面的self代表只想当前实例(申请的内存空间)的指针;
#age是自己定义的方法,只有调用的时候才会执行;

DAY2 SCC强连通分量&Dijkstra最短路径

①SCC强连通

image-20240109104845391

image-20240107182342796

计算步骤(Two pass):

1. 先把图“倒”过来;
2. 运行DFS进行编号;
3. 再对新编号的原始图遍历(从编号大的往小的走)。

第一二步:

image-20240107182314335

[1, 4, 5, 2, 8, 3, 6, 9, 7]

DFS(G, i)
    visited[i] = true
    for each edge(i, j) in G:
        if visited[j] == False
            DFS(G, j)
    t++
    f(i) = t

第三四步:

image-20240107182326976

for i = n down to 1:
    if i not yet visited
        leader[count] = i
        count++
        DFS(G, i)

代码实现:

from collections import defaultdict


class Graph:
    def __init__(self, adj_list):
        # 初始化图,使用邻接表表示有向图
        self.graph = adj_list

    def addEdge(self, vertex1, vertex2):
        # 向图中添加有向边
        self.graph[vertex1].append(vertex2)

    def reverGraph(self):
        # 定义一个空图,数据类型为 defaultdict(list)
        inverseG = Graph(defaultdict(list))

        # 遍历原图的顶点及其邻居
        for i in self.graph:
            for j in self.graph[i]:
                # 在反向图中添加反向的有向边
                inverseG.addEdge(j, i)
        return inverseG

    # 使用DFS按照倒的图进行编号
    def finishingTimeDFS(self, vertex, visited, stack):
        # 标记当前顶点为已访问
        visited[vertex] = True
        for i in self.graph[vertex]:
            if visited[i] == False:
                # 递归调用DFS,按照倒的图进行编号
                self.finishingTimeDFS(i, visited, stack)
        # 将当前顶点入栈,记录完成时间
        stack.append(vertex)

    # 按编号正向遍历,获取一个强连通分量
    def getOneSCC(self, vertex, visited, scc):
        # 将当前顶点加入当前强连通分量
        scc.append(vertex)
        # 标记当前顶点为已访问
        visited[vertex] = True

        for v in self.graph[vertex]:
            if visited[v] == False:
                # 递归调用,正向遍历顶点
                self.getOneSCC(v, visited, scc)

    # 计算强连通分量
    def computeSCC(self):
        stack = []
        visited = defaultdict(bool)

        # 编号:使用DFS按照倒的图进行编号
        for i in list(self.graph.keys()):
            if visited[i] == False:
                self.finishingTimeDFS(i, visited, stack)

        # 反转图
        inverG = self.reverGraph()

        # 存放强连通分量
        SCC_list = []
        visited = defaultdict(bool)
        while stack:
            i = stack.pop()
            if visited[i] == False:
                scc = []
                # 获取一个强连通分量
                inverG.getOneSCC(i, visited, scc)
                SCC_list.append(scc)
        return SCC_list


# 示例用法
adj_list = defaultdict(list,
                       {7: [1],
                        4: [7],
                        1: [4],
                        9: [7, 3],
                        6: [9],
                        8: [6, 5],
                        2: [8],
                        5: [2],
                        3: [6]})

# 创建图对象
oriG = Graph(adj_list)
# 获取反向图
invG = oriG.reverGraph()
# 计算并打印强连通分量
print(invG.computeSCC())

发现(2000年)

image-20240107215137784

Giant scc符合【small world】,爬虫会比较快。(https://snap.stanford.edu/class/cs224w-readings/broder00bowtie.pdf

②Dijkstra最短路径

image-20240109161255121

代码实现:

from collections import defaultdict

# 定义一个节点距离的类
class NodeDistance:
    def __init__(self, name, dist):
        self.name = name  # 节点名称
        self.dist = dist  # 节点距离

# 定义图的类
class Graph:
    def __init__(self, node_count):
        self.adjlist = defaultdict(list)  # 使用 defaultdict 创建邻接表
        self.node_count = node_count  # 图中节点的数量

    # 添加节点距离到邻接表中
    def add_into_adjlist(self, src, node_dist):
        self.adjlist[src].append((node_dist.name, node_dist.dist))

    # 计算从源节点到其他节点的最短路径
    def Shortest_Path(self, src):
        distance = [9999999999999] * self.node_count
        distance[src] = 0
        disc_node_length = {src: 0}

        while disc_node_length:
            # 选择距离最短的节点
            current_source_node = min(disc_node_length, key=lambda k: disc_node_length[k])
            del disc_node_length[current_source_node]

            # 更新相邻节点的距离
            for node_dist in self.adjlist[current_source_node]:
                adjnode = node_dist[0]
                length_to_adjnode = node_dist[1]

                if distance[adjnode] > distance[current_source_node] + length_to_adjnode:
                    distance[adjnode] = distance[current_source_node] + length_to_adjnode
                    disc_node_length[adjnode] = distance[adjnode]

# 实例化图对象
my_graph = Graph(4)  # 提供图中节点的数量

# 创建一些节点距离对象
v1 = NodeDistance(1, 1)
v2 = NodeDistance(2, 4)
v3 = NodeDistance(2, 2)
v4 = NodeDistance(3, 6)
v5 = NodeDistance(3, 3)

# 将节点距离添加到邻接表中
my_graph.add_into_adjlist(0, v1)
my_graph.add_into_adjlist(0, v2)
my_graph.add_into_adjlist(1, v3)
my_graph.add_into_adjlist(1, v4)
my_graph.add_into_adjlist(2, v5)

# 计算从源节点到其他节点的最短路径
my_graph.Shortest_Path(0)

# 打印邻接表
print("邻接表:", my_graph.adjlist)

DAY3 HEAP堆&HASH哈希算法

①HEAP堆

image-20240109152229274

节点(树)表示

1. 自己写一个Node对象,定义树;
2. 往里面填值;
3. 考虑如何遍历。

数组表示

[4, 4, 8, 9, 4, 12, 9, 11, 13]
[1, 2, 3, 4, 5, 6,  7, 8,  9]

结论:

数组的第一个元素一定是最小的;小顶堆

对象(Heap):

取(Extract):取第一个元素;
parent(i):i//2
child(i): i*2 
#分成左儿子和右儿子,怎么区分?

插入:

image-20240109152640392

时间复杂度:log(N),N代表的是节点;

获取:

image-20240109152702656

时间复杂度:log(N),N代表的是节点;

代码实现:

import sys

class Heap:
    def __init__(self, maxsize):
        # 初始化堆对象
        self.maxsize = maxsize
        self.size = 0
        # 用于存储堆元素的数组,索引从1开始,0位置不使用
        self.Heap = [0] * (self.maxsize + 1)
        # 哨兵,表示负无穷,用于比较时确保较小的元素被交换到正确的位置
        self.Heap[0] = -1 * sys.maxsize

    def parent(self, pos):
        # 返回父节点的索引
        return pos // 2

    def leftChild(self, pos):
        # 返回左子节点的索引
        return 2 * pos

    def rightChild(self, pos):
        # 返回右子节点的索引
        return (2 * pos) + 1

    def isLeaf(self, pos):
        # 检查当前节点是否为叶子节点
        return pos > (self.size // 2) and pos <= self.size
        #return pos * 2 > self.size

    def swap(self, spos, fpos):
        # 交换数组中两个位置的元素
        self.Heap[fpos], self.Heap[spos] = self.Heap[spos], self.Heap[fpos]

    # insert 方法将元素添加到堆中
    def insert(self, element):
        # 插入新元素到堆中
        if self.size >= self.maxsize:
            return
        self.size += 1
        self.Heap[self.size] = element

        current = self.size

        # 保持堆的性质,不断与父节点比较和交换
        while self.Heap[current] < self.Heap[self.parent(current)]:
            self.swap(current, self.parent(current))
            current = self.parent(current)

    #extract 方法删除并返回最小的元素
    def extract(self):
        # 弹出并返回堆中最小的元素
        popped = self.Heap[1]
        self.Heap[1] = self.Heap[self.size]
        self.size -= 1
        # 重新调整堆结构
        self.heapify(1)
        return popped

    #heapify 方法确保在提取元素后保持堆的属性
    def heapify(self, pos):
        # 从给定位置开始递归调整堆结构
        if not self.isLeaf(pos):
            if (
                self.Heap[pos] > self.Heap[self.leftChild(pos)]
                or self.Heap[pos] > self.Heap[self.rightChild(pos)]
            ):
                if self.Heap[self.leftChild(pos)] < self.Heap[self.rightChild(pos)]:
                    # 如果左子节点小于右子节点,交换并继续递归
                    self.swap(pos, self.leftChild(pos))
                    self.heapify(self.leftChild(pos))
                else:
                    # 如果右子节点小于等于左子节点,交换并继续递归
                    self.swap(pos, self.rightChild(pos))
                    self.heapify(self.rightChild(pos))

# 创建最大容量为10的堆
my_heap = Heap(10)

# 插入一些元素
my_heap.insert(9)
my_heap.insert(11)
my_heap.insert(13)
my_heap.insert(4)
my_heap.insert(4)
my_heap.insert(8)
my_heap.insert(9)
my_heap.insert(4)
my_heap.insert(12)
print("当前堆的原始状态:", my_heap.Heap)

# 打印当前堆的状态
print("当前堆的实际状态:", my_heap.Heap[1 : my_heap.size + 1])

# 提取并打印最小元素
min_element = my_heap.extract()
print("提取的最小元素:", min_element)

# 打印提取后的堆状态
print("提取后的堆状态:", my_heap.Heap[1 : my_heap.size + 1])

补充

可以利用Heap优化Dijkstra。

②HASH哈希算法

目的

通过名称(id)找人的信息;

{"yangkm": ["yyy", 18, "本科", "男",.. ],
"zhangjz": ["zjz", 18, "本科", "男",.. ],
"zhangjq": ["yyy", 18, "本科", "男",.. ]}

问题:

假如名称最长是25位,那么我的数组需要多长?

解决办法

用有限的数组 ”Buckets(桶)“,比如说n=100,也就是说用一个长度位100的数组存放人的信息。

如果n=100,名字:ascii(yangkm) = 719434234, 放到数组Array的719434234%100位置(Arrray[719434234%100]={杨铠铭的相关信息}),下次要查找的时候直接找:Arrray[719434234%100];

如果ascii(zhangjiaqin) = 719434334,那就和ascii(yangkm)对100取模后的结果一样,hash碰撞。

碰撞的概率

生日悖论:在n个人中,找两个人的出生年月在同一天,那么n多大的时候,这个概率会>0.5(超过一半)?23人。

如何解决碰撞

设计好的hash算法:

hash的过程:
29 % 8 = 5
37 % 8 = 5
上面会发生碰撞,两个元素都会放到5这个位置;
hash(29) = 2^2 + 9 = 13
hash(37) = 3^2 +7 = 16
13%8  = 5
16%8 = 0 
通过对hash做优化之后,碰撞会减少;

如果是发生碰撞了:

会形成一个链表;
如果链表太长,性能会下降的比较厉害,所以会把链表变成一颗树(红黑树);

好的Hash:

规则

让N变成质数;

不要接近2^n;

不要接近10^n;

如何判断一个Hash函数是不是好的

搜【universal hash function】

支持的运算

添加;

查找;

删除;

结构:

key:value
key一般要简单;
如果key = "今天星期5,我可以去洗脚",是不好的key
如果key = [身份证],那就是好的key;
value可以写具体的内容;

hashtable的代码实现:

class HashTable:
    def __init__(self, size):
        self.size = size
        # 初始化一个具有固定大小的表格,初始时所有位置都为空
        self.table = [None] * size

    def _hash(self, key):
        # 简单的哈希函数示例,将键的哈希值映射到表格大小范围内
        # return hash(key) % self.size
        return hash(key) % self.size

    def insert(self, key, value):
        index = self._hash(key)
        if self.table[index] is None:
            # 如果该位置为空,创建一个包含键值对的列表并将其放在该位置
            self.table[index] = [(key, value)]
        else:
            # 处理哈希冲突,遍历链表找到正确的位置
            for i, (existing_key, existing_value) in enumerate(self.table[index]):
                if existing_key == key:
                    # 如果键已经存在,则更新值
                    self.table[index][i] = (key, value)
                    break
            else:
                # 如果键不存在,则将新的键值对添加到链表中
                self.table[index].append((key, value))

    def search(self, key):
        index = self._hash(key)
        if self.table[index] is not None:
            # 遍历链表查找键,找到则返回相应的值
            for existing_key, existing_value in self.table[index]:
                if existing_key == key:
                    return existing_value
        # 如果键不存在,引发KeyError
        raise KeyError(f"Key not found: {key}")

    def delete(self, key):
        index = self._hash(key)
        if self.table[index] is not None:
            # 遍历链表找到并删除键
            for i, (existing_key, existing_value) in enumerate(self.table[index]):
                if existing_key == key:
                    del self.table[index][i]
                    break
            else:
                # 如果键不存在,引发KeyError
                raise KeyError(f"Key not found: {key}")
        else:
            # 如果哈希值对应的位置为空,引发KeyError
            raise KeyError(f"Key not found: {key}")

# 使用示例
hash_table = HashTable(size=10)
hash_table.insert("name", "John")
hash_table.insert("age", 25)

print(hash_table.search("name"))  # 输出: John
print(hash_table.search("age"))   # 输出: 25

hash_table.insert("age", 26)  # 更新键"age"的值
print(hash_table.search("age"))  # 输出: 26

hash_table.delete("name")
# hash_table.search("name")  # 如果执行此行,将会引发KeyError,因为键"name"已被删除

DAY4 Python操作文件

前面的一些时间复杂度的总结:

查找:时间复杂度:O(n),n条数据查n次;
二分查找:时间复杂度:O(log(n));
排序:时间复杂度:O(nlog(n));
Heap:extract(取)时间复杂度:O(log(n)),insert(插)时间复杂度:O(log(n));
(定义:爸爸最小(小堆);爸爸最大(大堆),实现方式:数组;用树节点表示;)
B+树:查询的时间复杂度:和树的高度有关;
二叉树:插入的时间复杂度:O(1),查询的时间复杂度:O(log(n))~O(n);
(定义:左边小于爸爸,右边大于爸爸;有序的,但是有可能会退化成链表;)
红黑树:插入的时间复杂度:O(log(n)),查询的时间复杂度:O(log(n))
(一边插入,一边保持平衡(需要额外的付出一些代价)。)
图-广度BFS:时间复杂度:O(m+n),m是边的数量,边的范围 n-1 <= m <= n(n-1)/2,实现:队列,先进先出;
图-深度DFS:时间复杂度:O(m+n)实现:1. 递归;2. 队列,先进后出;
图的应用-SCC:原理:两次运行DFS,可以找到同样的类,时间复杂度:O(m+n);
图的应用-Dijkstra:原理:找最短的路径;时间复杂度:O(nm),m是边的数量;
(如果用heap做优化,可以提升性能,O(nlog(m));)
HASH:不发生碰撞的时候,会找O(1)次;发生碰撞的时候,最坏需要O(log(N))次;

①普通文件操作

读,写;
读入(read):Input;
写出(write):Output;

例1-写:

#'a'代表的是追加,如果文件存在,就直接在文件里追加,如果不存在就创建然后追加;注意,读和写的编码格式要一致,一个中文在UTF-8里面是占3个字节,在GBK里面是占两个字节。
f = open("./database.txt", mode='a', encoding="UTF-8")
f.write("哇哇哇")
#要关闭文件流,如果不关闭会占用内存。
f.close()

例2-读:

#r+:代表可读可写,注意编码要一致;
f = open("./database.txt", mode='r+', encoding="utf-8")
# print(f.read())
print(f.readline())
# print(f.readline())
# print(f.readline())
f.close()

例3-读(seek):

#seek:按照传入的值进行查找,例如seek(2),代表:1. 从第二个字符开始找;1. 从第二个字节开始找。
f = open("./database.txt", mode='r', encoding="utf-8")
f.seek(3) #f.seek(2)会报错,是因为utf-8是3个字节;如果要seek(2)的话就把模式改成'b',encoding就不需写了
print(f.readline())
f.close()

例3-读(tell)

#告诉“我”当前游标的位置
f = open("./database.txt", mode='r', encoding="utf-8")
print(f.tell())
f.seek(3) #f.seek(2)会报错,是因为utf-8是3个字节;如果要seek(2)的话就把模式改成'b',encoding就不需写了
print(f.tell())
print(f.readline())
print(f.tell())
print(f.readline())
f.close()

例4-判断一个文件是否读完

1. 第一步:算文件的长度(字节);
2. 第二步:写逻辑;
length = 7 #判断文件的字节的长度;换行符这些如果步想读到的话要去掉"replace"
f = open("./database.txt", mode='r', encoding="utf-8")
index = 0
while True:
    #print(index)
    f.seek(index)
    print(f.read(1))
    index = f.tell()
    if index >= length:
        break

例5-防呆设计

#不用手动关闭文件流
with open("database.txt", mode="r", encoding="utf-8") as f:
    lines = f.read()
print(lines)

②操作CSV文件

CSV文件介绍,中间是用【,】隔开的一个一个的字段,类似数据库

[{'username': 'zjq', 'sex': '女', 'age': '18'}, {'username': 'zjq', 'sex': '女', 'age': '18'}, {'username': 'zjq', 'sex': '女', 'age': '18'}]

实现:

users = []
with open("data.csv", mode="r", encoding="utf-8") as f:
    first = f.readline()
    keys = first.strip().split(',')
    content = f.readlines()
    for line in content:
        user_dic = {}
        userinfo = line.strip().split(",")
        for index in range(len(userinfo)):
            user_dic[keys[index]] = userinfo[index]
        users.append(user_dic)
print(users)

DAY5 Python操作数据库&Socket套接字

①Python操作数据库

试验(用前面用到的MySQL数据库信息)

查找信息

import pymysql
from pymysql.cursors import DictCursor,SSDictCursor,Cursor,SSCursor
conn = pymysql.connect(host='192.168.10.132', user='root', password='123456', database='woniusales', port=3308)
cursor = conn.cursor(cursor=DictCursor)
cursor.execute("select * from user")
users = cursor.fetchall()
print(users)
conn.close()

增加信息

def insert_user(*user):
    conn = pymysql.connect(host='192.168.100.183', user='root', password='123456', database='woniusales', port=3308, autocommit=False)
    conn.autocommit(False)
    print(conn.get_autocommit())
    cursor = conn.cursor(cursor=DictCursor)
    #user=['ykm', '12345', 'yangkaiming', '156', 'stuff']
    cursor.execute("insert into user(username, password, realname, phone, role, createtime) values(%s,%s,%s,%s, %s, now())", ('name','12345','rname','12345566778','boss'))
    conn.commit()
    #print(conn.get_autocommit())
    cursor.close()
    conn.close()
intert_user()

Mysql数据库常用引擎:InnoDB,MyIsam

InnoDB:B+树,适合做增删改查,支持事务;

MyISAM:数据会压缩,不太适合做修改,不支持事务。所以不管你的auto commit是什么值,都是自动提交。

事务的特性

新建两张表:User和Store表

新建设计表

image-20240112115734978

STORE

idbook_namestockprice
1帝王心术2100

User

idnamemoney
3张继泽300

超卖会失败

def buy_book():
    conn = pymysql.connect(host='192.168.100.183', user='root', password='123456', database='woniusales', port=3308)
    cursor = conn.cursor(cursor=DictCursor)
    cursor.execute("update book_user set money = money - 200 where id = 0")
    conn.commit()
    cursor.execute("update book_store set stock=stock-2 where id=2")
    conn.commit()
    cursor.close()
    conn.close()

#一般可以这样写:
def cost_money():
    conn = pymysql.connect(host='192.168.100.183', user='root', password='123456', database='woniusales', port=3308)
    cursor = conn.cursor(cursor=DictCursor)
    cursor.execute("update book_user set money = money - 200 where id = 0")
    conn.commit()
def decr_stock():
    conn = pymysql.connect(host='192.168.100.183', user='root', password='123456', database='woniusales', port=3308)
    cursor = conn.cursor(cursor=DictCursor)
    cursor.execute("update book_store set stock=stock-2 where id=2")
    conn.commit()
def buy_sth():
    cost_money()
    1/0
    decr_stock()

②Socket套接字

简介

socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。Socket就是该模式的一个实现, socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭). 说白了Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

是进程间通信的一种方式,它与其他进程间通信的一个主要不同是:它能实现不同主机间的进程间通信,我们网络上各种各样的服务大多数是基于socket来完成通信的。socket是基于C/S架构的,也就是说socket网络编程,通常需要写两个文件,一个服务端,一个客户端。

版本-1(客户端单向发送)

服务器端代码:

import socket
mySocket = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
mySocket.bind(('127.0.0.1', 8080))
mySocket.listen()
#这一步会阻塞代码
sk, addr = mySocket.accept()
byte_msg = sk.recv(1024)
msg = byte_msg.decode('utf-8')
print(msg)

客户端代码:

import socket
mySocket = socket.socket()
mySocket.connect(('127.0.0.1',8080))
msg="你是谁?"
mySocket.send(msg.encode('utf-8'))
byte_recv = mySocket.recv(1024)
print(byte_recv.decode('utf-8'))

版本-2(客户端和服务器互相单次通信)

服务器代码:

import socket
mySocket = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
mySocket.bind(('127.0.0.1', 8080))
mySocket.listen()
#这一步会阻塞代码
sk, addr = mySocket.accept()
byte_msg = sk.recv(1024)
msg = byte_msg.decode('utf-8')
print(msg)
sk.send(("我是服务器").encode('utf-8'))

客户端:

import socket
mySocket = socket.socket()
mySocket.connect(('127.0.0.1',8080))
msg="你是谁?"
mySocket.send(msg.encode('utf-8'))
byte_recv = mySocket.recv(1024)
print(byte_recv.decode('utf-8')

版本-3(客户端和服务器相互发信息)

服务器

import socket
mySocket = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
mySocket.bind(('127.0.0.1', 8080))
mySocket.listen()
#这一步会阻塞代码
while True:
    sk, addr = mySocket.accept()
    byte_msg = sk.recv(1024)
    msg = byte_msg.decode('utf-8')
    print(msg)
    toClient = input("服务器发送:")
    sk.send(toClient.encode('utf-8'))

客户端:

import socket
while True:
    mySocket = socket.socket()
    mySocket.connect(('127.0.0.1', 8080))
    msg = input("客户端发送:")
    mySocket.send(msg.encode('utf-8'))
    byte_recv = mySocket.recv(1024)
    print(byte_recv.decode('utf-8'))

版本-4(BS架构)

服务器代码

import socket
mySocket = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
mySocket.bind(('127.0.0.1', 8080))
mySocket.listen()
#这一步会阻塞代码
while True:
    sk, addr = mySocket.accept()
    byte_msg = sk.recv(1024)
    msg = byte_msg.decode('utf-8')
    print(msg)
    content = "<html><head></head><body><div style=\"color:red\">welcome</div></body>   </html>"
    length = len(content)
    resp = "HTTP/1.1 200 File Contents\r\n" + "Content-type: text/html\r\n" + "Content-length: " + str(length) + "\r\n" + "\r\n" + content
    sk.send(bytes(resp, 'utf-8'))

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值