【Go语言】golang处理正则匹配和常见的正则表达式

今天简单介绍golang如何处理正则表达式。直接上代码了。

package main

import (
	"fmt"
	"regexp"
)

var reg *regexp.Regexp
var pattern string
var source string

func regexpMatch() {
	//  xy 匹配x y
	// x|y  匹配x或者y 优先x
	// source = "asdfdsxxxyyfergsfasfxyfa"
	// pattern = `x|y|a`

	//x* 匹配零个或者多个x,优先匹配多个
	//x+ 匹配一个或者多个x,优先匹配多个
	//x? 匹配零个或者一个x,优先匹配一个

	//source = "xxxxewexxxasdfdsxxxyyfergsfasfxyfa"
	//pattern = `x*`

	// x{n,m} 匹配n个到m个x,优先匹配m个
	// x{n,}  匹配n个到多个x,优先匹配更多
	// x{n} 或者x{n}?  只匹配n个x
	//source = "xxxxxxxewexxxasdfdsxxxyyfergsfasfxyfa"
	//pattern = `x{4,}`

	// x{n,m}? 匹配n个到m个x,优先匹配n个
	// x{n,}?  匹配n个到多个x,优先匹配n个
	// x*?   匹配零个或者多个x,优先匹配0个
	// x+?   匹配一个或者多个x,优先匹配1个
	// x??   匹配零个或者一个x,优先匹配0个
	//source = "xxxxxxxewexxxasdfdsxxxyyfergsfasfxyfa"
	//pattern = `x??`

	//[\d] 或者[^\D] 匹配数字
	//[^\d]或者 [\D] 匹配非数字
	//source = "xx435ff5237yy6346fergsfasfxyfa"
	//pattern = `[\d]{3,}` //匹配3个或者更多个数字

	//source = "xx435ffGUTEYgjk52RYPHFY37yy6346ferg6987sfasfxyfa"
	//pattern = `[a-z]{3,}` //三个或者多个小写字母

	//source = "xx435ffGUTEYgjk52RYPHFY37yy6346ferg6987sfasfxyfa"
	//pattern = `[[:alpha:]]{5,}` //5个或者多个字母,相当于A-Za-z

	//source = "xx435,./$%(*(_&jgshgs发个^$%ffG返回福hjh放假啊啥UTEYgjk52RYPHFY37yy6346ferg6987sfasfxyfa"
	//pattern = `[\p{Han}]+` //匹配连续的汉字

	//source = "13244820821HG74892109977HJA15200806084S11233240697hdgsfhah假发发货"
	//pattern = `1[3|5|7|8|][\d]{9}` //匹配电话号码

	//source = "132@12.comGKGk15@163.cn200806084S11233240697hdgsfhah假发发货"
	//pattern = `\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*` //匹配电子邮箱

	//匹配用户名或者密码 `^[a-zA-Z0-9_-]{4,16}$`  字母或者数字开头,区分大小写,最短4位最长16位
	//匹配IP地址1 `^$(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$`

	//匹配IP地址2
	//pattern = `((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)`

	//匹配日期 年-月-日 `(\d{4}|\d{2})-((1[0-2])|(0?[1-9]))-(([12][0-9])|(3[01])|(0?[1-9]))`
	//匹配日期 月-日-年  `((1[0-2])|(0?[1-9]))/(([12][0-9])|(3[01])|(0?[1-9]))/(\d{4}|\d{2})`
	//匹配时间 小时:分钟 24小时制 ` ((1|0?)[0-9]|2[0-3]):([0-5][0-9]) `
	//匹配邮编  `[1-9][\d]5`
	//匹配URL `[a-zA-z]+://[^\s]*`

	reg = regexp.MustCompile(pattern)
	fmt.Printf("%s", reg.FindAllString(source, -1))

}
func main() {
	regexpMatch()
}

现在介绍常见的正则表达式。参考博客:http://www.cnblogs.com/wenanry/archive/2010/09/06/1819552.html

