Python 基础练习3

用户输入——示例

请把最后一行 run_adder 从注释拿出来,这样才能运行这段代码。

In [1]:
# 我们定义一个加法计算器
def run_adder():
    total = 0
    while True:
        user_input = input("请输入下一个数。输入 'stop' 停止。")
        if user_input == "stop":
            break           # break 语句会退出当前的循环(如果是多层循环的话只会跳出一层循环)
        x = float(user_input) # 这一句可能会导致问题,比如当用户的输入包含字母
        total = total + x
        
    print(total)
    
# run_adder()

你会发现,好不容易输入了一大堆数字,一不小心输入一个字母,程序就崩溃,之前累加的数值就全白费了。

所以需要在 float(user_input) 这一句之前,判断一下用户输入到底能不能被转换成数字。

正则表达式——示例

注意,我们会用 \ 来对一些特殊字符进行 转义,来去掉它们的特殊含义。
比如 . 在表达式中是 通配符 的意思,会匹配任何字符。加上斜杠后,就退化成字面上的 .

In [2]:
import re

third_decimals = ["3.142", "123.456", "125135316235623.123", "0.456"] # 精确到三位小数的数字
non_third_decimals = ["12", "1.234567", "abc", "123a.133", "ba12"] # 所有其它的字符串

expression = "^[0-9]*\.[0-9]{3}$" # 注意 '*' 后面的 '\.'这个斜杠是必要的,因为 `.` 是一个有特殊含义的字符,就和 `[` 一样
                                  # 不是真的去匹配 `[` 这个字符。这种有特殊含义的字符前面需要加上斜杠来进行 `转义`。
for string in third_decimals:
    print(bool(re.search(expression, string)))
print("以上全部为 True")
    
for string in non_third_decimals:
    print(bool(re.search(expression, string)))
print("以上全部为 False")
True
True
True
True
以上全部为 True
False
False
False
False
False
以上全部为 False

请把最后一行 run_adder_safe 从注释拿出来,这样才能运行这段代码。

In [3]:
# 我们定义一个安全的加法计算器

integer_expression = "^[0-9]{1,}$"

def run_adder_safe():
    total = 0
    while True:
        user_input = input("请输入下一个数。输入 'stop' 停止。")
        if user_input == "stop":
            break
        
        if not re.search(integer_expression, user_input):
            print("输入不合法!")
            continue    # continue 可以直接结束当前这一轮的循环,进入下一轮。
                        # 因为输入不合法,所以直接重新来一轮循环,从而跳过了下面那句会出错的句子。
        x = float(user_input)
        total = total + x
        print("当前总和: " + str(total))
        
    print(total)
    
# run_adder_safe()

正则表达式——练习

In [4]:
valid_sort_code = ["24-12-45", "55-34-12", "00-00-11", "55-66-77"] # 合法的 sort code(英国银行卡上用的)
invalid_sort_code = ["aa-22-33", "11 22 33", "15t23g", "314159", "a1-b2-c3"] # 所有其它的字符串

expression = "" # 在这里写表达式!
 
for string in valid_sort_code:
    print(bool(re.search(expression, string)))
print("以上全部为 True")
    
for string in invalid_sort_code:
    print(bool(re.search(expression, string)))
print("以上全部为 False")
True
True
True
True
以上全部为 True
True
True
True
True
True
以上全部为 False

更多练习请点击这里

用户输入和正则匹配——练习

In [5]:
# 先读取用户输入数字,再读取 '+', '-', '*', '/' 四种字符中的一个
# 然后像计算器那样进行运算,直到用户输入 `stop`
# 注意使用正则表达式检查用户输入是否合法
# 如果觉得困难,就只允许整数。喜欢挑战的话可以尝试一下匹配浮点数。

import re

def run_calculator():
    while None:
        return None
    
def isValidNumber(user_input):
    return False
    
def isValidOp(user_input):
    return False

run_calculator()

字典——示例

In [22]:
x = dict()
x["abc"] = 10
x["bcd"] = 20
x["cde"] = 30

print(x.keys())
print(x.values())
print(x.items())
dict_keys(['abc', 'bcd', 'cde'])
dict_values([10, 20, 30])
dict_items([('abc', 10), ('bcd', 20), ('cde', 30)])
In [23]:
x["abc"] = 100
print(x.values())
dict_values([100, 20, 30])
In [26]:
print(20 in x.values())
print(200 in x.values())
True
False
In [28]:
print("abcd" in x.keys())
False

