-
Notifications
You must be signed in to change notification settings - Fork 0
/
content.json
1 lines (1 loc) · 42 KB
/
content.json
1
[{"title":"Python并行遍历zip()函数","date":"2017-04-15T06:34:00.000Z","path":"2017/04/15/Python并行遍历zip()函数/","text":"zip()函数在运算时,会以一个或多个序列做为参数,返回一个元组的列表。同时将这些序列中并排的元素配对。 一、zip()基本操作方法例如,有两个列表: 12a = [1,2,3]b = [4,5,6] 在python 3.0中,使用zip()函数来可以把列表合并,并创建一个可迭代对象,使用时必须将其包含在一个list中,方便一次性显示出所有结果 123456z = zip(a,b)print(z)# <zip object at 0x000001D8992CC0C8>list(zip(a,b))# [(1, 4), (2, 5), (3, 6)] zip()参数可以接受任何类型的序列,同时也可以有两个以上的参数;当传入参数的长度不同时,zip能自动以最短序列长度为准进行截取,获得元组。 1234str1 = 'abc'str2 = 'def123'list(zip(str1,str2))# [('a', 'd'), ('b', 'e'), ('c', 'f')] 二、搭配for循环,支持并行迭代操作方法zip()方法用在for循环中,就会支持并行迭代: 123456789l1 = [2,3,4]l2 = [4,5,6]for (x,y) in zip(l1,l2): print x,y,'--',x*y# 2 4 -- 8# 3 5 -- 15# 4 6 -- 24 其实它的工作原理就是使用了zip()函数生成的可迭代对象,在for循环里解包zip结果中的元组,用元组赋值运算。就好像(x,y)=(2,6),赋值、序列解包操作。在对文件的操作中我们也会用到遍历,例如Python遍历文件夹目录与文件操作,就是很方便实用的。 三、zip()在Python 2 和Python 3中的不同问题一:zip方法在Python 2 和Python 3中的不同Python 2 的代码演示: 12345$ python2>>> a = zip((1, 2), (3, 4))>>> a[(1, 3), (2, 4)]# 可以看到这里返回的是一个list Python 3 的代码演示: 12345678910111213141516171819202122$ python3>>> a = zip((1, 2), (3, 4))>>> a<zip object at 0x1007096c8># 可以看到这里返回的是一个对象,这里就是2和3的不同点>>> dir(a) # 查看a的相关属性['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']# 这里值得注意的是 '__iter__' 和 '__next__' 方法,说明a是一个支持遍历的对象# 既然知道了a是一个支持遍历的对象,我们也就基本明白了a的用法了### 和Python2的区别(一):返回的是一个支持遍历的对象,而不是一个list本身>>> for i in a: print(i) # in 方法...(1, 3)(2, 4)>>> next(a) # 我们测试__next__方法Traceback (most recent call last): File \"<stdin>\", line 1, in <module>StopIteration # 说明next方法是支持的,但是这里也说明了对象只能遍历一次>>> a = zip((1, 2), (3, 4)) # 这里需要重新赋值,因为这个对象只能遍历一次>>> next(a)(1, 3) # 运行良好### 返回的对象支持遍历的操作 问题二:为什么会有不同最主要的原因应该是节约内存,可迭代的对象只有在使用某个元素时才会生成这个元素 这个zip在不同版本的不同反应了python的一个演变:大部分返回list的函数不在返回list,而是返回一个支持遍历的对象,比如map、fiter之类的,基本的例子如下: 123456$ python3>>> a = map(abs, [1, 2, -3])>>> a<map object at 0x100707ed0>>>> list(a) # 如果不习惯,可以自己手动转化为list,也是写兼容代码需要注意的地方[1, 2, 3] 1234$ python2>>> a = map(abs, [1, 2, -3])>>> a[1, 2, 3]","tags":[{"name":"python","slug":"python","permalink":"http://yoursite.com/tags/python/"}]},{"title":"Beautiful Soup4 学习","date":"2017-04-15T06:11:00.000Z","path":"2017/04/15/Beautiful Soup4 学习/","text":"本文参考 Beautiful Soup官方文档 ,做学习记录用 一、初始化导入 BeautifulSoup4 包: 1from bs4 import BeautifulSoup 在构造 BeautifulSoup 对象时, BeautifulSoup 首先文档被转换成Unicode,并且HTML的实例都被转换成Unicode编码, 然后BeautifulSoup 使用指定的网页解析器来解析这段文档 关于解析器的描述参考官方文档, BeautifulSoup 根据当前系统安装的库自动选择解析器,解析器的优先数序: lxml, html5lib, html.paser, 推荐用 lxml 这个速度更快的解析器 构造 BeautifulSoup 对象: 12345678910111213html_doc = \"\"\"<html> <head> <title>The Dormouse's story</title> </head> <body> <p class=\"title\">The Dormouse's story</p>、 <p class=\"title\">The Dormouse's story</p> </body></html>\"\"\"soup = BeautifulSoup(html_doc, 'lxml') 当使用 lxml 解析器时,Beautiful Soup 将复杂HTML文档转换成一个复杂的DOM树结构进行解析: 每个节点都是Python对象,所有对象可以归纳为4种: Tag, NavigableString, BeautifulSoup, Comment这里只介绍简单的 tag 对象的方法, 其他对象参考官方文档 二、find_all 方法find_all() 几乎是 Beautiful Soup 中最常用的搜索方法; find_all 可以通过 tag 的名字或者属性等进行搜索,也可以将这个搜索策略结合起来搜索, find_all 根据你输入的策略找到所有符合要求的 tag,以 list 的方式返回。 1find_all(self, name=None, attrs={}, recursive=True, text=None, limit=None, **kwargs) 2.1 搜索标签名字(name):1234561 find_all(tagname) # 直接搜索名为tagname的tag, 如:find_all('head')2 find_all(list) # 搜索在list中的tag, 如:find_all(['head', 'body'])3 find_all(dict) # 搜索在dict中的tag, 如:find_all({'head':True, 'body':True})4 find_all(re.compile('')) # 搜索符合正则的tag, 如:find_all(re.compile('^p')) 搜索以p开头的tag5 find_all(lambda) # 搜索函数返回结果为true的tag, 如:find_all(lambda name: len(name) == 1) 搜索长度为1的tag6 find_all(True) # 搜索所有tag 2.2 搜索属性(attrs):12341 find_all('id':'xxx') # 寻找id属性为xxx的所有tag2 find_all('_class':'xxx') # 寻找class属性为xxx的所有tag3 find_all(attrs={'id':re.compile('xxx'), 'algin':'xxx'}) # 寻找id属性符合正则且algin属性为xxx的所有tag4 find_all(attrs={'id':True, 'algin':None}) # 寻找有id属性但是没有algin属性的所有tag 2.3 搜索文字(text):注意,文字的搜索会导致其他搜索给的值如:tag, attrs都失效。方法与搜索便签名字一样 2.4 recursive, limit:recursive=False表示只搜索直接儿子,否则搜索整个子树,默认为True。当使用findAll或者类似返回list的方法时,limit属性用于限制返回的数量,如findAll(‘p’, limit=2): 返回首先找到的两个tag 2.5 像调用 find_all() 一样调用tag:由于find_all() 几乎是Beautiful Soup中最常用的搜索方法,所以我们定义了它的简写方法. BeautifulSoup 对象和 tag 对象可以被当作一个方法来使用,这个方法的执行结果与调用这个对象的 find_all() 方法相同; 下面两行代码是等价的: 12soup.find_all(\"a\")soup(\"a\") 这两行代码也是等价的: 12soup.title.find_all(text=True)soup.title(text=True) 三、find 方法find_all() 方法将返回文档中符合条件的所有tag,尽管有时候我们只想得到一个结果.比如文档中只有一个标签,那么使用 find_all() 方法来查找标签就不太合适, 使用 find_all 方法并设置 limit=1 参数不如直接使用 find() 方法.下面两行代码是等价的: 12345soup.find_all('title', limit=1)# [<title>The Dormouse's story</title>]soup.find('title')# <title>The Dormouse's story</title> 唯一的区别是 find_all() 方法的返回结果是值包含一个元素的列表,而 find() 方法直接返回第一个符合条件的tagfind_all() 方法没有找到目标是返回空列表, find() 方法找不到目标时,返回 None . 12print(soup.find(\"nosuchtag\"))# None 可以使用 soup.head.title 这种方式直接得到子节点,其中 head 和 title 是 tag的名字; 这个简写的原理就是调用当前 tag 的 find() 方法: 12345soup.head.title# <title>The Dormouse's story</title>soup.find(\"head\").find(\"title\")# <title>The Dormouse's story</title> 四、tag结点之间的关系4.1 子节点:tag的 .contents 属性可以将tag的子节点以列表的方式输出: 123456soup.head.contents# [<title>The Dormouse's story</title>]# 即可以使用congtents[i],来找到某个特定的结点soup.head.contents[0]# <title>The Dormouse's story</title> 4.2 父节点:通过 .parent 属性来获取某个元素的父节点: 12345title_tag = soup.titletitle_tag# <title>The Dormouse's story</title>title_tag.parent# <head><title>The Dormouse's story</title></head> 4.3 兄弟结点:在文档树中,使用 .next_sibling 和 .previous_sibling 属性来查询兄弟节点: 123456789sibling_soup = BeautifulSoup(\"<a><b>text1</b><c>text2</c></b></a>\")# 因为<b>标签和<c>标签是同一层,所以他们是同一个元素的子节点,所以<b>和<c>可以被称为兄弟节点# 一段文档以标准格式输出时,兄弟节点有相同的缩进级别.在代码中也可以使用这种关系.sibling_soup.b.next_sibling# <c>text2</c>sibling_soup.c.previous_sibling# <b>text1</b> 实际文档中的tag的 .next_sibling 和 .previous_sibling 属性通常是字符串或空白,因为空白或者换行也可以被视作一个节点,所以得到的结果可能是空白或者换行,所以在使用兄弟节点时需要注意这个坑! 五、输出通过上述的方法找到了便签之后,需要将标签中相应的信息进行输出 5.1 格式化输出:prettify() 方法将Beautiful Soup的文档树格式化后以Unicode编码输出,每个XML/HTML标签都独占一行 123456789101112131415161718markup = '<a href=\"http://example.com/\">I linked to <i>example.com</i></a>'soup = BeautifulSoup(markup)soup.prettify()# '<html>\\n <head>\\n </head>\\n <body>\\n <a href=\"http://example.com/\">\\n...'print(soup.prettify())# <html># <head># </head># <body># <a href=\"http://example.com/\"># I linked to# <i># example.com# </i># </a># </body># </html> BeautifulSoup 对象和它的tag节点都可以调用 prettify() 方法: 1234567print(soup.a.prettify())# <a href=\"http://example.com/\"># I linked to# <i># example.com# </i># </a> 5.2 get_text():如果只想得到tag中包含的文本内容,那么可以调用 get_text() 方法,这个方法获取到tag中包含的所有文本内容包括子孙tag中的内容,并将结果作为Unicode字符串返回: 1234567markup = '<a href=\"http://example.com/\">\\nI linked to <i>example.com</i>\\n</a>'soup = BeautifulSoup(markup)soup.get_text()# u'\\nI linked to example.com\\n'soup.i.get_text()# u'example.com' 5.3 得到便签的属性:一个tag可能有很多个属性. tag 有一个 “class” 的属性,值为 “boldest” ; tag的属性的操作方法与字典相同: 12tag['class']# u'boldest'","tags":[{"name":"python","slug":"python","permalink":"http://yoursite.com/tags/python/"},{"name":"爬虫","slug":"爬虫","permalink":"http://yoursite.com/tags/爬虫/"}]},{"title":"字符编码与字符集以及UTF-8总结","date":"2017-04-10T12:12:10.000Z","path":"2017/04/10/字符编码与字符集以及UTF8总结/","text":"之前一直搞不清楚各种字符编码、字符集的区别,也被搞晕了很多次,只知道用UTF-8编码格式就行了。但是作为一个程序猿,秉着喜欢探索技术内部原理的精神,查阅了很多资料,决定搞清楚他们之间的爱恨情仇。 一、 字符集与字符编码字符集:字符集是由字符组成的一个集合。而字符是由数字、汉字和符号等信息单位的总称,一般来说不同国家的字符集有着不同的字符,但也有统一全世界大部分国家的语言的字符集UCS。 字符编码:字符编码把字符集中的每一个字符对应成不同的二进制数,以便文本在计算机中存储和通过网络传输。在计算机发展早期,每一个字符对应一个特定的二进制位,字符集与字符编码不作区分是同一个概念,如:ASCII、GB2312B、BIG5、Shift_JIS等;但是随着计算机的发展。字符集不断扩大,为了节约存储空间,出现了各式各样的字符编码。 二、ASCII编码我们知道最早的计算机是美国弄出来的,在“冯•诺依曼结构”中计算机只能存储和处理二进制文件,所以当时为了能让计算机处理和存储英文字符,美国国家标准学会制定了ASCII(American Standard Code for Information Interchange,美国信息交换标准代码),ASCII码使用指定的7 位或8 位二进制数组合来表示128 或256 种可能的字符。标准ASCII 码也叫基础ASCII码,使用7 位二进制数(剩下的1位二进制为0)来表示所有的大写和小写字母,数字0 到9、标点符号, 以及在美式英语中使用的特殊控制字符。比如大写的字母A是65(二进制01000001)。 三、非ASCII编码与ANSI编码在英文中,用128个字符来编码就足够了,但是对于其他语言,128个字符是完全不够了。比如在法语中,字母上方有注音符号,它就无法用ASCII码表示。于是,一些欧洲国家就决定,利用ASCII码表中闲置的128-255这一段来表示自己国家的字符集。比如,法语中的é的编码为130(二进制10000010)。于是国际标准化组织在ASCII的基础上进行了扩展,形成了ISO-8859标准,跟EASCII类似,兼容ASCII。然而欧洲有很多国家并且每个国家的的语言不一样,于是不同的国家利用ASCII码表的128-255这一段,纷纷制定了符合本国语言的标准。对于不同国家的标准,128-255这一段表示的字符不一样,无法兼容。 而对于亚洲来说,语言环境更加复杂,对于中国来说,汉字就多达10万左右。一个字节只能表示256种符号,肯定是不够的,就必须使用多个字节表达一个符号。于是中国国家标准总局制定了GB2312编码方式,使用两个字节表示一个汉字,所以理论上最多可以表示256x256=65536个符号。不止中国,在亚洲的不同地方也有着不同的编码方式,比如中国台湾有BIG5编码方式,日本有Shift_JIS编码方式。 像上面说的,那时候计算机正在发展,各国之间的交流少,编码问题不是特别大,那时候为了方便的用户的使用,屏蔽各个国家的不同的编码方式,出现了ANSI编码(严格来说不算是一种编码方式),ANSI到现在为止也是默认的编码方式,ANSI会根据当前系统的语言自动的使用一种编码方式,比如对于英文文件是ASCII编码,对于简体中文文件是GB2312编码(只针对Windows简体中文版,如果是繁体中文版会采用Big5码)等 四、UCS字符集与Unicode字符编码由于每个国家有着自己的编码方式,互不兼容,乱码问题经常出现(对同一组二进制数据,不同的编码会解析出不同的字符,用对了编码,解析出来的字符组成的文字是有意义的,用错了编码,解析出来的字符组成的文字是没意义的,也就是通常所说的乱码)。要想打开一个文本文件,就必须知道它的编码方式,否则用错误的编码方式解读,就会出现乱码。为什么电子邮件常常出现乱码?就是因为发信人和收信人使用的编码方式不一样。 为了能够方便全世界各国的计算机进行正常的交流,计算机科学领域发展的一个老套路——标准化出现了。UCS与Unicode应运而生。UCS(Universal Character Set,通用字符集)是由ISO(International Organization for Standardization,国际标准化组织)标准所定义的标准字符集。Unicode是伴随着UCS的标准而发展的一种统一的标准字符编码。 UCS包括了其他所有字符集。它保证了与其他字符集的双向兼容,即,如果你将任何文本字符串翻译到UCS格式,然后再翻译回原编码,你不会丢失任何信息。UCS包含了已知语言的所有字符。除了拉丁语、希腊语、斯拉夫语、希伯来语、阿拉伯语、亚美尼亚语、格鲁吉亚语,还包括中文、日文、韩文这样的方块文字,UCS还包括大量的图形、印刷、数学、科学符号。UCS至今仍在不断增修,每个新版本都加入更多新的字符,现在的规模可以容纳100多万个符号。每个符号的编码都不一样,UCS不仅给每个字符分配一个代码,而且赋予了一个正式的名字。表示一个UCS或Unicode值的十六进制数通常在前面加上“U+”,例如“U+0041”代表字符“A”。 在UCS字符集的基础上Unicode字符编码把所有语言都统一到一套编码里,这样就不会再有乱码问题了。Unicode标准也在不断发展,但最常用的是用两个字节表示一个字符(如果要用到非常偏僻的字符,就需要四个字节)。现代操作系统和大多数编程语言都直接支持Unicode。 五、ASCII编码与Unicode编码在UCS与Unicode出现之前字符集与字符编码不作区分,两者为同一个。比如ASCII就既表示由大小写字母、符号等组成的字符集,也表示这个字符集所对应的字符编码。但是UCS与Unicode出现以后,字符集与字符编码开始区分。UCS为全世界大部分国家的字符集的集合,Unicode是以UCS字符集为基础的字符编码。虽然Unicode统一所有的字符编码,但是由于在此之前很多老系统用的都是ASCII编码或者兼容ASCII的编码,所以要求新编码方案必须能够兼容原来的系统,并且由于Unicode自身存在着一些问题,在很长一段时间内无法推广,直到互联网的出现。 问题一:如何区分ASCII与Unicode在ASCII编码中,每个字符为一个字节,而在Unicode编码中,每个字符为两个字节,比如:字母 A 用ASCII编码是十进制的 5,二进制的 01000001;但是Unicode使用两个字节表示一个字符,那应该如何表示Unicode编码中的字母 A 呢?你可能很容易的想到,直接在ASCII表示的字母 A 的二进制前面加0就好了,于是字母 A 用Unicode编码也是十进制的“U+0041”,二进制的 00000000 01000001。但是这样子随即出现第二个问题。 问题二:Unicode表示纯英文浪费存储空间从上面的例子我们可以看出,要区分ASCII与Unicode只需要在ASCII编码表示的字符的二进制前面添加8个二进制0即可,但是如果我们的文本文件是纯英文文件,如果用ASCII编码的话每个字符只需要一个字节即可表示,但是如果使用Unicode编码的话每个字符需要两个字节,这样子所需空间增加了一倍,在存储和传输上就十分不划算。 六、Unicode与UTF-8随着互联网的普及,各国之间的交流越来越频繁,急需一种既能够统一编码全世界大部分国家的字符集,又能够完美兼容ASCII,并且不会造成空间浪费的编码方式。就在这个时候,把Unicode编码转化为“可变长编码”的UTF-8字符编码出现了!UTF-8编码把一个Unicode字符根据不同的数字大小编码成1-6个字节,常用的英文字母被编码成1个字节,汉字通常是3个字节,只有很生僻的字符才会被编码成4-6个字节。如果你要传输的文本包含大量英文字符,用UTF-8编码就能节省空间。例如:字符 A 用ASCII字符编码为01000001,用Unicode字符编码00000000 01000001,用UTF-8字符编码为01000001。从例子中可以看出,UTF-8可以完美的兼容ASCII,并且可以说UTF-8把ASCII当成自己的一部分。 UTF-8字符编码的实现UTF-8的编码规则很简单,只有二条: 1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。 2)对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。 下表总结了编码规则,字母x表示可用编码的位。 Unicode符号范围 | UTF-8编码方式(十六进制) | (二进制)————————————– + ——————————————————–0000 0000-0000 007F | 0xxxxxxx0000 0080-0000 07FF | 110xxxxx 10xxxxxx0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 跟据上表,解读UTF-8编码非常简单。如果一个字节的第一位是0,则这个字节单独就是一个字符;如果第一位是1,则连续有多少个1,就表示当前字符占用多少个字节。 下面,以汉字”严”为例,演示如何实现UTF-8编码。 已知”严”的unicode是4E25(100111000100101),根据上表,可以发现4E25处在第三行的范围内(0000 0800-0000 FFFF),因此”严”的UTF-8编码需要三个字节,即格式是”1110xxxx 10xxxxxx 10xxxxxx”。然后,从”严”的最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0。这样就得到了,”严”的UTF-8编码是”11100100 10111000 10100101”,转换成十六进制就是E4B8A5。 在存储和传输过程中UTF-8与Unicode的转换计算机系统通用的字符编码工作方式:在计算机内存中,统一使用Unicode编码,当需要保存到硬盘或者需要传输的时候,就转换为UTF-8编码。 用记事本编辑的时候,从文件读取的UTF-8字符被转换为Unicode字符到内存里,编辑完成后,保存的时候再把Unicode转换为UTF-8保存到文件: 浏览网页的时候,服务器会把动态生成的Unicode内容转换为UTF-8再传输到浏览器,所以你看到很多网页的源码上会有类似<meta charset="UTF-8" />的信息,表示该网页正是用的UTF-8编码: 本文部分内容参考、部分图文摘抄以下博客 郭文圣:字符,字符集,字符编码 阮一峰:字符编码笔记:ASCII,Unicode和UTF-8 廖雪峰:字符串和编码 知乎问题:Windows 记事本的 ANSI、Unicode、UTF-8 这三种编码模式有什么区别? 维基百科:Unicode","tags":[{"name":"编码","slug":"编码","permalink":"http://yoursite.com/tags/编码/"}]},{"title":"利用Sentris特价服务器和Shadowsocks搭建自己的梯子","date":"2017-04-09T03:06:30.000Z","path":"2017/04/09/利用Sentris特价服务器和Shadowsocks搭建自己的梯子/","text":"博主之前一直使用蓝灯fq,由于蓝灯的速度实在太渣以及流量太少(毕竟免费),并且看过一些文章说免费的代理可能存在py,在无法忍受的前提下,决定自己动手搭建梯子。最开始是找到了一位大牛的博客(老高的技术博客),里面推荐了几个国外的vps,也不是很贵,本来准备入手了,但是闲着无聊多看了下底下的评论,发现另外一位 dalao(尼玛博客)推荐了一个非常便宜的Sentris服务器,顿时眼前一亮(主要是没钱),于是开启了愉快的造梯子之旅。 一、购买Sentris特价服务器Sentris是一家老牌美国IDC,之前也有用FreeServer.US的牌子推出过免费的vps,不过现在已经不提供了免费的了,但是现在还有活动,最低可以用$5.99 USD买三年的一台很小的vps,只有64M内存(博主买的就是这个),还是很实惠的。 【注】:由于博主只是平时fq不怎么看视频,所以用64M的内存搭建一个梯子平时用用并且不看高清视频还是没问题的,如果你有到YouTube上看1080P视频的需求,亲测这个64M内存的vps是绝对不行的 1.Sentris特价服务器购买地址:http://freeserver.us/plans/ 2.在弹出来的页面上点击:New LXC/OpenVZ SATA RAID from $0.16/mo* 3.在出现的面板中,点击:Order Now 4.在弹出的新网页中选择第一个vps:New OVZ SATA SEA Custom - Based VPS: New OpenVZ 5.然后点击Continue按钮,进入一个新窗口选择vps的配置,vps的配置一共有四项:Product/Service、Configurable Options、Addons、Additional Required Information。 配置Product/Service:产品的名字和产品的描述,这里只需要将Billing Cycle这一项改成购买36个月 配置Configurable Options:vps的配置选项,改成和下图一样就行了,其实主要就是改了Bandwidth in MB,因为默认的带宽是500G每个月的,需要多交钱,实际上250G就够了。 配置Addons:这个选项是额外的扩展,可以新加硬盘、内存和交换区,但是要另加钱!!!,默认什么都不加就好了。 配置Additional Required Information:一些附加的必须的信息,你可以把它当做是一个用户协议,你只能每一项的按照它的选择来,不然不卖给你! 6.点击Continu按钮,会弹出购物车。如果按照我的教程一步一步的点击的话一共是$5.99 USD。 7.然后点击Checkout按钮提交订单,如果你没注册的话会先让你填信息注册。然后就是付款了,这里付款是不能使用支付宝的,只能使用PayPal国际版(中国版也不能用)、比特币、信用卡或者Visa卡等。由于博主这些都没有(穷逼学生),只能使用万能的淘宝,淘宝搜索代付就行了,不过需要几块钱的手续费,反正博主最后48块钱人民币搞定的(5.99美元=41.2453人民币元) 8.付款完成之后你就可以点击Service的My Service选项查看自己的服务器,点击你的服务器就可以看到服务器的控制面板(控制面板的图就不放了),里面有服务器的ip、登录账号密码等等所有信息,可以使用ssh工具登录服务器进行第二步操作了! 二、搭建Shadowsocks服务Shadowsocks是一个使用Socks5代理方式的代理服务。Shadowsocks分为服务器端和客户端。在使用之前,需要先将服务器端部署到服务器上面,然后通过客户端连接并创建本地代理。本节将从服务器端搭建与客户端搭建两方面来讲。 Shadowsocks的github地址:https://github.com/shadowsocks 1.搭建Shadowsocks服务端 首先用ssh工具登录到你的服务器,然后使用以下命令查看一下你的linux发行版是什么,如果按照我的教程一步一步的来,应该都是CentOS发行版,下面安装ShadowSocks的命令也是适用于CentOS发行版的。(如果选择了Debian发行版的系统,自己去Shadowsocks的github上查找相关搭建方法) 1cat /etc/issue 使用yum命令安装ShadowSocks服务器端(这里不要使用Shadowsocks的github地址上提供的安装办法,因为我们买的sentris服务器安装的python版本太低了) 12345# 首先安装python包管理工具和安装pipyum install python-setuptools && easy_install pip# 使用pip直接安装shadowsocks服务器端pip install shadowsocks 我们使用配置文件的方式配置ShadowSocks服务器端,需要自己创建配置文件/etc/shadowsocks.json 12345678910111213141516# 使用touch命令在etc目录下创建配置文件/etc/shadowsocks.jsontouch /etc/shadowsocks.json# 使用vi写入配置参数vi /etc/shadowsocks.json# 写入的参数如下:{\"server\":\"my_server_ip\",\"server_port\":8388,\"local_address\":\"127.0.0.1\",\"local_port\":1080,\"password\":\"mypassword\",\"timeout\":300,\"method\":\"rc4-md5\"} 各字段说明:(更多配置见这里) server:服务器IP server_port:服务器端口,一般设置为8388 local_address:本地地址,一般设置为127.0.0.1 local_port:本地端端口,一般设置为1080 password:用来加密的密码 timeout:超时时间(秒) method:加密方法,可选择aes-256-cfb,des-cfb, rc4-md5”等,默认的是aes-256-cfb,推荐使用更快的rc4-md5 使用配置文件在后台运行Shadowsocks服务 12345# 后台启动ssserver -c /etc/shadowsocks.json -d start# 关闭服务ssserver -c /etc/shadowsocks.json -d stop 2.安装Shadowsocks客户端 首先访问Shadowsocks的github地址下载最新版本的Shadowsocks客户端地址:https://github.com/shadowsocks/shadowsocks-windows/releases 下载好之后双击exe打开(绿色版,没有安装过程),输入前面在Shadowsocks服务端配置文件中配置的信息,包括服务器地址、服务器端口、密码、加密方式、超时和代理端口,输入完成之后点击确定即可。 右键点击右下角的Shadowsocks小飞机图标,选择启动系统代理,并且在系统代理模式那里选择PAC模式(在PAC模式下系统会自动切换代理,如果访问的是墙内的网站,则不会使用代理,如果访问墙外的网站,则使用代理;在全局模式下则全部使用代理),最后选择开机启动,这样子就不用每次开机都要手动启动Shadowsocks客户端了。 这样子本地Shadowsocks服务器就已经安装成功了,如果使用IE浏览器的话就可以正常的访问墙外的世界了,但是博主使用的是Chrome浏览器,还不能直接fq,还需要进一步的设置。 三、Chrome浏览器设置自动切换代理上网这里需要使用Chrome浏览器的一个代理管理插件——SwitchyOmega,这个插件能实现自动根据URL来决定是否使用代理。 正常来说运行了Shadowsocks后就可以直接访问Chrome应用商店安装了,在应用商店上面搜索SwitchyOmega,然后添加至Chrome即可。 安装完成之后打开SwitchyOmega插件后,选择新建情景模式,情景模式名称随便填,我这里填为proxy,类型选择为代理服务器。(由于我已经存在一个名为proxy的情景模式了,所以会提示已存在) 修改名为proxy的情景模式的配置,代理协议选择SOCKS5,代理服务器为127.0.0.1,代理端口为1080。 选择右边的自动切换/auto switch进行自动切换设置,在切换规则中先选择规则列表规则,然后将情景模式设置成proxy,然后设置默认情景模式为直连模式,在下面的*规则列表设置中选择AutoProxy,然后填入网址:https://raw.githubusercontent.com/gfwlist/gfwlist/master/gfwlist.txt点击立即更新情景模式即可。 最后是最关键的一步,在浏览器右上角点击插件按钮,然后选择自动切换规则模式,这样只要在规则列表里面的网站都会fq访问。对于没在翻墻规则里面的网站,可以自己添加规则(无法访问的时候插件那图标会有显示,点击后就可以看到快速添加方法)。 主要参考文章:CentOS6.6安装ShadowSocks服务端Chrome+SwitchyOmega+Shadowsocks 图文教程完整篇","tags":[{"name":"翻墙","slug":"翻墙","permalink":"http://yoursite.com/tags/翻墙/"}]},{"title":"Collection与Iterator的remove方法区别与ConcurrentModificationException异常","date":"2017-04-09T02:53:00.000Z","path":"2017/04/09/Collection与Iterator的remove方法区别与ConcurrentModificationException异常/","text":"在我的上一篇文章 Java中三种遍历Collection中元素的方法Iterator、forEach、for循环对比 中提到Iterator和forEach循环在遍历Collection中元素时最大的差别就是在方法remove()上,由于在Iterator的remove()方法中维护一个标志位,所以删除元素时不会出现异常,所以本篇文章就深入Collection与Iterator的源码看看内部究竟是如何实现的。 一. Collection及其实现类ArrayList的部分源码1.Collection内部源码首先我们来看一下Collection内部源码(为方便分析,此处只展示与本篇文章有关的部分):12345678910public interface Collection<E> extends Iterable<E> { boolean remove(Object o); Iterator<E> iterator(); /** * 此处省去其他方法定义 */} 可以看到Collection是一个接口,内部定义了remove()与iterator()方法。 2.ArrayList内部源码由于Collection接口内部无具体实现,所以我们来看Collection的一个最常用的实现类ArrayList内部源码(为方便分析,此处只展示与本篇文章有关的部分):12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { public boolean remove(Object o) { if (o == null) { for (int index = 0; index < size; index++) if (elementData[index] == null) { fastRemove(index); return true; } } else { for (int index = 0; index < size; index++) if (o.equals(elementData[index])) { fastRemove(index); return true; } } return false; } private void fastRemove(int index) { modCount++; int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // clear to let GC do its work } /* -----------------------我是便于观察的分割线----------------------- */ public Iterator<E> iterator() { return new Itr(); } private class Itr implements Iterator<E> { int cursor; // index of next element to return int lastRet = -1; // index of last element returned; -1 if no such int expectedModCount = modCount; public boolean hasNext() { return cursor != size; } @SuppressWarnings(\"unchecked\") public E next() { checkForComodification(); int i = cursor; if (i >= size) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; return (E) elementData[lastRet = i]; } public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.remove(lastRet); cursor = lastRet; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } /** * 此处省去其他方法定义 */} 在ArrayList中并没有直接实现Collection接口,而是通过继承AbstractList抽象类,而AbstractList抽象类又继承了AbstractCollection抽象类,最终AbstractCollection抽象类实现Collection接口;所以ArrayList间接实现了Collection接口,有兴趣的大佬可以自己去研究下为什么这样子设计,在这里就不多加讨论。 可以看到在ArrayList中有实现remove()与iterator()方法,并且通过iterator()方法得到的内部类Itr实现了Iterator接口,在Itr内部类中也有实现remove()方法,下面就来具体的探讨其中的区别。 二. ArrayList的remove()方法分析1.remove()方法在ArrayList的remove()方法内部的实现主要是通过循环找到元素的下标, 然后调用私有的fastRemove()方法:1fastRemove(index); remove()方法没啥好讲的,关键在于调用的fastRemove()方法上。 2.fastRemove()方法fastRemove()方法中会先修改modCount的值,然后将通过复制一个新的数组的方法将原来index位置上的值覆盖掉,最后数组大小减一。我们重点关注fastRemove()方法的第一行代码:1modCount++; 也就是每次调用remove()方法都会使modCount的值加一。那么modCount变量又是什么呢? 3.modCount变量modCount在ArrayList中没有定义,是在ArrayList的父类AbstractList抽象类中定义的:12345678public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> { protected transient int modCount = 0; /** * 此处省去其他方法定义 */} modCount的作用是记录操作(添加删除)ArrayList中元素的次数(这个很关键),每次操作ArrayList中元素后就会使modCount加一。 三. Iterator的remove()方法分析看源码可知ArrayList通过iterator()方法得到了一个内部类Itr,这个内部类实现了Iterator接口,我们重点分析内部类Itr中的实现。 1.expectedModCount 变量在内部类Itr中定义了一个变量expectedModCount :1int expectedModCount = modCount; expectedModCount 只在new一个Itr对象时初始化为modCount 2.next()与remove()方法在调用Itr对象的next()与remove()方法时第一步会先调用checkForComodification()方法。1checkForComodification(); 并且在remove()方法中会调用ArrayList.this.remove(lastRet)方法(也就是具体的ArrayList对象的remove()方法,上面我们讲过,在ArrayList对象的remove()方法中会使得modCount的值加一),然后修改expectedModCount 的值为modCount。 3.checkForComodification()方法checkForComodification()会检查expectedModCount与modCount 是否相等,如果不相等就会抛出ConcurrentModificationException异常。1234final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException();} 四. 总结通过上面的分析我们可以得出,Collection与Iterator的remove()方法最大的区别就是:Iterator的remove()方法会在删除元素后将modCount 的值赋值给expectedModCount,使其又相等。 1.如果我们在Iterator循环中调用Collection的remove()方法123456789public static void display(Collection<Object> collection) { Iterator<Object> it = collection.iterator(); // 会抛出ConcurrentModificationException异常 while(it.hasNext()) { Object obj = it.next(); collection.remove(obj ); }} 由于collection.remove(obj )只会删除obj元素后将modCount 的值加一,并不会修改expectedModCount的值,所以当下一次调用it.next()方法时发现modCount != expectedModCount,将抛出ConcurrentModificationException异常。 2.如果我们在Iterator循环中调用Iterator的remove()方法12345678public static void display(Collection<Object> collection) { Iterator<Object> it = collection.iterator(); // 正常执行 while(it.hasNext()) { Object obj = it.next(); it.remove(obj ); }} 由于it.remove(obj )会在删除obj元素后将modCount 的值加一,并将expectedModCount重新赋值为modCount ,使其相等,所以当下一次调用it.next()方法时发现modCount == expectedModCount,正常执行。","tags":[{"name":"Java","slug":"Java","permalink":"http://yoursite.com/tags/Java/"}]},{"title":"Java中三种遍历Collection中元素的方法Iterator、forEach、for循环对比","date":"2017-04-09T02:15:30.000Z","path":"2017/04/09/Java中三种遍历Collection中元素的方法Iterator、forEach、for循环对比/","text":"[注]仅对比此三种方法在实现下面的此功能中的区别在实现遍历并选择序列中的对象,而客户端程序员不必知道或关心该序列底层的结构 (摘自Java编程思想第四版)也就是说在使用此方法时,不必考虑需要遍历的是ArrayList、LinkedList还是HashSet中的元素 一. 三种方法实现方式代码1.1 Iterator123456public static void display(Iterator<Object> it) { while(it.hasNext()) { Object obj = it.next(); System.out.println(obj); }} 1.2 forEach12345public static void display(Collection<Object> co) { for (Object obj : co) { System.out.print(obj); }} 1.3 for循环123456public static void display(Collection<Object> co) { Object[] obj = co.toArray(new Object[co.size()]); for (int i=0; i<obj.length; i++) { System.out.print(s[i]); } } 二. 三种方法实现方式对比2.1 for循环分析 在for循环中实现中,由于Collection接口本身没有获取Collection中元素的方法,所以必须将Collection先转化为一个Array再进行遍历(Collection的不同实现中有不同的获取元素的方法,由于此处是讨论的是通用的实现方法,所以不考虑具体的实现),因此需要新建一个数组(实际上用 T[] toArray(T[] a) 方法实现是需要创建两个数组的)用来容纳原Collection中的所有元素,这样子增加了额外的开销,另一方面由于数组中的元素是由Collection中的元素复制过来的,所以使得我们对原Collection中的元素进行操作(如删除元素)变得困难(虽然可以使用Collection中的remove(Object o)方法删除元素,但是方法只是删除Collection中第一个与o匹配的元素,过于局限)。 2.2 forEach分析 forEach是 JDK1.5 新加入的一个功能,用于更简单的实现对数组或者Collection对象(实现iterable接口的集合)进行遍历,其实在实现对Collection对象的遍历时,编译器对forEach的实现用的就是Iterator: 123for(Object obj : collection) {}//相当于:for(Iterator iterator = collection.iterator(); iterator.hasNext(); ) {} 但是在forEach实现中如果需要对Collection中的元素进行操作(如删除元素),那么系统将会抛出ConcurrentModificationException异常,这也是他与Iterator实现方式的唯一区别 2.3 Iterator分析 Iterator是Java专门为了实现对Collection中的元素的遍历而设计的一个接口,使用Iterator进行遍历时,不用担心此Collection容器的具体是现是什么,关于Iterator的详细介绍Iterator的中的方法: (1)next()获取序列中的下一个元素(2) hasNext()检查序列中是否还有元素(3) remove()将迭代器新进返回的元素删除 其中Iterator与forEach最大的差别就是在方法remove()上,由于在Iterator的remove()方法中维护一个标志位,所以删除元素时不会出现异常 三. 总结 通过上面的代码,可以很清楚的看到在实现一个通用的遍历Collection中的元素时,Iterator与forEach这两种实现比for循环的代码更简洁、实现更容易。 在实际使用过程中,如只是单纯的对Collection中的元素进行遍历,那么使用forEach相对来说比较方面,如果需要在遍历的过程中对Collection中的元素进行操作(如删除元素),那么无疑必须使用Iterator 续集:从源码上分析操作(删除)元素时为什么建议Iterator循环:Collection与Iterator的remove方法区别与ConcurrentModificationException异常","tags":[{"name":"Java","slug":"Java","permalink":"http://yoursite.com/tags/Java/"}]}]