学习一门新的编程语言的最佳途径就是用它来编写一个实用程序。
在 “博客备份” 一文中,编写了两个 python 脚本,然后使用一个命令行将博客备份流程串联起来。这里,我们将使用 Go 来实现。
不太熟悉 Go 语言的读者,可以阅读之前写的两篇文章:“Go语言初尝”,“Go 面向对象简明教程”。
设计五个 go 函数,分别实现如下功能:
为了避免重复在文中粘贴代码,先给出完整程序,读者可以先概览一遍,复制出来,对照解读来看。程序解读会比较简洁,不太明白的地方可以留言哈。
为了调试程序,我们仅选取两篇博文内容组成 XML 文件。
/tmp/cnblogs_backup.xml
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/"
xmlns:wfw="http://wellformedweb.org/CommentAPI/"
xmlns:slash="http://purl.org/rss/1.0/modules/slash/">
<channel>
<title>博客园-编程大观园</title>
<link>https://www.cnblogs.com/lovesqcc/</link>
<description>Enjoy programming, Enjoy life ~~~ 设计,诗歌与爱</description>
<language>zh-cn</language>
<lastBuildDate>Tue, 07 Feb 2023 14:39:24 GMT</lastBuildDate>
<pubDate>Tue, 07 Feb 2023 14:39:24 GMT</pubDate>
<ttl>60</ttl>
<item>
<title>专业精进之路:构建扎实而有实效的编程知识体系</title>
<link>http://www.cnblogs.com/lovesqcc/archive/2023/02/01/17084292.html</link>
<dc:creator>琴水玉</dc:creator>
<author>琴水玉</author>
<pubDate>Wed, 01 Feb 2023 14:05:00 GMT</pubDate>
<guid>http://www.cnblogs.com/lovesqcc/archive/2023/02/01/17084292.html</guid>
<description>
<![CDATA[ blog content ]]>
</description>
</item>
<item>
<title>知乎一答:什么样的知识是有价值的</title>
<link>http://www.cnblogs.com/lovesqcc/archive/2023/01/29/17072238.html</link>
<dc:creator>琴水玉</dc:creator>
<author>琴水玉</author>
<pubDate>Sun, 29 Jan 2023 03:29:00 GMT</pubDate>
<guid>http://www.cnblogs.com/lovesqcc/archive/2023/01/29/17072238.html</guid>
<description>
<![CDATA[ blog content ]]>
</description>
</item>
</channel>
</rss>
执行:
go run blog_backup.go /tmp/cnblogs_backup.xml
或者
go build blog_backup.go
./blog_backup /tmp/cnblogs_backup.xml
blog_backup.go
package main
import (
"fmt"
"os"
"strings"
"encoding/xml"
"io/ioutil"
"net/http"
"github.com/PuerkitoBio/goquery"
"github.com/JohannesKaufmann/html-to-markdown"
)
type BlogRss struct {
XMLName xml.Name `xml:"rss"`
Channel *BlogChannel `xml:channel`
}
type BlogChannel struct {
XMLName xml.Name `xml:"channel"`
Title string `xml:"title"`
Link string `xml:"link"`
Description string `xml:"description"`
Language string `xml:"language"`
LastBuildDate string `xml:"lastBuildDate"`
PubDate string `xml:"pubDate"`
Ttl int `xml:"ttl"`
Items []BlogItem `xml:"item"`
}
type BlogItem struct {
Title string `xml:"title"`
Link string `xml:"link"`
Creator string `xml:"dc:creator"`
Author string `xml:"author"`
PubDate string `xml:"pubDate"`
guid string `xml:"guid"`
Description string `xml:description`
}
type MarkdownFile struct {
Title string
Link string
}
func ReadXml(fpath string) (*BlogRss, error) {
fp,err := os.Open(fpath)
if err != nil {
fmt.Printf("error open file: %s error: %v", fp, err)
return nil, err
}
defer fp.Close()
data, err := ioutil.ReadAll(fp)
if err != nil {
fmt.Printf("error read file: %s error: %v", fpath, err)
return nil, err
}
blogRss := BlogRss{}
err = xml.Unmarshal(data, &blogRss)
if err != nil {
fmt.Printf("error unmarshal xml data: %v error: %v", data, err)
return nil, err
}
blogchannelp := blogRss.Channel
fmt.Printf("%+v\n", *blogchannelp)
blogitems := (*blogchannelp).Items
for _, item := range blogitems {
fmt.Printf("%+v\n", item)
}
return &blogRss, nil
}
func GetMdLinks(blogRss *BlogRss) []MarkdownFile {
blogchannelp := blogRss.Channel
blogitems := (*blogchannelp).Items
mdlinks := make([]MarkdownFile, 0)
for _, item := range blogitems {
mdlinks = append(mdlinks, MarkdownFile{Title: item.Title, Link: item.Link})
}
return mdlinks
}
func GetMdLinksPtr(blogRss *BlogRss) *[]MarkdownFile {
blogchannelp := blogRss.Channel
blogitems := (*blogchannelp).Items
mdlinks := make([]MarkdownFile, 0)
for _, item := range blogitems {
mdlinks = append(mdlinks, MarkdownFile{Title: item.Title, Link: item.Link})
}
return &mdlinks
}
func WriteMarkdown(mdlink MarkdownFile) {
urllink := mdlink.Link
filename := mdlink.Title
resp, err := http.Get(urllink)
if err != nil {
fmt.Printf("error get url: %s error: %v", urllink, err)
}
doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {
fmt.Printf("err: %v", err)
}
postbody := doc.Find("#cnblogs_post_body")
fmt.Printf("%s\n", postbody.Text())
converter := md.NewConverter("", true, nil)
markdown, err := converter.ConvertString(postbody.Text())
if err != nil {
fmt.Printf("err parse html: %v", err)
}
ioutil.WriteFile(filename + ".md", []byte(markdown), 0666)
resp.Body.Close()
}
func SequentialRunMultiple(files []string) {
for _, f := range files {
SequentialRun2(f)
}
}
func SequentialRun(fpath string) {
blogRssp, err := ReadXml(fpath)
if err != nil {
os.Exit(2)
}
mdlinks := GetMdLinks(blogRssp)
for _, mdlink := range mdlinks {
linktrimed := strings.Trim(mdlink.Link, " ")
if linktrimed == "" {
continue
}
WriteMarkdown(mdlink)
}
}
func SequentialRun2(fpath string) {
blogRssp, err := ReadXml(fpath)
if err != nil {
os.Exit(2)
}
mdlinksptr := GetMdLinksPtr(blogRssp)
for i:=0 ; i<len(*mdlinksptr); i++ {
linktrimed := strings.Trim((*mdlinksptr)[i].Link, " ")
if linktrimed == "" {
continue
}
WriteMarkdown((*mdlinksptr)[i])
}
}
func GetFiles() []string {
fpaths := make([]string, 0)
for _, arg := range os.Args[1:] {
argtrimed := strings.Trim(arg, " ")
if argtrimed == "" {
continue
}
fpaths = append(fpaths, argtrimed)
}
fmt.Println(cap(fpaths))
fmt.Println(fpaths)
return fpaths
}
func main() {
SequentialRunMultiple(GetFiles())
}
GetFiles 的作用是从命令行参数获取博客备份文件。支持读取多个博客备份文件。也可以用于测试。比如测试文件不存在的情形。
这里有一个习惯用法。掌握习惯用法,初学者可以很快上手一门新语言。_ 表示用不到,可以忽略这个值。习惯性打印日志,有利于快速排错。
送给编程初学者一句忠告:打几行日志很廉价,但你的时间价值千金。
for _, arg := range os.Args[1:] {
}
解析 XML 的函数见: ReadXml。这里的重点是,要建立与 XML 内容结构对应的 struct 。
要点如下:
xml:"rss"。其下是这个 XML 嵌套的具体元素内容。type BlogRss struct {
XMLName xml.Name `xml:"rss"`
Channel *BlogChannel `xml:channel`
}
这个主要就是结构体遍历操作。对于大量博文来说,直接传入结构体对象切片是开销较大的,可以改成结构体切片指针类型。相应地,可替换成 GetMdLinksPtr 和 SequentialRun2 函数。
注意,go 数组指针不支持指针运算。因此,使用数组指针遍历数组,没有采用 range 方式。
这个函数根据指定链接,下载 HTML 文件,使用 goquery 解析出 HTML 博文内容块,然后使用 html-to-markdown 来生成 Markdown 文件。
基本就是搜索网上相关程序,拼接而成。因此,知识信息搜索能力还是很重要滴。
这个函数的作用就是串联逻辑以上逻辑单元,构建完整流程。
这里展示了程序模块化的优势。当每个逻辑单元设计成短小而正交的,那么整个流程看起来会很清晰。对于编程初学者,这一点尤为重要。
Go 的语法可以说是一个“大杂烩”。虽然融合了很多语言的优点,但感觉缺乏一致性。为了编译省事,把很多负担都转移给了开发者。虽说部分做法初衷是好的。作为一位熟悉多门编程语言的多年开发者,我也感觉不太适应,很容易写错。需要一点时间扭转过来。语言使用的不适应感,会阻滞这门语言的快速普及化。将来再出现一门更易于使用的编程语言,这门语言虽然有先发优势,也会被埋没成为小众语言,即使是大牛操刀而成。
与 python 相比, Go 的依赖管理也令人不满意。python 只要安装了 pip,使用 pip install 安装模块,就可以直接使用了。而 Go 报各种问题,且报错不友好,需要去了解详细信息,对初学者不友好。
引入 goquery 后执行 go run blog_backup.go 报错:
go run blog_backup.go
blog_backup.go:9:5: no required module provides package github.com/PuerkitoBio/goquery: go.mod file not found in current directory or any parent directory; see 'go help modules'
安装 goquery,执行如下命令报错:
➜ basic go get -u github.com/PuerkitoBio/goquery
go: go.mod file not found in current directory or any parent directory.
'go get' is no longer supported outside a module.
To build and install a command, use 'go install' with a version,
like 'go install example.com/cmd@latest'
For more information, see https://golang.org/doc/go-get-install-deprecation
or run 'go help get' or 'go help install'.
执行 如下命令又报错:
➜ basic go install github.com/PuerkitoBio/goquery@latest
go: downloading github.com/PuerkitoBio/goquery v1.8.1
go: downloading github.com/andybalholm/cascadia v1.3.1
go: downloading golang.org/x/net v0.7.0
package github.com/PuerkitoBio/goquery is not a main package
解决办法:退回到 go 项目根目录 gostudy,执行:
go mod init gostudy
go get github.com/PuerkitoBio/goquery
go get github.com/JohannesKaufmann/html-to-markdown
本文采用 Go 语言实现了博客备份。学习一门新的编程语言的最佳途径就是用它来编写一个实用程序,尽可能用到大部分语言特性。
是否有必要学习多种编程语言?学习这么多编程语言又有什么益处? “学习多种编程语言的益处”给出了一些思考。“如何掌握一门编程语言的运用 ”给出了学习一门新编程语言的基本步骤和建议。
就学习Go而言,我接触时间并不长,总计只花了不到一周,而且四天都是在写实用程序,语法熟悉大概两天左右。因为我之前学习和使用过 C, Java, Python, Javascript, Shell, Groovy, Scala,HTML,甚至还接触过 Ruby, CommonLisp,所以,对于 Go 的语法和做法的适应还是很快的(语法不新鲜)。“见多识广”,自然对一门新语言的适应期会快速缩短。这也是学习多种编程语言的益处。
几个月前,我读了一篇关于rubygem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:
网络编程套接字网络编程基础知识理解源`IP`地址和目的`IP`地址理解源MAC地址和目的MAC地址认识端口号理解端口号和进程ID理解源端口号和目的端口号认识`TCP`协议认识`UDP`协议网络字节序socket编程接口`sockaddr``UDP`网络程序服务器端代码逻辑:需要用到的接口服务器端代码`udp`客户端代码逻辑`udp`客户端代码`TCP`网络程序服务器代码逻辑多个版本服务器单进程版本多进程版本多线程版本线程池版本服务器端代码客户端代码逻辑客户端代码TCP协议通讯流程TCP协议的客户端/服务器程序流程三次握手(建立连接)数据传输四次挥手(断开连接)TCP和UDP对比网络编程基础知识
@作者:SYFStrive @博客首页:HomePage📜:微信小程序📌:个人社区(欢迎大佬们加入)👉:社区链接🔗📌:觉得文章不错可以点点关注👉:专栏连接🔗💃:感谢支持,学累了可以先看小段由小胖给大家带来的街舞👉微信小程序(🔥)目录自定义组件-behaviors 1、什么是behaviors 2、behaviors的工作方式 3、创建behavior 4、导入并使用behavior 5、behavior中所有可用的节点 6、同名字段的覆盖和组合规则总结最后自定义组件-behaviors 1、什么是behaviorsbehaviors是小程序中,用于实现
我完全不是程序员,正在学习使用Ruby和Rails框架进行编程。我目前正在使用Ruby1.8.7和Rails3.0.3,但我想知道我是否应该升级到Ruby1.9,因为我真的没有任何升级的“遗留”成本。缺点是什么?我是否会遇到与普通gem的兼容性问题,或者甚至其他我不太了解甚至无法预料的问题? 最佳答案 你应该升级。不要坚持从1.8.7开始。如果您发现不支持1.9.2的gem,请避免使用它们(因为它们很可能不被维护)。如果您对gem是否兼容1.9.2有任何疑问,您可以在以下位置查看:http://www.railsplugins.or
我创建了一个由于“在运行时执行的单例元类定义”而无法编码的对象(这段代码的描述是否正确?)。这是通过以下代码执行的:#defineclassXthatmyusesingletonclassmetaprogrammingfeatures#throughcallofmethod:break_marshalling!classXdefbreak_marshalling!meta_class=class我该怎么做才能使对象编码正确?是否可以从对象instance_of_x的classX中“移除”单例组件?我真的需要一个建议,因为我们的一些对象需要通过Marshal.dump序列化机制进行缓存。
我正在查看Ruby日志记录库Logging.logger方法并从sourceatgithub提出问题与这段代码有关:logger=::Logging::Logger.new(name)logger.add_appendersappenderlogger.additive=falseclass我知道类 最佳答案 这实际上删除了方法(当它实际被执行时)。这是确保close不会被调用两次的保障措施。看起来好像有嵌套的“class 关于Ruby元编程问题,我们在StackOverflow上找到一
使用Paperclip,我想从这样的URL抓取图像:require'open-uri'user.photo=open(url)问题是我最后得到一个像“open-uri20110915-4852-1o7k5uw”这样的文件名。有什么方法可以更改user.photo上的文件名?作为一个额外的变化,Paperclip将我的文件存储在S3上,所以如果我可以在初始分配中设置我想要的文件名就更好了,这样图像就会上传到正确的S3key。像这样:user.photo=open(url),:filename=>URI.parse(url).path 最佳答案
我正在开发一个xcode自动构建系统。在执行一些预构建验证时,我想检查指定的证书文件是否已被撤销。我了解securityverify-cert验证其他证书属性但不验证吊销。我如何检查撤销?我正在用Ruby编写构建系统,但我对任何语言的想法都持开放态度。我阅读了这个答案(Openssl-Howtocheckifacertificateisrevokedornot),但指向底部的链接(DoesOpenSSLautomaticallyhandleCRLs(CertificateRevocationLists)now?)进入的Material对我的目的来说有点过于复杂(用户上传已撤销的证书是一
关闭。这个问题是off-topic.它目前不接受答案。想改进这个问题吗?Updatethequestion所以它是on-topic用于堆栈溢出。关闭11年前。Improvethisquestion我不经常使用ruby-通常它加起来相当于每两个月或更长时间编写一次脚本。我的大部分编程都是使用C++进行的,这与ruby有很大不同。由于我与ruby之间的差距如此之大,我总是忘记语言的基本方面(比如解析文本文件和其他简单的东西)。我想每天练习一些基本的东西,我想知道是否有一些我可以订阅的网站,并且会向我发送当天的Ruby问题或类似的东西。有人知道这样的站点/Internet服务吗?
我一直在寻找一种以编程方式或通过命令行将mp3转换为aac的方法,但没有成功。理想情况下,我有一段代码可以从我的Rails应用程序中调用,将mp3转换为aac。我安装了ffmpeg和libfaac,并能够使用以下命令创建aac文件:ffmpeg-itest.mp3-acodeclibfaac-ab163840dest.aac当我将输出文件的名称更改为dest.m4a时,它无法在iTunes中播放。谢谢! 最佳答案 FFmpeg提供AAC编码功能(如果您已编译它们)。如果您使用的是Windows,则可以从here获取完整的二进制文件。