"""
模块:python3 inputOutput_open_pickle 输入和输出.py
功能:python3 输入和输出。
参考:https://www.runoob.com/python3/python3-inputoutput.html
知识点:
    1.使用 str.format() 函数来格式化输出值。
        str():函数返回一个用户易读的表达形式。
        repr():产生一个解释器易读的表达形式。

    2.pickle.dump(obj, file, protocol=None, *, fix_imports=True)
        将 obj 的腌渍表示写入打开的文件对象 file。
        这相当于 ``Pickler(file, protocol).dump(obj)``, 但可能更高效。
    
        可选参数 *protocol*  指定协议,可选的协议如下:0, 1, 2, 3 and 4.
        默认协议是 3; 为 Python 3 设计的向后不兼容的协议。
    
        指定一个负的协议版本,意味着选择可支持的最高版本。
        使用的协议版本越高, 需要的 python 版本也越高 来读 pickle 生成的文件。
    
        *file* 参数必需有一个 write() 方法,该方法接受一个字节参数。
        因此,它可以是为二进制编写而打开的文件对象,一个 io.BytesIO 实例,
        或满足此接口的任何其他自定义对象。
    
        如果 *fix_imports* 是 True and protocol is less than 3, pickle will try
        to map the new Python 3 names to the old module names used in Python
        2, so that the pickle data stream is readable with Python 2.
    
    3.pickle.load(file, *, fix_imports=True, encoding='ASCII', errors='strict')
        从存储在文件中的泡菜数据中读取并返回对象。
    
        这相当于 ``Unpickler(file).load()``, 但可能更高效。
    
        pickle 的协议版本是自动检测的, 所以不需要 protocol 参数。
        超过 pickled 对象的表现形式的 字节 将被忽略。
    
        参数 *file* 必需有两个方法,一个 read() 方法,该方法需要一个 整型参数,
        和一个 readline() 方法,该方法不需参数。
        两个方法都需要返回 bytes。因此 *file* 可以是为读取打开的二进制文件对象,
        一个 io.BytesIO 对象, 或者任何满足这个接口的自定义对象。
    
        可选的关键字参数是 *fix_imports*, *encoding* and *errors*,
        它们用于控制 python2 生成的泡菜流的兼容性支持。
        如果 *fix_imports* is True, pickle will try to
        map the old Python 2 names to the new names used in Python 3.
        The
        *encoding* and *errors* tell pickle how to decode 8-bit string
        instances pickled by Python 2; these default to 'ASCII' and 'strict',
        respectively.  The *encoding* can be 'bytes' to read these 8-bit
        string instances as bytes objects.
"""
# 1.str() vs repr()。
s = 'Hello, Runoob'
print("s:", str(s))
# s: Hello, Runoob
print("s:", repr(s))
# s: 'Hello, Runoob'

s = 'hello, runoob\n'
print("s:", str(s))
# s: hello, runoob
#
# repr() 函数可以转义字符串中的特殊字符。
print("s:", repr(s))
# s: 'hello, runoob\n'

# for x in range(1, 11):
#     print(repr(x).rjust(2), repr(x*x).rjust(3), end=' ')
#     # 注意前一行 'end' 的使用
#     print(repr(x*x*x).rjust(4))

# str.rjust(width[, fillchar])
# rjust() 返回一个原字符串右对齐,并使用空格填充至长度 width 的新字符串。
# 如果指定的长度小于字符串的长度则返回原字符串。
# width -- 指定填充指定字符后中字符串的总长度.
# fillchar -- 填充的字符,默认为空格。
for x in range(1, 11):
    print(str(x).rjust(2), str(x * x).rjust(3), end=' ')
    # 注意前一行 'end' 的使用
    print(str(x * x * x).rjust(4))
#  1   1    1
#  2   4    8
#  3   9   27
#  4  16   64
#  5  25  125
#  6  36  216
#  7  49  343
#  8  64  512
#  9  81  729
# 10 100 1000



# 3.str.zfill() 方法, 它会在数字的左边填充 0。
print("3:")
print('12'.zfill(5))  # 00012
print('-3.14'.zfill(7))  # -003.14
print('3.14159265359'.zfill(5))  # 3.14159265359

# 4.旧式字符串格式化
# % 操作符也可以实现字符串格式化。
print("4:")
import math

print('常量 PI 的值近似为:%5.3f。' % math.pi)
# 常量 PI 的值近似为:3.142。
# 注:
# 因为 str.format() 是比较新的函数,
# 大多数的 Python 代码仍然使用 % 操作符。
# 但是这种旧式的格式化最终会从该语言中移除,
# 应该更多的使用 str.format().

# 5.读取键盘输入
# input 可以接收一个Python表达式作为输入,并将运算结果返回。
# s = input("请输入:")
# print("你输入的内容是: ", s)

# 6.读和写文件
# open() 将会返回一个 file 对象
# open(filename, mode)
# filename:包含了你要访问的文件名称的字符串值。
# mode:决定了打开文件的模式:只读,写入,追加等。
# 默认文件访问模式为只读(r)。
# 打开一个文件
f = open("./foo.txt", mode="w", encoding='utf-8')
f.write("Python 是一个非常好的语言。\n是的,的确非常好!!\n")
# 关闭打开的文件
f.close()

