大海Online的博客

Looking for Interest

ruby 字符串与数组

ruby的字符串与数组,在API设计上,有很多的相似之处,同时方法广度上,又有所区分,这里还是有很多学问

字符串在底层上是字符的数组, 同是数组, 我们先从相同点开始

比较方法名

让我们先单纯的比较一下

'string'.methods.length   ### 161
('string'.methods - Object.new.methods).length   ## 107

[].methods.length         ### 167
([].methods - Object.new.methods).length   ## 113

([].methods & 'string'.methods).length    ## 76
([].methods & 'string'.methods - Object.new.methods).length  ## 22

[].methods & 'string'.methods - Object.new.methods
[:[], :[]=, :concat, :<<, :insert, :length, :size, :empty?, :index, :rindex, :reverse, :reverse!, :delete, :replace, :clear, :include?, :slice, :slice!, :+, :*, :count, :partition]
  • 字符串共有161个方法,去掉继承后, 剩107个
  • 数组共有167个方法,去掉继承后,剩113个
  • 字符串与数组相同的方法,共有76,去掉继承的54个, 只剩22个

让我们来仔细看看这22个方法

  • [],[]=, 下标操作
  • [],slice,slice! 子字符串,子数组操作
  • «,insert,+,* 插入操作
  • length,size,count,empty? 大小计算
  • index,rindex,include? 查找
  • reverse, reverse! 反转
  • delete,clear 删除
  • partition 拆分 (数组中没找到此方法????)

汇总起来, 就是 增删改查, 子反转,大小

字符串

而字符串中去掉数组的方法, 就是真正的字符串操作了

irb(main):026:0> 'string'.methods - [].methods
=> [:casecmp, :%, :bytesize, :match, :succ, :succ!, :next, :next!, :upto, :chr, :getbyte, :setbyte, :byteslice, :to_i, :to_f, :to_str, :dump, :upcase, :downcase, :capitalize, :swapcase, :upcase!, :downcase!, :capitalize!, :swapcase!, :hex, :oct, :split, :lines, :bytes, :chars, :codepoints, :prepend, :crypt, :intern, :to_sym, :ord, :start_with?, :end_with?, :scan, :ljust, :rjust, :center, :sub, :gsub, :chop, :chomp, :strip, :lstrip, :rstrip, :sub!, :gsub!, :chop!, :chomp!, :strip!, :lstrip!, :rstrip!, :tr, :tr_s, :squeeze, :tr!, :tr_s!, :delete!, :squeeze!, :each_line, :each_byte, :each_char, :each_codepoint, :sum, :rpartition, :encoding, :force_encoding, :b, :valid_encoding?, :ascii_only?, :unpack, :encode, :encode!, :to_r, :to_c, :>, :>=, :<, :<=, :between?]

其中: getbyte,setbyte,byteslice,split,bytes,chars,codepoints,prepend,start_with?,end_with? ,sub,gsub,sub!,gsub!,delete!,each_line,each_byte,each_char,each_codepoint 等等,其实也是围绕这个概念,派生出来的方法,都是在”部分”,”全体”, 在这个概念上生成的方法

而to_i, to_f, hex, < > 等, 这些是属于转换与比较的方法, 另upcase,downcase 是字符串间的转换

除去这个概念后, 与字符串本质有关的方法, 只有encoding,force_encoding 这几个方法了

所以字符串的方法, 我们可以归结为三类

  • 字符数组, 数组的操作方法
  • 转换与比较
  • 编码相关

数组

让我们再来看看数组

irb(main):031:0> [].methods - 'string'.methods
=> [:to_a, :to_ary, :at, :fetch, :first, :last, :push, :pop, :shift, :unshift, :each, :each_index, :reverse_each, :find_index, :join, :rotate, :rotate!, :sort, :sort!, :sort_by!, :collect, :collect!, :map, :map!, :select, :select!, :keep_if, :values_at, :delete_at, :delete_if, :reject, :reject!, :zip, :transpose, :fill, :assoc, :rassoc, :-, :&, :|, :uniq, :uniq!, :compact, :compact!, :flatten, :flatten!, :shuffle!, :shuffle, :sample, :cycle, :permutation, :combination, :repeated_permutation, :repeated_combination, :product, :take, :take_while, :drop, :drop_while, :bsearch, :pack, :entries, :sort_by, :grep, :find, :detect, :find_all, :flat_map, :collect_concat, :inject, :reduce, :group_by, :all?, :any?, :one?, :none?, :min, :max, :minmax, :min_by, :max_by, :minmax_by, :member?, :each_with_index, :each_entry, :each_slice, :each_cons, :each_with_object, :chunk, :slice_before, :lazy]

