范文健康探索娱乐情感热点
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

Go语言核心36讲(Go语言实战与应用二十一)学习笔记

  43 | bufio包中的数据类型(下)
  在上一篇文章中,我提到了bufio包中的数据类型主要有Reader、Scanner、Writer和ReadWriter。并着重讲到了bufio.Reader类型与bufio.Writer类型,今天,我们继续专注bufio.Reader的内容来进行学习。 知识扩展问题 :bufio.Reader类型读取方法有哪些不同?
  bufio.Reader类型拥有很多用于读取数据的指针方法, 这里面有 4 个方法可以作为不同读取流程的代表,它们是:Peek、Read、ReadSlice和ReadBytes。
  Reader值的Peek方法 的功能是:读取并返回其缓冲区中的n个未读字节,并且它会从已读计数代表的索引位置开始读。
  在缓冲区未被填满,并且其中的未读字节的数量小于n的时候,该方法就会调用fill方法,以启动缓冲区填充流程。但是,如果它发现上次填充缓冲区的时候有错误,那就不会再次填充。
  如果调用方给定的n比缓冲区的长度还要大,或者缓冲区中未读字节的数量小于n,那么Peek方法就会把"所有未读字节组成的序列"作为第一个结果值返回。
  同时,它通常还把"bufio.ErrBufferFull变量的值(以下简称缓冲区已满的错误)"
  作为第二个结果值返回,用来表示:虽然缓冲区被压缩和填满了,但是仍然满足不了要求。
  只有在上述的情况都没有出现时,Peek方法才能返回:"以已读计数为起始的n个字节"和"表示未发生任何错误的nil"。
  bufio.Reader类型的 Peek 方法有一个鲜明的特点,那就是:即使它读取了缓冲区中的数据,也不会更改已读计数的值。
  这个类型的其他读取方法并不是这样。就拿 该类型的Read方法来说 ,它有时会把缓冲区中的未读字节,依次拷贝到其参数p代表的字节切片中,并立即根据实际拷贝的字节数增加已读计数的值。 在缓冲区中还有未读字节的情况下,该方法的做法就是如此。不过,在另一些时候,其所属值的已读计数会等于已写计数,这表明:此时的缓冲区中已经没有任何未读的字节了。 当缓冲区中已无未读字节时,Read方法会先检查参数p的长度是否大于或等于缓冲区的长度。如果是,那么Read方法会索性放弃向缓冲区中填充数据,转而直接从其底层读取器中读出数据并拷贝到p中。这意味着它完全跨过了缓冲区,并直连了数据供需的双方。
  需要注意的是,Peek方法在遇到类似情况时的做法与这里的区别(这两种做法孰优孰劣还要看具体的使用场景)。
  Peek方法会在条件满足时填充缓冲区,并在发现参数n的值比缓冲区的长度更大时,直接返回缓冲区中的所有未读字节。
  如果我们当初设定的缓冲区长度很大,那么在这种情况下的方法执行耗时,就有可能会比较长。最主要的原因是填充缓冲区需要花费较长的时间。
  由fill方法执行的流程可知,它会尽量填满缓冲区中的可写空间。然而,Read方法在大多数的情况下,是不会向缓冲区中写入数据的,尤其是在前面描述的那种情况下,即:缓冲区中已无未读字节,且参数p的长度大于或等于缓冲区的长度。
  此时,该方法会直接从底层读取器那里读出数据,所以数据的读出速度就成为了这种情况下方法执行耗时的决定性因素。
  当然了,我在这里说的只是耗时操作在某些情况下更可能出现在哪里,一切的结论还是要以性能测试的客观结果为准。
  说回Read方法的内部流程。如果缓冲区中已无未读字节,但其长度比参数p的长度更大,那么该方法会先把已读计数和已写计数的值都重置为0,然后再尝试着使用从底层读取器那里获取的数据,对缓冲区进行一次从头至尾的填充。
  不过要注意,这里的尝试只会进行一次。无论在这一时刻是否能够获取到数据,也无论获取时是否有错误发生,都会是如此。而fill方法的做法与此不同,只要没有发生错误,它就会进行多次尝试,因此它真正获取到一些数据的可能性更大。
  不过,这两个方法有一点是相同,那就是:只要它们把获取到的数据写入缓冲区,就会及时地更新已写计数的值。
  再来说ReadSlice方法和ReadBytes方法。  这两个方法的功能总体上来说,都是持续地读取数据,直至遇到调用方给定的分隔符为止。
  ReadSlice方法 会先在其缓冲区的未读部分中寻找分隔符。如果未能找到,并且缓冲区未满,那么该方法会先通过调用fill方法对缓冲区进行填充,然后再次寻找,如此往复。
  如果在填充的过程中发生了错误,那么它会把缓冲区中的未读部分作为结果返回,同时返回相应的错误值。
  注意,在这个过程中有可能会出现虽然缓冲区已被填满,但仍然没能找到分隔符的情况。
  这时,ReadSlice方法会把整个缓冲区(也就是buf字段代表的字节切片)作为第一个结果值,并把缓冲区已满的错误(即bufio.ErrBufferFull变量的值)作为第二个结果值。
  经过fill方法填满的缓冲区肯定从头至尾都只包含了未读的字节,所以这样做是合理的。
  当然了,一旦ReadSlice方法找到了分隔符,它就会在缓冲区上切出相应的、包含分隔符的字节切片,并把该切片作为结果值返回。无论分隔符找到与否,该方法都会正确地设置已读计数的值。
  比如,在返回缓冲区中的所有未读字节,或者代表全部缓冲区的字节切片之前,它会把已写计数的值赋给已读计数,以表明缓冲区中已无未读字节。
  如果说ReadSlice是一个容易半途而废的方法的话,那么可以说ReadBytes方法算得上是相当的执着。
  ReadBytes方法 会通过调用ReadSlice方法一次又一次地从缓冲区中读取数据,直至找到分隔符为止。
  在这个过程中,ReadSlice方法可能会因缓冲区已满而返回所有已读到的字节和相应的错误值,但ReadBytes方法总是会忽略掉这样的错误,并再次调用ReadSlice方法,这使得后者会继续填充缓冲区并在其中寻找分隔符。
  除非ReadSlice方法返回的错误值并不代表缓冲区已满的错误,或者它找到了分隔符,否则这一过程永远不会结束。
  如果寻找的过程结束了,不管是不是因为找到了分隔符,ReadBytes方法都会把在这个过程中读到的所有字节,按照读取的先后顺序组装成一个字节切片,并把它作为第一个结果值。如果过程结束是因为出现错误,那么它还会把拿到的错误值作为第二个结果值。
  在bufio.Reader类型的众多读取方法中,依赖ReadSlice方法的除了ReadBytes方法,还有ReadLine方法。不过后者在读取流程上并没有什么特别之处,我就不在这里赘述了。
  另外,该类型的ReadString方法完全依赖于ReadBytes方法,前者只是在后者返回的结果值之上做了一个简单的类型转换而已。
  最后,我还要提醒你一下,有个安全性方面的问题需要你注意。bufio.Reader类型的Peek方法、ReadSlice方法和ReadLine方法都有可能会造成内容泄露。
  这主要是因为它们在正常的情况下都会返回直接基于缓冲区的字节切片。我在讲bytes.Buffer类型的时候解释过什么叫内容泄露。你可以返回查看。
  调用方可以通过这些方法返回的结果值访问到缓冲区的其他部分,甚至修改缓冲区中的内容。这通常都是很危险的。 package main  import ( 	"bufio" 	"fmt" 	"strings" )  func main() { 	comment := "Package bufio implements buffered I/O. " + 		"It wraps an io.Reader or io.Writer object, " + 		"creating another object (Reader or Writer) that " + 		"also implements the interface but provides buffering and " + 		"some help for textual I/O." 	basicReader := strings.NewReader(comment) 	fmt.Printf("The size of basic reader: %d ", basicReader.Size())  	size := 300 	fmt.Printf("New a buffered reader with size %d ... ", size) 	reader1 := bufio.NewReaderSize(basicReader, size) 	fmt.Println()  	fmt.Print("[ About "Peek" method ]  ") 	// 示例1。 	peekNum := 38 	fmt.Printf("Peek %d bytes ... ", peekNum) 	bytes, err := reader1.Peek(peekNum) 	if err != nil { 		fmt.Printf("error: %v ", err) 	} 	fmt.Printf("Peeked contents(%d): %q ", len(bytes), bytes) 	fmt.Printf("The number of unread bytes in the buffer: %d ", reader1.Buffered()) 	fmt.Println()  	fmt.Print("[ About "Read" method ]  ") 	// 示例2。 	readNum := 38 	buf1 := make([]byte, readNum) 	fmt.Printf("Read %d bytes ... ", readNum) 	n, err := reader1.Read(buf1) 	if err != nil { 		fmt.Printf("error: %v ", err) 	} 	fmt.Printf("Read contents(%d): %q ", n, buf1) 	fmt.Printf("The number of unread bytes in the buffer: %d ", reader1.Buffered()) 	fmt.Println()  	fmt.Print("[ About "ReadSlice" method ]  ") 	// 示例3。 	fmt.Println("Reset the basic reader ...") 	basicReader.Reset(comment) 	fmt.Println("Reset the buffered reader ...") 	reader1.Reset(basicReader) 	fmt.Println()  	delimiter := byte("(") 	fmt.Printf("Read slice with delimiter %q... ", delimiter) 	line, err := reader1.ReadSlice(delimiter) 	if err != nil { 		fmt.Printf("error: %v ", err) 	} 	fmt.Printf("Read contents(%d): %q ", len(line), line) 	fmt.Printf("The number of unread bytes in the buffer: %d ", reader1.Buffered()) 	fmt.Println()  	delimiter = byte("[") 	fmt.Printf("Read slice with delimiter %q... ", delimiter) 	line, err = reader1.ReadSlice(delimiter) 	if err != nil { 		fmt.Printf("error: %v ", err) 	} 	fmt.Printf("Read contents(%d): %q ", len(line), line) 	fmt.Printf("The number of unread bytes in the buffer: %d ", reader1.Buffered()) 	fmt.Println()  	// 示例4。 	fmt.Println("Reset the basic reader ...") 	basicReader.Reset(comment) 	size = 200 	fmt.Printf("New a buffered reader with size %d ... ", size) 	reader2 := bufio.NewReaderSize(basicReader, size) 	fmt.Println()  	delimiter = byte("[") 	fmt.Printf("Read slice with delimiter %q... ", delimiter) 	line, err = reader2.ReadSlice(delimiter) 	if err != nil { 		fmt.Printf("error: %v ", err) 	} 	fmt.Printf("Read contents(%d): %q ", len(line), line) 	fmt.Printf("The number of unread bytes in the buffer: %d ", reader2.Buffered()) 	fmt.Println()  	fmt.Print("[ About "ReadBytes" method ]  ") 	// 示例5。 	fmt.Println("Reset the basic reader ...") 	basicReader.Reset(comment) 	size = 200 	fmt.Printf("New a buffered reader with size %d ... ", size) 	reader3 := bufio.NewReaderSize(basicReader, size) 	fmt.Println()  	delimiter = byte("[") 	fmt.Printf("Read bytes with delimiter %q... ", delimiter) 	line, err = reader3.ReadBytes(delimiter) 	if err != nil { 		fmt.Printf("error: %v ", err) 	} 	fmt.Printf("Read contents(%d): %q ", len(line), line) 	fmt.Printf("The number of unread bytes in the buffer: %d ", reader3.Buffered()) 	fmt.Println()  	// 示例6和示例7。 	fmt.Print("[ About contents leak ]  ") 	showContentsLeak(comment) }  func showContentsLeak(comment string) { 	// 示例6。 	basicReader := strings.NewReader(comment) 	fmt.Printf("The size of basic reader: %d ", basicReader.Size())  	size := len(comment) 	fmt.Printf("New a buffered reader with size %d ... ", size) 	reader4 := bufio.NewReaderSize(basicReader, size) 	fmt.Println()  	peekNum := 7 	fmt.Printf("Peek %d bytes ... ", peekNum) 	bytes, err := reader4.Peek(peekNum) 	if err != nil { 		fmt.Printf("error: %v ", err) 	} 	fmt.Printf("Peeked contents(%d): %q ", len(bytes), bytes) 	fmt.Println()  	// 只要扩充一下之前拿到的字节切片bytes, 	// 就可以用它来读取甚至修改缓冲区中的后续内容。 	bytes = bytes[:cap(bytes)] 	fmt.Printf("The all of the contents in the buffer: %q ", bytes) 	fmt.Println()  	blank := byte(" ") 	fmt.Println("Set blanks into the contents in the buffer ...") 	for _, i := range []int{55, 56, 57, 58, 66, 67, 68} { 		bytes[i] = blank 	} 	fmt.Println()  	peekNum = size 	fmt.Printf("Peek %d bytes ... ", peekNum) 	bytes, err = reader4.Peek(peekNum) 	if err != nil { 		fmt.Printf("error: %v ", err) 	} 	fmt.Printf("Peeked contents(%d): %q ", len(bytes), bytes) 	fmt.Println()  	// 示例7。 	// ReadSlice方法也存在相同的问题。 	delimiter := byte(",") 	fmt.Printf("Read slice with delimiter %q... ", delimiter) 	line, err := reader4.ReadSlice(delimiter) 	if err != nil { 		fmt.Printf("error: %v ", err) 	} 	fmt.Printf("Read contents(%d): %q ", len(line), line) 	fmt.Println()  	line = line[:cap(line)] 	fmt.Printf("The all of the contents in the buffer: %q ", line) 	fmt.Println()  	underline := byte("_") 	fmt.Println("Set underlines into the contents in the buffer ...") 	for _, i := range []int{89, 92, 103} { 		line[i] = underline 	} 	fmt.Println()  	peekNum = size 	fmt.Printf("Peek %d bytes ... ", peekNum) 	bytes, err = reader4.Peek(peekNum) 	if err != nil { 		fmt.Printf("error: %v ", err) 	} 	fmt.Printf("Peeked contents(%d): %q ", len(bytes), bytes) } 总结
  我们用比较长的篇幅介绍了bufio包中的数据类型,其中的重点是bufio.Reader类型。
  bufio.Reader类型代表的是携带缓冲区的读取器。它的值在被初始化的时候需要接受一个底层的读取器,后者的类型必须是io.Reader接口的实现。
  Reader值中的缓冲区其实就是一个数据存储中介,它介于底层读取器与读取方法及其调用方之间。此类值的读取方法一般都会先从该值的缓冲区中读取数据,同时在必要的时候预先从其底层读取器那里读出一部分数据,并填充到缓冲区中以备后用。填充缓冲区的操作通常会由该值的fill方法执行。
  在填充的过程中,fill方法有时还会对缓冲区进行压缩。在Reader值拥有的众多读取方法中,有 4 个方法可以作为不同读取流程的代表,它们是:Peek、Read、ReadSlice和ReadBytes。
  Peek方法的特点是即使读取了缓冲区中的数据,也不会更改已读计数的值。而Read方法会在参数值的长度过大,且缓冲区中已无未读字节时,跨过缓冲区并直接向底层读取器索要数据。
  ReadSlice方法会在缓冲区的未读部分中寻找给定的分隔符,并在必要时对缓冲区进行填充。
  如果在填满缓冲区之后仍然未能找到分隔符,那么该方法就会把整个缓冲区作为第一个结果值返回,同时返回缓冲区已满的错误。
  ReadBytes方法会通过调用ReadSlice方法,一次又一次地填充缓冲区,并在其中寻找分隔符。除非发生了未预料到的错误或者找到了分隔符,否则这一过程将会一直进行下去。
  Reader值的ReadLine方法会依赖于它的ReadSlice方法,而其ReadString方法则完全依赖于ReadBytes方法。
  另外,值得我们特别注意的是,Reader值的Peek方法、ReadSlice方法和ReadLine方法都可能会造成其缓冲区中的内容的泄露。
  最后再说一下bufio.Writer类型。把该类值的缓冲区中暂存的数据写进其底层写入器的功能,主要是由它的Flush方法实现的。
  此类值的所有数据写入方法都会在必要的时候调用它的Flush方法。一般情况下,这些写入方法都会先把数据写进其所属值的缓冲区,然后再增加该值中的已写计数。但是,在有些时候,Write方法和ReadFrom方法也会跨过缓冲区,并直接把数据写进其底层写入器。
  请记住,虽然这些写入方法都会不时地调用Flush方法,但是在写入所有的数据之后再显式地调用一下这个方法总是最稳妥的。 package main  import ( 	"bufio" 	"bytes" 	"fmt" 	"strings" )  func main() { 	comment := "Writer implements buffering for an io.Writer object. " + 		"If an error occurs writing to a Writer, " + 		"no more data will be accepted and all subsequent writes, " + 		"and Flush, will return the error. After all data has been written, " + 		"the client should call the Flush method to guarantee all data " + 		"has been forwarded to the underlying io.Writer." 	basicWriter1 := &strings.Builder{}  	size := 300 	fmt.Printf("New a buffered writer with size %d ... ", size) 	writer1 := bufio.NewWriterSize(basicWriter1, size) 	fmt.Println()  	// 示例1。 	begin, end := 0, 53 	fmt.Printf("Write %d bytes into the writer ... ", end-begin) 	writer1.WriteString(comment[begin:end]) 	fmt.Printf("The number of buffered bytes: %d ", writer1.Buffered()) 	fmt.Printf("The number of unused bytes in the buffer: %d ", 		writer1.Available()) 	fmt.Println("Flush the buffer in the writer ...") 	writer1.Flush() 	fmt.Printf("The number of buffered bytes: %d ", writer1.Buffered()) 	fmt.Printf("The number of unused bytes in the buffer: %d ", 		writer1.Available()) 	fmt.Println()  	// 示例2。 	begin, end = 0, 326 	fmt.Printf("Write %d bytes into the writer ... ", end-begin) 	writer1.WriteString(comment[begin:end]) 	fmt.Printf("The number of buffered bytes: %d ", writer1.Buffered()) 	fmt.Printf("The number of unused bytes in the buffer: %d ", 		writer1.Available()) 	fmt.Println("Flush the buffer in the writer ...") 	writer1.Flush() 	fmt.Println()  	// 示例3。 	basicWriter2 := &bytes.Buffer{} 	fmt.Printf("Reset the writer with a bytes buffer(an implementation of io.ReaderFrom) ... ") 	writer1.Reset(basicWriter2) 	reader := strings.NewReader(comment) 	fmt.Println("Read data from the reader ...") 	writer1.ReadFrom(reader) 	fmt.Printf("The number of buffered bytes: %d ", writer1.Buffered()) 	fmt.Printf("The number of unused bytes in the buffer: %d ", 		writer1.Available()) } 思考题
  今天的思考题是:bufio.Scanner类型的主要功用是什么?它有哪些特点? 笔记源码
  https://github.com/MingsonZheng/go-core-demo

FindX3系列首批用户口碑出炉,迄今为此最佳旗舰之一?3月11日晚,OPPO召开新品发布会,正式推出新一代旗舰FindX3系列。因为是OPPO旗舰的十周年代表作,该机型在发布之后就获得了国内外媒体极高的关注度。那么,大家对于FindXFind系列十年盘点,这些设计太经典!多次掀起行业革命要说国内最早的高端手机,第一个想到的应该就是OPPOFind系列了。今年,距离第一代OPPOFind发布已经十年。第一代OPPOFind列邀请了李奥纳多拍宣传片,当时正是盗梦空间爆精准定位客户B2B关键词挖掘(二)独立站的推广运营中,合适的关键词选择十分影响在战场上高效获客。所以,千万不要跑错片场了,你是做B2B的就潜心去挖掘B2B关键词进行推广,别整些B不BC不C的操作。不仅money哗哗黑格增长ampampamp全球搜携手TikTokForBusiness探索品牌跨境出海新机遇2021年9月3日,2021TikTokForBusiness品牌出海峰会于深圳隆重开展。作为本次峰会的官方合作伙伴,全球搜深耕多年服务外贸企业顺利出海,也出席本次峰会,共同思考如中国军团摘金揽银帽子戏法勇进4金已经进入到东京奥运会比赛第8天,今天收获4块金牌,分别是乒乓球1金,游泳1金,羽毛球1金1银,蹦床1金1银。第16金!男子200米混合泳汪顺夺冠并打破亚洲纪录蛙泳安德鲁再次领先,1好用不贵,realme体验领先小米,千元价位独一档相信很多人对于千元机的概念还停留在品控一般,配置不高,但随着智能手机的普及,现在的千元机在性能方面的表现也越来越强了,甚至不逊色一些旗舰机,今天,笔者就为大家推荐目前最值得入手的三东京奥运会中国举重勇夺7金霸气收官刘洋斩获体操首金今勇收5金东京奥运会比赛第10天,今天收获5金3银3铜举重2金,体操1金1银,射击1金,场地自行车1金!等等(主要统计金,银,铜捎带一下,个人大概收集,不喜勿喷)第25金!汪周雨夺得奥运举重当一个人不联系你,也不拉黑你,原因只有一个一生只够爱一人,对待感情坚贞不渝,如今在这个近水楼台先得月的年代里,带着滤镜去看爱情,已经变得不再真实。曾经的相见恨晚,无话不说,到如今的冷漠疏远,无话可说。当生活中再有趣的细节,今日的东京奥运中国队金牌战况勇获三金第13金!张雨霏女子200米蝶泳摘金打破奥运会纪录游泳女子200米蝶泳决赛,中国选手张雨霏以2分03秒86的成绩获得冠军,并打破奥运纪录!第14金!破世界纪录!中国女子4x200米2020东京奥运会闭幕2024相约巴黎他(她)们都是我们的奥运英雄东京奥运会闭幕之日,中国的奖牌榜第一被易位了,总榜最终排在第二位,全新的希望有一种精彩叫超越全力拼搏东京奥运会中国体育代表团38金全回顾100后运动员杨倩最后一枪顶住压力,最终以2美式豪华持续进化,林肯携三款重磅新车登录成都车展中国成都,2021年8月29日稳中求变,智见新可能。今日,2021年下半年首场重量级汽车盛会成都国际车展正式拉开帷幕。作为美式豪华领导品牌,林肯顺应消费升级的市场趋势,推出更年轻更
荣耀V10更新9。0后,流畅度耗电量和bug究竟怎么样?感谢您的阅读!目前已经发现问题耗电太快多任务动画卡顿下拉界面非常丑骚扰拦截没有了兼容性变差等等。(你呢?)当然,荣耀V10升级9。0后,很多人欢呼真香!而且更新后,还是有一部分用户世界级数学家洛朗拉福格加入华为,他什么来头?华为创始人任正非在最新讲话中指出,华为正在引进高鼻子人才,世界级数学家洛朗拉福格加入华为。消息一出,立即在网上炸开了锅!华为众所周知,华为是中国乃至全世界最重视创新的企业之一,每年炒股用什么软件比较好?做梦发财软件。捂脸目前社会上炒股软件比较多,大同小异,不能寄希望于某个软件炒股就能赚钱。决定炒股赚不赚钱因素比较多,比如,有个人的知识能力悟性,有国家的经济发展规划,有外围股市的影当你已是摄影老司机,你会轻易换相机品牌吗?严肃的讲频繁的換相机都是因自己的拍照功底有关但也有的是为了收藏。卡片相机一样能拍出令人滿意的效果。(新疆之行卡片拍攝)是不是轻易换相机品牌,也是衡量一个人是不是真正摄影人的标准,摄想买套音响,用途是在家里听音乐,预算几千块,有什么好的推荐?查了一下资料,前几年推荐的几款搭配1天逸AD68功放搭配童笛3号书架箱端庄稳重,声音较均衡,音色醇厚圆润,声音的全频段平稳,适合播放人声和协奏曲的作品。2AD68功放搭配英国AEE新鲜早科技丨巴西监管机构将再次对苹果公司开出罚单阿里旗下多个APP已接入微信支付快手高级副总裁严强将离职21世纪经济报道记者杨清清实习记者陈龙潼综合报道早上好,新的一天又开始了。在过去24小时内,科技行业发生了哪些有意思的事情?来跟21tech一起看看吧。巨头风向标1巴西监管机构将再如果芯片恢复了,哪个品牌的销量会大涨?芯片恢复了销量最大应该属于华为,现在华为主要芯片限制了华为手机销量,给了苹果和一些国产手机厂商机会。华为芯片和系统一步一步会完善和更新,相信不就将来,手机厂商还是有华为一席之地,希下一代储备池计算速度提高百万倍可用于解决目前信息处理难题总编辑圈点储备池计算是一种模仿人脑工作方式的计算方法。美国科学家在最新一期自然通信杂志上撰文称,他们找到了一种新方法,将储备池计算的速度提高33到100万倍,而所需的计算资源和数据输入却大大网民规模超10亿,互联网可以改变你的人生网民已经超过了10亿,这么庞大的一个互联网市场,如果你不懂得如何赚钱,那就太可惜了。现在在家懒得做饭,就可以点外卖。出门坐车,可以有打车软件,还可以在网上预订火车票和飞机票。如果想是未来重要呢,还是过去重要?生活中,一切向前看,肯定是未来更重要。过去毕竟过去,未来将要应对,如何走好才是最重要。过去再辉煌或者再困难,都仅仅做为人生一部分,或怀念丶或感叹丶或变成经验与教训。但时光不会倒转,拼多多9。9购手机电动车等商品,欺诈消费者活动,为什么没人管拼多多广告遍地开花,只要你看手机,就能免费看拼多多广告,在手机上无论你看书还是听书,都能免费送你拼多多9。9购物,在抖音上拼多多也有一席之地,拼多多真的很讨厌。我玩手机时候看到下载