文本处理——示例

我们先下载一下《红楼梦》这本书。下面的代码之后要求大家都会写,现在可以先跳过。

In [6]:
# 这一段代码请复制到本地运行,拿到我们需要处理的文本。
# 有兴趣的同学可以研究一下它做了啥。
# 不想看的同学可以先跳过。
# 不过这种代码之后要求会写的。

# 总之就是自动从服务器下载了一个 txt 文件。

import requests
import os

dir_path = 'txt_files'
file_name = 'hongloumeng.txt'
full_path = os.path.join(dir_path, file_name)
if not os.path.exists(dir_path):
    os.mkdir(dir_path)

if not os.path.exists(full_path):
    url = "http://icewould.com/m5-101/hongloumeng"
    print("正在下载文件...")
    result = requests.get(url)
    with open(full_path, "wb") as f:
        f.write(result.content)
        print("下载完成")
In [7]:
# 读取文件
lines = None
with open('./txt_files/hongloumeng.txt', 'r', encoding = 'utf-8') as f:
    lines = f.readlines()
In [8]:
print(lines[:3]) # 看一下前 3 个段落
print()
print("全书一共 " + str(len(lines)) + " 个段落") # 看一下全书一共有多少段落
content = "".join(lines) # lines 是一个数组,每一个元素是一个段落。现在把它完整拼起来,成为一整个字符串,存放在 content 变量里。
print("全书一共 " + str(len(content)) + " 个字符")
['\ufeff 第一回\u3000甄士隐梦幻识通灵\u3000贾雨村风尘怀闺秀\n', '\n', '    此开卷第一回也。作者自云:因曾历过一番梦幻之后,故将真事隐去,而借“通灵”之说,撰此《石头记》一书也。故曰“甄士隐”云云。但书中所记何事何人?自又云:“今风尘碌碌,一事无成,忽念及当日所有之女子,一一细考较去,觉其行止见识皆出于我之上。何我堂堂须眉,诚不若彼裙钗哉?实愧则有馀,悔又无益之大无可如何之日也!当此,则自欲将已往所赖天恩祖德,锦衣纨袴之时,饫甘餍肥之日,背父兄教育之恩,负师友规训之德,以至今日一技无成,半生潦倒之罪,编述一集,以告天下人:我之罪固不免,然闺阁中本自历历有人,万不可因我之不肖,自护己短,一并使其泯灭也。虽今日之茅椽蓬牖,瓦灶绳床,其晨夕风露,阶柳庭花,亦未有妨我之襟怀笔墨者。虽我未学,下笔无文,又何妨用假语村言,敷演出一段故事来,亦可使闺阁昭传,复可悦世之目,破人愁闷,不亦宜乎?”故曰“贾雨村云云。\n']

全书一共 6233 个段落
全书一共 882432 个字符

通过字典,我们来统计一下,每一个字出现的次数。
大家可以把最后的 print 从注释里拿出来,打印一下看看。

In [9]:
counter = dict()
for character in content:
    if not character in counter.keys():
        counter[character] = 1
    else:
        counter[character] = counter[character] + 1
# print(counter)

其实,可以直接使用 Python 自带的 Counter 进行统计,但是为了让大家熟练掌握 Dictionary 的用法,还是建议大家先自己手写。

然后,我们看看《红楼梦》中,贾xx 或者 贾x 字符串共出现了多少次。

In [10]:
import re
expression = "贾[\u4e00-\u9fa5]{1,2}"
result = re.findall(expression, content)
print(len(result))
5113

好奇它们都是啥么?我们打印出前几个看一下。