我们再来分下组

  • 常用的增删改查, 子反转,大小
  • 同样的转换与比较 to_a
  • 单项相关, at,first,last,pop,min,max,take
  • 遍历, each,each_index, collect, map,select, take_while,drop_while,grep,find,find_all,reduce,all?,any?,one?, each_*

后续

我们可以看出,在数组中有很多方法,并没有在字符串中体现,字符串中部分数组的方法,必定是最常用

假如,数组里所有的方法,都放在字符串中,虽然行得通,但并不好,相反不好理解, 比如string.grep,实际意义不大

所以我们现在可以理解, 为什么 String 不是直接继承自 Array 了, 因为Array并不是String的核心, String中最核心的应该是别的, 比如字符与编码

ruby 学习笔记

学习ruby已经有一段时间了, 抽点时间把常用的语法方法总结一下, 算是入门小笔记吧

简述

  • ruby为动态语言,弱类型,一切皆对象(就是可以.)
  • ruby没有属性,只有方法,看似属性的,其实都只是其成员变量的读方法
  • ruby调用方法时括号可省略, 行尾无分号, return 关键字可省略
  • ruby支持[]语法创建变长数组, {}语法创建Hash对象, 支持闭包

常用的语法结构

#encoding:utf-8
require 'nokogiri'

# comment

return if(i==0)

if(page==0)
	page = 1
end

for i in 0..10
	puts i.to_s + '.'
end

for step in ['one','two','three']
	process(step)
end

list.each do |x|
	puts "echo #{x}"
end

list.each_index { |i|
    puts "echo #{i} -> #{list[i]}"
}

def nilisnull
	nil.to_s === ''
end

case x
when 1..10              #匹配数字
    puts "First branch"
when foobar()           #批量方法返回的值
    puts "Second branch"
when /^hel.*/           #匹配正则表达式
    puts "Third branch"
else  
    puts "Last branch"
end

方法符号

语法上除了一些常见的顺序,选择,循环,变量,函数,模块,类, 还有一些特有的内容

# 查看一个变量可以调用哪些方法
irb(main):108:0> 'string'.methods
=> [:<=>, :==, :===, :eql?, :hash, :casecmp, :+, :*, :%, :[], :[]=, :insert, :length, :size, :bytesize, :empty?, :=~, :match, :succ, :succ!, :next, :next!, :upto, :index, :rindex, :replace, :clear, :chr, :getbyte, :setbyte, :byteslice, :to_i, :to_f, :to_s, :to_str, :inspect, :dump, :upcase, :downcase, :capitalize, :swapcase, :upcase!, :downcase!, :capitalize!, :swapcase!, :hex, :oct, :split, :lines, :bytes, :chars, :codepoints, :reverse, :reverse!, :concat, :<<, :prepend, :crypt, :intern, :to_sym, :ord, :include?, :start_with?, :end_with?, :scan, :ljust, :rjust, :center, :sub, :gsub, :chop, :chomp, :strip, :lstrip, :rstrip, :sub!, :gsub!, :chop!, :chomp!, :strip!, :lstrip!, :rstrip!, :tr, :tr_s, :delete, :squeeze, :count, :tr!, :tr_s!, :delete!, :squeeze!, :each_line, :each_byte, :each_char, :each_codepoint, :sum, :slice, :slice!, :partition, :rpartition, :encoding, :force_encoding, :b, :valid_encoding?, :ascii_only?, :unpack, :encode, :encode!, :to_r, :to_c, :>, :>=, :<, :<=, :between?, :nil?, :!~, :class, :singleton_class, :clone, :dup, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :freeze, :frozen?, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variables, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :tap, :send, :public_send, :respond_to?, :extend, :display, :method, :public_method, :define_singleton_method, :object_id, :to_enum, :enum_for, :equal?, :!, :!=, :instance_eval, :instance_exec, :__send__, :__id__]

其中:

  • 冒号: 用冒号+字符串代表Symbol(名字), 表示创建了一个Symbol对象, 常用于表示方法, 可以参考这里
  • 问号: 问号也是方法名的一部分,表示这个方法,返回true或者false
  • 叹号: 叹号也是方法名的一部分,表示这个方法,与同名的方法(如gsub,gsub!)功能类似,但此方法会改变对象的内部属性, 相反, 另一个方法则不会修改
  • 下划线: 如__send__, __id__, 为特殊命名方式, 因send太常见,可能被覆盖,用__send__可避免