# 7.文件对象的方法
# f.read()
# 为了读取一个文件的内容,调用 f.read(size),
# 这将读取一定数目的数据, 然后作为字符串或字节对象返回。
# size 是一个可选的数字类型的参数。
# 当 size 被忽略了或者为负, 那么该文件的所有内容都将被读取并且返回。
print("7:")
# 打开一个文件
f = open("./foo.txt", "r", encoding='utf-8')
s = f.read()
print(s)
# Python 是一个非常好的语言。
# 是的,的确非常好!!
# 关闭打开的文件
f.close()

# 8.f.readline()
# f.readline() 会从文件中读取单独的一行。换行符为 '\n'。
# f.readline() 如果返回一个空字符串, 说明已经读取到最后一行。
print("8:")
f = open("./foo.txt", "r", encoding='utf-8')
line = f.readline()
print(line)
# Python 是一个非常好的语言。
# 关闭打开的文件
f.close()

# 9.f.readlines()
# f.readlines() 将返回该文件中包含的所有行。
# 如果设置可选参数 sizehint, 则读取指定长度的字节, 并且将这些字节按行分割。
print("9:")
f = open("./foo.txt", "r", encoding='utf-8')
lines = f.readlines()
print(lines)
# ['Python 是一个非常好的语言。\n', '是的,的确非常好!!\n']
# 关闭打开的文件
f.close()

# 10.另一种方式是迭代一个文件对象然后读取每行:
print("10:")
f = open("./foo.txt", "r", encoding='utf-8')
# print("f:", f)
# f: <_io.TextIOWrapper name='./foo.txt' mode='r' encoding='utf-8'>
for line in f:
    print(line, end='')
# Python 是一个非常好的语言。
# 是的,的确非常好!!
# 关闭打开的文件
f.close()

# 11.f.write()
# f.write(string) 将 string 写入到文件中, 然后返回写入的字符数。
print("11:")
f = open("./foo.txt", "w", encoding='utf-8')
num = f.write("Python 是一个非常好的语言。\n是的,的确非常好!!\n")
print(num)  # 29
# 关闭打开的文件
f.close()

# 12.如果要写入一些不是字符串的东西, 那么将需要先进行转换:
f = open("./foo1.txt", "w")
value = ('www.runoob.com', 14)
s = str(value)
f.write(s)

# 关闭打开的文件
f.close()

# 13.f.tell()
# f.tell() 返回文件对象当前所处的位置, 它是从文件开头开始算起的字节数。

# f.seek()
# 如果要改变文件当前的位置, 可以使用 f.seek(offset, from_what) 函数。
# from_what 的值, 如果是 0 表示开头, 如果是 1 表示当前位置, 2 表示文件的结尾,例如:
# seek(x,0) : 从起始位置即文件首行首字符开始移动 x 个字符
# seek(x,1) : 表示从当前位置往后移动x个字符
# seek(-x,2):表示从文件的结尾往前移动x个字符
# from_what 值为默认为0,即文件开头。下面给出一个完整的例子:
#
# >>> f = open('/tmp/foo.txt', 'rb+')
# >>> f.write(b'0123456789abcdef')
# 16
# >>> f.seek(5)     # 移动到文件的第六个字节
# 5
# >>> f.read(1)
# b'5'
# >>> f.seek(-3, 2) # 移动到文件的倒数第三字节
# 13
# >>> f.read(1)
# b'd'

# 当处理一个文件对象时, 使用 with 关键字是非常好的方式。
# 在结束后, 它会帮你正确的关闭文件。
# 而且写起来也比 try - finally 语句块要简短:
# with open('/tmp/foo.txt', 'r') as f:
#     read_data = f.read()
# f.closed
# True


# 14.pickle 模块
# python的pickle模块实现了基本的数据序列和反序列化。
# 通过pickle模块的序列化操作我们能够将程序中运行的对象信息保存到文件中去,永久存储。
# 通过pickle模块的反序列化操作,我们能够从文件中创建上一次程序保存的对象。
# 基本接口:
# pickle.dump(obj, file, [,protocol])
# x = pickle.load(file)
print("14:")
# 14.1.pickle.dump()
import pickle

# 使用pickle模块将数据对象保存到文件
data1 = {
    'a': [1, 2.0, 3, 4 + 6j],
    'b': ('string', u'Unicode string'),
    'c': None
}
# print("data1:", data1)
selfref_list = [1, 2, 3]
# print(id(selfref_list))
# selfref_list.append(selfref_list)
# print("selfref_list:", selfref_list)
output = open('data.pkl', 'wb')

# Pickle dictionary using protocol 0.
pickle.dump(data1, output)

# Pickle the list using the highest protocol available.
pickle.dump(selfref_list, output, -1)

output.close()

# 14.2 pickle.load()
import pprint, pickle

# 使用pickle模块从文件中重构python对象
pkl_file = open('data.pkl', 'rb')

data1 = pickle.load(pkl_file)
pprint.pprint(data1)
# {'a': [1, 2.0, 3, (4+6j)], 'b': ('string', 'Unicode string'), 'c': None}
print(type(data1))
# <class 'dict'>

data2 = pickle.load(pkl_file)
pprint.pprint(data2)
# [1, 2, 3]
print(type(data2))
# <class 'list'>
pkl_file.close()