正则表达式中有很多上述的具有特别意义的字符。首先是下列字符。

<dl compact="compact"><dt>[ ]</dt><dd>范围描述符。[a-z]表示从a到z之间的任意一个。</dd><dt>\w</dt><dd>英文字母和数字。即[0-9 A-Z a-z]。</dd><dt>\W</dt><dd>非英文字母和数字</dd><dt>\s</dt><dd>空字符,即[\t\n\r\f]。</dd><dt>\S</dt><dd>非空字符。</dd><dt>\d</dt><dd>数字,即[0-9]。</dd><dt>\D</dt><dd>非数字。</dd><dt>\b</dt><dd>词边界字符(在范围描述符外部时)</dd><dt>\B</dt><dd>非词边界字符</dd><dt>\b</dt><dd>退格符(0x08)(在范围描述符内部时)</dd><dt>*</dt><dd>前面元素出现0次以上</dd><dt>+</dt><dd>前面元素出现1次以上</dd><dt>{m,n}</dt><dd>前面元素最少出现m次,最多出现n次</dd><dt>?</dt><dd>前面元素出现0次或1次</dd><dt>|</dt><dd>选择</dd><dt>( )</dt><dd>群组</dd><dt>其他字符</dt><dd>该字符本身</dd></dl></div>
</div>
常用正则式

匹配中文字符的正则表达式: [\u4e00-\u9fa5]

匹配双字节字符(包括汉字在内):[^\x00-\xff]

匹配空行的正则表达式:\n[\s| ]*\r

匹配HTML标记的正则表达式:/<(.*)>.*<\/\1>|<(.*) \/>/

匹配首尾空格的正则表达式:(^\s*)|(\s*$)

匹配IP地址的正则表达式:/(\d+)\.(\d+)\.(\d+)\.(\d+)/g //

匹配Email地址的正则表达式:\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*

匹配网址URL的正则表达式:<a href="http://%28/" target="_blank">http://(/</a>[\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?

 sql语句:^(select|drop|delete|create|update|insert).*$

 1、非负整数:^\d+$

 2、正整数:^[0-9]*[1-9][0-9]*$

 3、非正整数:^((-\d+)|(0+))$

 4、负整数:^-[0-9]*[1-9][0-9]*$

 5、整数:^-?\d+$

 6、非负浮点数:^\d+(\.\d+)?$

 7、正浮点数:^((0-9)+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$

 8、非正浮点数:^((-\d+\.\d+)?)|(0+(\.0+)?))$

 9、负浮点数:^(-((正浮点数正则式)))$

10、英文字符串:^[A-Za-z]+$

 11、英文大写串:^[A-Z]+$

 12、英文小写串:^[a-z]+$

 13、英文字符数字串:^[A-Za-z0-9]+$

 14、英数字加下划线串:^\w+$

 15、E-mail地址:^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$