字符串操作

## 简单
'hello'.length   ## 5
'hello'.start_with?('he')   ## true
'hello'.end_with?('he')     ## false
' hello '.strip             ## 'hello'

# 字符串连接
'hello ' + 'world ' + 3.to_s  ## 'hello world 3'

# 子字符串
'hello world'[3]     ## 'l'
'hello world'[3,5]   ## 'lo wo'
'hello world'[3..5]  ## 'lo '

# 查找
'hello world'.index('orl')   ## 7
'hello world'.index(/[ole]+/)  ## 1

# 替换
'hello world'.gsub('o', 'e')  ## 'helle werld'
'hello world'.gsub(/[a-z]+/, 'ok')   ## 'ok ok'

# 分拆
'hello world'.lines(' ')     ## ["hello ", "world"]
" now's  the time".split(' ')   #=> ["now's", "the", "time"]
"hello".split(//)               #=> ["h", "e", "l", "l", "o"]

数组操作

['one','two','three'].length    ## 3
['one','two','three'].include?('four')   ## false
['1','2','3'] << '4'            ## ['1','2','3','4']
[1,2,3,4][1..3]                 ## [2,3]
[3,2,4,1].sort					## [1,2,3,4]
[1,3,3,4].uniq					## [1,3,4]
['one','two','three'].join(',') ## 'one,two,three'

函数

def process

end

iteye 博客迁移

之前一直是在iteye上写博客的, 后来建立了本博客系统后, 就希望将博客从iteye上迁移过来

然而iteye上博客的文章太多了, 手动一篇一篇拷贝很不现实, 所以写了个工具来辅助迁移

iteye2markdown是一款博客迁移工具,可以将博客从iteye迁移至octopress

  • 此工具会自动下载iteye博客上的所有文章
  • 接着会将博客中的bbcode代码转换成markdown
  • 下载转换后的markdown可用于octopress,迁移至自己的博客系统上

使用方式:

  • 修改download.rb中的cookie配置, cookie将用于连接iteye.com的后台系统, 下载博客内容
  • 修改download.rb中的博客地址
  • 使用以下命令
    ruby download.rb
    

    执行下载转换, 结果保存于blog目录中

源代码见: github

核心代码如下:

re 'open-uri'
require 'nokogiri'

def process(id, date)
	host = 'http://seavers.iteye.com'
	cookie = ''      #IO.read('cookie.txt')
	agent = 'Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.14 Safari/537.36'

	html = open(host + '/admin/blogs/' + id + '/edit', 'User-Agent'=>agent, 'Cookie'=>cookie).read
	doc = Nokogiri::HTML(html)

	title = doc.search('#blog_title').attr('value').to_s
	text = doc.search('#editor_body').text
	category = doc.search('#blog_category_list').attr('value').to_s
	tag = doc.search('#blog_tag_list').attr('value').to_s
	

	content = text
		.gsub('[size=xx-large]', '#')
		.gsub('[size=x-large]', '##')
		.gsub('[size=large]', '###')
		.gsub('[size=medium]', '####')
		.gsub('[size=small]', '')
		.gsub('[size=x-small]', '')
		.gsub('[size=xx-small]', '')
		.gsub('[/size]', '')
		.gsub(/\[url=(.*)\](.*)\[\/url\]/, '[\2](\1)')
		.gsub('[url]', '').gsub('[/url]', '')
		.gsub('[b]', '*').gsub('[/b]', '*')
		.gsub('[list]', '').gsub('[/list]', '')
		.gsub('[b]', '*').gsub('[/b]', '*')
		.gsub('[*]', '* ')
		.gsub('[code]', '```').gsub('[ne/code]', '```')
		.gsub(/\[code="(.*)"\]/, '```')


	filename = date[0..9] + '-' + id + '.markdown'
	File.open('blog/' + filename, 'w') { |file|
		file.puts '---'
		file.puts 'layout: post'
		file.puts 'title: "' + title.to_s + '"'
		file.puts 'date: ' + date + ':00 +0800'
		file.puts 'comments: true'
		file.puts 'categories:'
		file.puts '- ' + category.to_s
		file.puts 'tags:'
		tag.split(',').each {|x|
			file.puts '- ' + x
		}
		file.puts '---'

		file.puts ''
		file.puts content 
	}

end

def spider(page)
	agent = 'Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.14 Safari/537.36'

	url = 'http://seavers.iteye.com/?page='+page.to_s
	html = open(url, 'User-Agent'=>agent)

	doc = Nokogiri::HTML(html)
	doc.search('.blog_main').each {|x|
		id = x.search('h3 a').attr('href').text[/[0-9]+/, 0]
		date = x.search('.blog_bottom .date').text
		puts id + "\t" + date
		process(id, date)
	}

	return doc.search('.pagination a.next_page').length == 0
end

##process('1416013', '2013-12-31 14:01')
(1..100).each {|page|
	break if spider(page)
}

ruby spider

最近开始喜欢上研究ruby了,正好手头有一些抓取的需求,正好练练手

在写爬虫时,我们总希望能方便的实现以下功能

  • 类似curl的下载模块,提供URL即可获取下载内容
  • 方便的网页分析工具,最好能提供类似css selector的元素选择器
  • 简单方便的数据处理模块

写个简单的例子

#encoding:utf-8
require 'open-uri'
require 'nokogiri'

def spide
    url = "http://home.photo.qq.com/index.php?mod=activity&act=detail&category_id=1"

    html = open(url).read
    doc = Nokogiri::HTML(html)

    list = doc.search('.photo-list li img')
    list.each { |x|
        src = x.attr('src')
        puts src.gsub(/400/, '800');
    }
end

spide

其中:

  1. open-uri模块用于实现curl的功能, open后read即可
  2. Nokogiri为html分析模块,支持css selector式的元素选择
  3. 而ruby本身的特点, 则可快速处理数据

抓到图片URL后,剩下的就容易多了,都下载下来吧~~

全屏图片查看器 fullscreen picture viewer

全屏图片查看器是一个chrome扩展

可以用于全屏查看图片,当查看网页时,按f键后,会隐藏掉网页里的其它内容,只留下大图

举一些实际的使用场景

  • 看一些图片时,发现旁边全是广告,直接屏蔽掉,只显示中间的大图
  • 浏览壁纸时,全屏展示大图,用于观看全屏效果
  • 查看所有的图片,直接寻找兴趣点

使用方式

  • 双击网页内的空白区
  • 按f键 (fullscreen的缩写)
  • 按w/s按来调整threshold, 调整图片最小尺寸的阀值

扩展下载: fullscreen.crx 源码浏览: github

试用

  • 图片大小的默认阀值是350px
  • 可以随便找个有大图的页面,按下f键体验一下

hive 常用技巧

最近在使用hive来处理些大数据,偶有心得,记录之

建表

create external table hive_table (
	id int,
	name string,
	category string,
	tag string
)
PARTITIONED BY (pt string) 
ROW FORMAT DELIMITED
   FIELDS TERMINATED BY ',' 
STORED AS TEXTFILE

分组统计

select id,name,count(*),
	count(distinct category),collect_set(category),
	count(distinct tag),collect_set(tag)
from hive_table
where pt='20131213'
group by id,name

拆分多行

select id,name,category_id
from hive_table
lateral view explode(split(category, ',')) category_table as category_id
where pt='20131213'

过滤某些行

select h.*
from hive_table h
left semi join
girl_names g
on h.name = g.name


linux repeat

linux下循环执行命令, 小工具, 觉得有用, 可收录

	vi ~/.bash_aliases
repeat() {
    n=100
    s=1

    while true; do
        arg=$1
        if [ "$arg" == "-n" ]; then
            n=$2
            shift
            shift
        elif [ "$arg" == "-s" ]; then
            s=$2
            shift
            shift
        else
            break
        fi
    done

    while [ $((n -= 1)) -ge 0 ]; do
        "$@"
        sleep $s
    done
}
	source ~/.bash_aliases
	repeat ls -l
	repeat date
	repeat iostat
	repeat -n 3 curl 'http://www.lianghaijun.com'
	repeat -n 3 -s 300 curl 'http://www.lianghaijun.com'
	repeat -n 3 -s 0.5 sh -c "curl -I -s 'http://www.lianghaijun.com' | head -1"

utuntu group

linux用户管理中, 一直都有分组的概念, 然而我们总感觉使用场景较少, 其实是因为我们不了解

举个例子, 比如

  • 在配置apache,nginx时, htdocs的目录权限, 遇到问题时, 我们总喜欢用 777,
  • 在使用samba, 或者 nfs 或者 ftp 时, 为避免复杂性, 也是 777
  • 是否希望将工作,生活的帐户体系分开, 是否希望, 使用不同的git环境

基础

  • 每个用户都有一个主分组, 以及多个次分组, 即一个用户可以属于多个分组

常用命令

分组常用的几个操作

查看自己的分组

seavers@seavers:/home/admin$ id
uid=1000(seavers) gid=1000(seavers) groups=1000(seavers),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare)