In [11]:
print(result[:100])
['贾雨村', '贾雨村', '贾名化', '贾雨村', '贾雨村', '贾爷今', '贾夫人', '贾名化', '贾氏生', '贾氏夫', '贾府中', '贾复以', '贾代化', '贾敷', '贾敬袭', '贾珍', '贾蓉', '贾代善', '贾赦', '贾政', '贾赦袭', '贾政', '贾珠', '贾府就', '贾府中', '贾府亦', '贾敏', '贾琏', '贾雨村', '贾政', '贾政已', '贾政最', '贾政', '贾赦贾', '贾母一', '贾母又', '贾母又', '贾母道', '贾母笑', '贾赦之', '贾琏', '贾母身', '贾母笑', '贾母命', '贾赦之', '贾母笑', '贾赦', '贾母处', '贾源', '贾政之', '贾母的', '贾珠之', '贾母正', '贾母笑', '贾母命', '贾母便', '贾母因', '贾母道', '贾母请', '贾母便', '贾母因', '贾母笑', '贾母笑', '贾母急', '贾母忙', '贾母说', '贾母想', '贾母见', '贾母之', '贾母因', '贾母', '贾母时', '贾母', '贾母', '贾珠之', '贾兰', '贾不假', '贾府王', '贾府之', '贾府王', '贾政并', '贾政的', '贾家姨', '贾雨村', '贾母', '贾政', '贾琏又', '贾赦', '贾珍等', '贾政便', '贾母也', '贾母闲', '贾宅居', '贾宅族', '贾政训', '贾珍', '贾母万', '贾母一', '贾珍之', '贾母']

注意,这些不都是人名哦,比如 贾政便
要提取出姓贾的人名,可能需要一番功夫了。

此外,我们可以把重复的删去。这里要用到 set 这个数据结构,有兴趣的同学可以去搜一下 python setset 其实就是数学里的 集合,它不允许有重复的元素。

In [12]:
print(set(result[:100]))
{'贾夫人', '贾府就', '贾母因', '贾兰', '贾珍等', '贾政训', '贾母笑', '贾珠之', '贾政并', '贾赦袭', '贾爷今', '贾母处', '贾政的', '贾代善', '贾母正', '贾宅族', '贾政之', '贾赦', '贾赦之', '贾政', '贾母说', '贾府之', '贾敷', '贾母一', '贾母闲', '贾母的', '贾母身', '贾母急', '贾敏', '贾敬袭', '贾政已', '贾宅居', '贾氏夫', '贾名化', '贾氏生', '贾代化', '贾母想', '贾母便', '贾琏又', '贾家姨', '贾珠', '贾政最', '贾母', '贾母万', '贾府亦', '贾珍之', '贾府王', '贾政便', '贾母之', '贾赦贾', '贾母命', '贾母又', '贾母忙', '贾雨村', '贾珍', '贾府中', '贾琏', '贾母请', '贾母时', '贾复以', '贾蓉', '贾母也', '贾源', '贾母见', '贾母道', '贾不假'}

一共有多少个不重复的呢?

In [13]:
print(len(set(result)))
1471

文本处理——练习

练习1

请把我们的《红楼梦》文本中所有不是汉字的东西去掉

匹配所有汉字的正则表达式是:
[^\u4e00-\u9fa5]

匹配所有汉字的正则表达式是:
[\u4e00-\u9fa5]

请先不要看以下代码,自己尝试一下。提示,使用 re.sub 函数以及 ""(空字符串)来替换,达到删除的效果。

In [14]:
# 请遮住以下代码,自己先写

import re
def clear_content(content):
    expression = '[^\u4e00-\u9fa5]'
    result = re.sub(expression, "", content)
    return result

result = clear_content(content)
print(result[:300]) # 展示处理完毕后的前 300 个字
第一回甄士隐梦幻识通灵贾雨村风尘怀闺秀此开卷第一回也作者自云因曾历过一番梦幻之后故将真事隐去而借通灵之说撰此石头记一书也故曰甄士隐云云但书中所记何事何人自又云今风尘碌碌一事无成忽念及当日所有之女子一一细考较去觉其行止见识皆出于我之上何我堂堂须眉诚不若彼裙钗哉实愧则有馀悔又无益之大无可如何之日也当此则自欲将已往所赖天恩祖德锦衣纨袴之时饫甘餍肥之日背父兄教育之恩负师友规训之德以至今日一技无成半生潦倒之罪编述一集以告天下人我之罪固不免然闺阁中本自历历有人万不可因我之不肖自护己短一并使其泯灭也虽今日之茅椽蓬牖瓦灶绳床其晨夕风露阶柳庭花亦未有妨我之襟怀笔墨者虽我未学下笔无文又何妨用假语村言敷演出一段故事

练习2

请统计《红楼梦》中出现次数最多的汉字(注意,请先把标点符号去除掉)

练习 3

请统计《红楼梦》中出现次数最多的前三个汉字。

练习 4

请统计《红楼梦》中,黛玉与宝玉两个人名在相隔 20 字内同时出现的次数。