16、URL:^[a-zA-Z]+://(\w+(-\w+)*)(\.(\w+(-\w+)*))*(\?\s*)?$
或:^http:\/\/[A-Za-z0-9]+\.[A-Za-z0-9]+[\/=\?%\-&_~`@[\]\':+!]*([^<>\"\"])*$

17、邮政编码:^[1-9]\d{5}$

 18、中文:^[\u0391-\uFFE5]+$

 19、电话号码:^((\(\d{2,3}\))|(\d{3}\-))?(\(0\d{2,3}\)|0\d{2,3}-)?[1-9]\d{6,7}(\-\d{1,4})?$

 20、手机号码:^((\(\d{2,3}\))|(\d{3}\-))?13\d{9}$

 21、双字节字符(包括汉字在内):^\x00-\xff

 22、匹配首尾空格:(^\s*)|(\s*$)(像vbscript那样的trim函数)

23、匹配HTML标记:<(.*)>.*<\/\1>|<(.*) \/>

 24、匹配空行:\n[\s| ]*\r

 25、提取信息中的网络链接:(h|H)(r|R)(e|E)(f|F) *= *('|")?(\w|\\|\/|\.)+('|"| *|>)?

 26、提取信息中的邮件地址:\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*

 27、提取信息中的图片链接:(s|S)(r|R)(c|C) *= *('|")?(\w|\\|\/|\.)+('|"| *|>)?

 28、提取信息中的IP地址:(\d+)\.(\d+)\.(\d+)\.(\d+)

 29、提取信息中的中国手机号码:(86)*0*13\d{9}

 30、提取信息中的中国固定电话号码:(\(\d{3,4}\)|\d{3,4}-|\s)?\d{8}

 31、提取信息中的中国电话号码(包括移动和固定电话):(\(\d{3,4}\)|\d{3,4}-|\s)?\d{7,14}

 32、提取信息中的中国邮政编码:[1-9]{1}(\d+){5}

 33、提取信息中的浮点数(即小数):(-?\d*)\.?\d+

 34、提取信息中的任何数字 :(-?\d*)(\.\d+)?

 35、IP:(\d+)\.(\d+)\.(\d+)\.(\d+)

 36、电话区号:/^0\d{2,3}$/

 37、腾讯QQ号:^[1-9]*[1-9][0-9]*$

 38、帐号(字母开头,允许5-16字节,允许字母数字下划线):^[a-zA-Z][a-zA-Z0-9_]{4,15}$

 39、中文、英文、数字及下划线:^[\u4e00-\u9fa5_a-zA-Z0-9]+$

Share

【Go语言】golang 并发编程基础

今天简单介绍golang最核心的应用:并发编程。

当今硬件发展迅速,CPU早就变成多核心了,如何处理并发编程以适应多核CPU是每一种现代编程语言做重视的点。golang自出生起就宣扬着并发编程,原生的goroutines和channel 很简洁的支持了复杂的并行操作。
http://blog.csdn.net/gdutliuyun827/article/details/25145535
这篇文章细致分析了Go语言的特色,在此对原作者表示感谢。
下面边上代码边分析实现过程。

func showNum() {
	for i := 0; i &lt; 30; i++ {
		fmt.Println(i)
		atm := time.Duration(rand.Intn(250)) //延迟处理
		time.Sleep(time.Millisecond * atm)
	}
}

这是一个简单的函数,打印出0到29,为了能够看得比较清楚,我做了相关的延迟处理。
如果在主函数中直接去调用这个函数,那么就只能打印一次,这就是非并行化的处理。那么怎么变成并行化呢?很简单,调用的时候在前面加上go就好了。

go showNum()

就像这样。既然是并行,我们就同时调用10次这个函数做一个试验

for i := 0; i &lt; 10; i++ {
		go showNum()
	}

结果你去运行的时候发现什么东西都跑不出来。
不是没有跑出来,太快了,看不到,并且每一次go showNum 的结果都被丢弃了。那么怎么才能看到结果呢?得想一个办法让main停住,那么就写这么一句:

vat in string
fmt.Scanln(&in)

用标准输入给main随便输入一个值。加上这句话以后你去运行就会发现有数字在打印,并且和java 中thread类似,每一次打印都是随机的,无法确定的。这个就是最简单的goroutines了。

简单来说,任何一个函数如果要做并行处理,那么就可以再调用这个函数的时候前面加上关键词go 即可。

要讲的第二个点就是channel,这个是用来实现goroutine之间通信的,就是说两个goroutine之间交换一些信息<当然主要是相关变量>。定义一个channel的方法是向下面这样:

var c1 chan string = make(chan string)
var c2 = make(chan string)
c3 :=make(chan string)

都是可以的。你要注意的是每一个channel必须有一种类型,并且这个类型是不能随便混合使用的。golang中有用很形象的方式来表示信息的传递:这就是<-这个东西,

input <- "lkn"   //input 是一个chan string ,可以接受一个string
data<-input  //input是一个chan string ,用来传出一个string

一般的chan是可以双向传递的,而你如果定义某一个chan只能单向传递的话,可以写成这样:var c2 = make(chan<- string)或者var c2 = make(<-chan string)。前一个表示只能传入,后一个表示只能传出。现在我们用一个简单的打印素数的例子,结合goroutine和channel。

//素数生产者
func prime_generate() chan int {
ch := make(chan int)
go func() {
for i := 2; ; i++ {
ch <- i
}
}()
return ch
}
//素数筛选器
func prime_filter(in chan int, prime int) chan int {
out := make(chan int)
go func() {
for {
if i := <-in; i%prime != 0 {
out <- i
}
}
}()
return out
}
//素数消费者
func prime_sieve() chan int {
out := make(chan int)
go func() {
ch := prime_generate()
for {
prime := <-ch
out <- prime
ch = prime_filter(ch, prime)
}
}()
return out
}

这样就是可以了,主函数里面要这样一直读100次

primes := prime_sieve()
	for i := 0; i < 100; i++ {
		fmt.Println(time.Now(), &lt;-primes)

简单的应用就是这样了,关于并发编程后期会主要讨论,今天先记录到这里。

刘凯宁@C2P
20140805

Share

【Go语言】golang 解析新闻URL获取新闻内容

今天介绍怎么通过Golang解析一个新闻的URL然后抓取新闻内容。
小小的尝试,基本实现了抓取新浪,搜狐、凤凰网、网易、腾讯和新华网这几大网站上的新闻。当然我用的办法是最笨的,就是获取网页所有内容然后想法子把新闻的内容抠出来。现在先上代码吧,代码比较全,注释也很多。

package main

import (

"fmt"

"github.com/axgle/mahonia"

"io/ioutil"

"log"

"net/http"

"strings"

)

//获取URL内容

func getBody(URL string) []byte {

res, err := http.Get(URL)

if err != nil {

log.Fatal(err)

}

robots, err := ioutil.ReadAll(res.Body)

res.Body.Close()

if err != nil {

log.Fatal(err)

}

return robots

}

//处理新闻

func processNews(URL string) {

body := getBody(URL)

sbody := fmt.Sprintf("%s", body)

//fmt.Println(sbody)

var content_slice []byte //内容切片

var mainContentBegin int //开始点

var mainContentEnd int //结束点

//判断URL来自哪里的新闻

if strings.Contains(URL, "xinhuanet") { //新华社新闻

mainContentBegin = strings.Index(sbody, `&lt;div id="content"&gt;`) //开始

mainContentEnd = strings.Index(sbody, "&lt;!--分享--&gt;") //结束

content_slice = body[mainContentBegin : mainContentEnd-1] //切片出内容

} else if strings.Contains(URL, "ifeng.com") { //凤凰网新闻

mainContentBegin = strings.Index(sbody, "&lt;!--mainContent begin--&gt;")

mainContentEnd = strings.Index(sbody, "&lt;!--mainContent end--&gt;")

content_slice = body[mainContentBegin:mainContentEnd]

} else if strings.Contains(URL, "sina.com") { //新浪网新闻

encoding := mahonia.NewDecoder("gbk") //转码

body = []byte(encoding.ConvertString(sbody))

mainContentBegin = strings.Index(string(body), "publish_helper")

mainContentEnd = strings.Index(string(body), "publish_helper_end ")

content_slice = body[mainContentBegin:mainContentEnd]

} else if strings.Contains(URL, "qq.com") { //腾讯网新闻

encoding := mahonia.NewDecoder("gbk") //转码

body = []byte(encoding.ConvertString(sbody))

//fmt.Printf("%s", body)

mainContentBegin = strings.Index(string(body), `bossZone="content"`)

mainContentEnd = strings.Index(string(body), "正文已结束")

content_slice = body[mainContentBegin:mainContentEnd]

} else if strings.Contains(URL, "sohu.com") { //搜狐新闻

encoding := mahonia.NewDecoder("gbk")

body = []byte(encoding.ConvertString(sbody))

//fmt.Printf("%s", body)

mainContentBegin = strings.Index(string(body), `articleBody`)

mainContentEnd = strings.Index(string(body), `&lt;!-- 分享 --&gt;`)

content_slice = body[mainContentBegin:mainContentEnd]

} else if strings.Contains(URL, "163.com") { //网易新闻

encoding := mahonia.NewDecoder("gbk")

body = []byte(encoding.ConvertString(sbody))

//fmt.Printf("%s", body)

mainContentBegin = strings.Index(string(body), `&lt;div id="endText"&gt;`)

mainContentEnd = strings.Index(string(body), `&lt;!-- 分页 --&gt;`)

content_slice = body[mainContentBegin:mainContentEnd]

}

//整理内容

processSlice(fmt.Sprintf("%s", content_slice))

}

//去除HTML标记

func processSlice(content string) {

//HTML标记数组

var count []int

//确定标记位置

for k, v := range content {

if string(v) == `&lt;` || string(v) == `&gt;` {

count = append(count, k)

}

}

//打印内容

for k, _ := range count {

//确定k范围

if k &gt; 0 &amp;&amp; k+1 &lt; len(count) {

if k%2 == 1 {

//如果第一个是&lt;

if string(content[count[0]]) == "&lt;" {

fmt.Printf("%s", content[count[k]+1:count[k+1]])

} else { //如果第一个是&gt;

fmt.Printf("%s", content[count[k-1]+1:count[k]])

}
}
}
}
}
//主函数

func main() {

//URL

var URL string = "写上URL"

//执行

processNews(URL)

}

整份代码注释很多,应该可以看懂。
使用的时候传入一个URL,限于新浪,搜狐、凤凰网、网易、腾讯和新华网这几个网站上的新闻网页,基本能够保证抓取新闻。我试了,凤凰网和新华网的比较严格,抓取成功率较高,而腾讯的是最没有规律的,会抓取到一些额外的东西。主要难点是对字符串的分析和处理上。我用的方法还不是很好,期待会有所改进。
附上图两张:


整体效果就这样了。

刘凯宁@C2P
20140804

Share

【Go语言】golang读写JSON文件

简单介绍golang对JSON文件的读写操作

JSON,JavaScript Object Notation,一种轻量级的数据交换格式,常用来在网络中传递数据,和XML是一种类型的功能。JSON 语法是 JavaScript 语法的子集。具体的用法不是我的重点:如果你不知道什么是JSON的语法,右键百度一下就好了。如果想校验自己的JSON格式的文件是否正确,下面这个网站是你很好的选择:http://www.bejson.com/  值得放入收藏夹哦!
下面上代码:

 type User struct {
 Name string
 Tel string
 Age string
 }

type Info struct {
 Id string
 User []*User
 Collage string
 }
 

现在编码

func marshalJSON() []byte {
user1 := &amp;User{"刘凯宁", "15200806084", "21"}
user2 := &amp;User{"lkn", "13244398812", "12"}
info := Info{"0010", []*User{user1, user2}, "中南大学"}
info_json, _ := json.Marshal(info)
//fmt.Printf("JSON格式:%s", info_json)
file, _ := os.OpenFile("info.json", os.O_CREATE|os.O_WRONLY, 0)
defer file.Close()
ioutil.WriteFile("info.json", info_json, 0x644)
return info_json
}

使用方式:自己构造好struct 然后就可以编码了,照着例子看应该不难。要注意的是,编码的时候只能对struct所有能导出的属性进行编码,就是说大写的属性,小写的属性是不导出的,这个要一定注意

现在解码

func unmarshalJSON(data_JSON []byte) {
data := make(map[string]interface{})
err := json.Unmarshal(data_JSON, &amp;data)
checkError(err)
//data_map := data.(map[string]interface{})
for k, v := range data {
switch valueType := v.(type) {
case string:
fmt.Println(k, "is string", valueType)
case int:
fmt.Println(k, "is int", valueType)
case []interface{}:
fmt.Println(k, "is array:")
for k, u := range valueType {
fmt.Println("k=:", k, " v=:", u)
}
default:
fmt.Println(k, "is a type that I don't know")
}
}
}

使用方式,先读出来文件,生成byte数组,然后去解码就可以了。可以解析位置结构的JSON,你可以随便找几个json文件去试一下啦!

最后附一个复制文件的函数:

//复制文件
func copyFile(sourceFile, resultFile string) {
//source文件
source, err := os.Open(sourceFile)
checkError(err)
defer source.Close()
//复制的文件
result, err := os.OpenFile(resultFile, os.O_WRONLY|os.O_CREATE, 0644)
checkError(err)
defer result.Close()
//复制
io.Copy(result, source)
fmt.Println("复制成功!")

}

刘凯宁@C2P
20140801

Share

【Go语言】golang 文件读写

简单讨论一下golang中文件读写实现,并附上读写CSV文件的方法

文件读写,首先要有文件路径/文件名,读文件的时候,打开文件,读取数据,处理数据;写文件时,新建文件,关闭文件,打开文件,写入数据。基本思路就是这样子的,然后就上代码。代码注释很详细哒

package main
import (
"bufio"
"bytes"
"encoding/csv"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"strings"
)

//读取文件内容
func readContent(path string) {
//打开文件
inputFile, err := os.Open(path)
checkError(err)
//关闭文件
defer inputFile.Close()
fmt.Println("读取到的内容是")
//读取
inputReader := bufio.NewReader(inputFile)
for {
inputString, err := inputReader.ReadString('\n')
//inputString, _, err := inputReader.ReadLine()
//到末尾就停止
if err == io.EOF {
return
}
//打印
fmt.Printf("%s", inputString)
}
}

//检查错误
func checkError(err error) {
if err != nil {
log.Fatal(err)
}
}

//读和写
func readAndWrite(inputFile, outputFile string) {
//读取文件
buffer, err := ioutil.ReadFile(inputFile)
//检查错误
checkError(err)
//打印
fmt.Printf("读到的内容是\n%s", buffer)
//写入文件
err = ioutil.WriteFile(outputFile, buffer, 0x664)
checkError(err)
}

//写CSV文件,输入文件名和data
func writeCSV(fileName string, data [][]string) {
//new buffer
buffer := new(bytes.Buffer)
//new writer
writer := csv.NewWriter(buffer)
//写数据
writer.WriteAll(data)
writer.Flush()
//打印数据
fmt.Println(buffer)
//新建文件
fileOut, err := os.Create(fileName)
defer fileOut.Close()
checkError(err)
//写入内容
fileOut.WriteString(buffer.String())
}

//读取CSV,输入文件名,返回data
func readCSV(fileName string) [][]string {
buffer, err := ioutil.ReadFile(fileName)
checkError(err)
reader := csv.NewReader(strings.NewReader(string(buffer)))
data, _ := reader.ReadAll()

//fmt.Println(data)
for k, v := range data {
fmt.Println(k, "###", v)
}
return data
}

func main() {
//读取文件内容
//path := "输入文件路径"
//readContent(path)

//读取和写入
//inputFile := "输入文件路径"
//outputFile := "输入文件名"
//readAndWrite(inputFile, outputFile)
//readContent(outputFile)

//读CSV文件,编码格式:UTF8!!!!!
//data := readCSV("请你自己输入!!!")
//写CSV文件
//writeCSV("请你自己写名字!!!", data)

}

就是这样了。
也许会有人问我代码是怎么这么写的?这个很简单:贴代码的时候用<pre></pre>这个标签包住就好了

刘凯宁@C2P
20140731

Share