查看所有的分组

	cat /etc/group

查看某个用户所在的分组

	groups seavers

增加用户到某个分组

	usermod seavers -a -G admin 

octopress 自动部署方案

今天准备实现 octopress 的自动部署, 完成以下功能

  • 在github.com提交markdown文件后,自动触发服务器打包更新
  • 服务器打包更新后, 同步至github.io, 以及服务器展示

实现之前, 参考了下这篇, 监听github,自动编译octopress博客, 写的蛮不错的 不过, 我的方案, 有些不同的地方

钩子

在github的项目设置的Service Hooks中添加一个WebHook URLs的钩子

	http://<host>:<port>/blog-update

而github钩子, 我采用的是ruby, 命名为hook.rb

require 'socket'

server = TCPServer.open 4001
puts "Listening on port " + server.addr[1].to_s

loop {
  client = server.accept()
  while((x = client.gets) != "\r\n")
    puts x
  end

  client.puts "HTTP/1.1 200 OK\r\n\r\nOK"
  client.close
  puts 'OK'

  system('git pull')
  system('rake generate')
  ##system('rake deploy')
  puts ''
}

然后启动为后台进程

   nohup ruby hook.rb &

当github更新时,会触发hook时, 服务器运行ruby代码,执行shell脚本

	git pull
	rake generate

部署

那服务器上如何来更新呢, 部署的时候, 是有两种方案的,一种是

	rake preview

然后nginx代理转发至preview的端口

另一种方案是, nginx直接请求 _deploy 目录

	location /blog/ {
		alias ~/octopress/public/;
	}

部署在github上

当然, 如果想在github.io上部署, 需要再同步至github.io, 如在刚才在hook.rb里修改

	rake deploy

这里要小心,这样改会死循环的. deploy会触发git commit, commit再触发hook, hook里再deploy,再commit, 如此死循环 所以需要在hook里判断是master还是source

另一种解决方案, 就是把master与source, 放置在不同git仓库上 这样, 提交source的时候, 就不会触发blog的hook了

sed

sed是基于行进行文本替换的神器, 这里记录一些自己常用的功能

直接替换

  cat data.txt | sed 's/abc/def/g'

删掉空行

  cat data.txt | sed '/^$/d'

删除掉第一行

  cat data.txt | sed '1d'

删除掉每行的第一个字符

  cat data.txt | sed 's/^.//g'

还有一些从其它地方拷贝过来的

sed '1,3d' file //删除1-3行
sed '/[Aa]bc/p' file //打印包含Abc或者abc的行
sed '/ABC/d' //删除包含ABC的行
sed '/ABC/!d' //不删除包含ABC的行
sed '5,$d' //从第五行到最后一行全部删除
sed 1q  //显示第一行
sed 's/$/.../g' //每行的最后面添加 ...
sed 's/d.(sk)/1&/g' //满足d*sk的单词,在单词前添加sk
sed 's/<[^>]*>//g' //去除html标签
sed '/ABC/a123' //在包含ABC的行后面添加一行数值为123的行
sed '/ABC/i123' //跟a不同的是i是插入当前行的前面
sed '/ABC/c123' //包含ABC的行将更改为123
sed '/ABC/{n;s/123/ABC/g}' //精确匹配包含ABC的下一行,将123全部替换成ABC
sed '/ABC/{s/A/a/}' //在包含ABC的行将A替换成a
sed -e '/ABC/h' -e '$g' //将包含ABC的行复制,在最后一行添加.
sed -e '/ABC/{h;d}' -e '/123/G' //将包含ABC的行复制并删除,在包含123的行后添加.
g等于在当前行覆盖,G是在符合条件的行后新增加一行.
sed 's/^/ /g' //每行前面增加一个空格
sed 's/.(.)$/*1/' //将倒数第二位换成*
sed '/ABC/!s/1/*/g' //在不包含ABC的行里,将所有的1替换成*
sed 's/1|2|3/*/g' //在GNU里将不管是1或2或者是3替换成*

还有这个 http://henter.me/post/sed-notes.html http://blog.csdn.net/hepeng597/article/details/11269387