<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Go &#8211; 张三太爷</title>
	<atom:link href="https://www.somedoc.net/category/go/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.somedoc.net</link>
	<description>看前面，黑洞洞</description>
	<lastBuildDate>Thu, 25 Feb 2021 09:01:54 +0000</lastBuildDate>
	<language>zh-Hans</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.7.1</generator>

<image>
	<url>https://www.somedoc.net/wp-content/uploads/2016/12/cropped-dandycheung-1-32x32.jpg</url>
	<title>Go &#8211; 张三太爷</title>
	<link>https://www.somedoc.net</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Go 的数据库操作</title>
		<link>https://www.somedoc.net/2021/02/25/go-%e7%9a%84%e6%95%b0%e6%8d%ae%e5%ba%93%e6%93%8d%e4%bd%9c/</link>
					<comments>https://www.somedoc.net/2021/02/25/go-%e7%9a%84%e6%95%b0%e6%8d%ae%e5%ba%93%e6%93%8d%e4%bd%9c/#respond</comments>
		
		<dc:creator><![CDATA[张三太爷]]></dc:creator>
		<pubDate>Thu, 25 Feb 2021 09:01:54 +0000</pubDate>
				<category><![CDATA[Go]]></category>
		<category><![CDATA[备忘录]]></category>
		<category><![CDATA[技术]]></category>
		<guid isPermaLink="false">https://www.somedoc.net/?p=4767</guid>

					<description><![CDATA[三天不拿手中生。如之前提起，想把一个 Java 的实现用 G <a href="https://www.somedoc.net/2021/02/25/go-%e7%9a%84%e6%95%b0%e6%8d%ae%e5%ba%93%e6%93%8d%e4%bd%9c/" class="more-link">[&#8230;]</a>]]></description>
										<content:encoded><![CDATA[<p>三天不拿手中生。如之前提起，想把一个 Java 的实现用 Go 重构出来，结果上手的时候发现已经又快把基础也忘光了。</p>
<p>Java 里的 mapper，在 Go 里物色相关的东东，找到了以下几个项目：<br />
&#8211; https://github.com/xiuno/dbx<br />
&#8211; https://github.com/gohouse/gorose<br />
&#8211; https://github.com/jmoiron/sqlx<br />
&#8211; https://github.com/facebook/ent<br />
&#8211; https://github.com/xiaolingzi/lingorm</p>
<p>dbx 是第一个上手测试的。把代码拉下来，粗看的时候就有点心惊，再用测试程序一跑，结果令人失望，率先出局（执行到 <code>db.Table("tbl_sth").All(&list)</code> 这样的语句，必崩溃）。</p>
<p>后续的测试，会逐步更新上来。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.somedoc.net/2021/02/25/go-%e7%9a%84%e6%95%b0%e6%8d%ae%e5%ba%93%e6%93%8d%e4%bd%9c/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>vscode 里 gopls 的小性子</title>
		<link>https://www.somedoc.net/2021/02/15/vscode-%e9%87%8c-gopls-%e7%9a%84%e5%b0%8f%e6%80%a7%e5%ad%90/</link>
					<comments>https://www.somedoc.net/2021/02/15/vscode-%e9%87%8c-gopls-%e7%9a%84%e5%b0%8f%e6%80%a7%e5%ad%90/#respond</comments>
		
		<dc:creator><![CDATA[张三太爷]]></dc:creator>
		<pubDate>Mon, 15 Feb 2021 09:02:58 +0000</pubDate>
				<category><![CDATA[Go]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[问题解决]]></category>
		<guid isPermaLink="false">https://www.somedoc.net/?p=4758</guid>

					<description><![CDATA[在学习 go 的道路上，月拱一卒。今天把元旦前搁置的一个工程 <a href="https://www.somedoc.net/2021/02/15/vscode-%e9%87%8c-gopls-%e7%9a%84%e5%b0%8f%e6%80%a7%e5%ad%90/" class="more-link">[&#8230;]</a>]]></description>
										<content:encoded><![CDATA[<p>在学习 go 的道路上，月拱一卒。今天把元旦前搁置的一个工程打开来，继续学习用 go 语言与数据库进行交互，使用了 github.com/xiuno/dbx 这个库。</p>
<p>把例子程序保存为 main.go 后创建了个工程，可是一编译就会报一个错“Error loading workspace: gopls requires a module at the root of your workspace.”。看了别人的踩坑记录才搞清楚，原来是 gopls 的锅，需要在 <code>settings.json</code> 里加上 <code>"gopls": { "experimentalWorkspaceModule": true }</code> 这个配置（加上以后要重启一下 vscode）。</p>
<p>接着又报几处 <code>composite literal uses unkeyed fields</code> 这样的警告。仍然是看他人的踏坑记录，是定义结构对象时初始化成员处没有写成员名导致的，写成 key: value 样式即可。</p>
<p>编译后执行，数据库在创建数据表的时候返回错误“Error 1101: BLOB, TEXT, GEOMETRY or JSON column &#8216;xxx&#8217; can&#8217;t have a default value”。他人的踏坑经验说，这是因为数据库的 sql_mode（可以执行 <code>select @@sql_mode</code> 语句查看）里有 <code>STRICT_TRANS_TABLES</code>，将其去掉重启一下数据库服务即可。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.somedoc.net/2021/02/15/vscode-%e9%87%8c-gopls-%e7%9a%84%e5%b0%8f%e6%80%a7%e5%ad%90/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>vscode 调试 go 项目的一些细节</title>
		<link>https://www.somedoc.net/2020/05/18/vscode-%e8%b0%83%e8%af%95-go-%e9%a1%b9%e7%9b%ae%e7%9a%84%e4%b8%80%e4%ba%9b%e7%bb%86%e8%8a%82/</link>
					<comments>https://www.somedoc.net/2020/05/18/vscode-%e8%b0%83%e8%af%95-go-%e9%a1%b9%e7%9b%ae%e7%9a%84%e4%b8%80%e4%ba%9b%e7%bb%86%e8%8a%82/#respond</comments>
		
		<dc:creator><![CDATA[张三太爷]]></dc:creator>
		<pubDate>Mon, 18 May 2020 10:14:28 +0000</pubDate>
				<category><![CDATA[Go]]></category>
		<category><![CDATA[备忘录]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[vscode]]></category>
		<guid isPermaLink="false">https://www.somedoc.net/?p=4494</guid>

					<description><![CDATA[vscode 的调试，最开始的时候，要手写 launch.j <a href="https://www.somedoc.net/2020/05/18/vscode-%e8%b0%83%e8%af%95-go-%e9%a1%b9%e7%9b%ae%e7%9a%84%e4%b8%80%e4%ba%9b%e7%bb%86%e8%8a%82/" class="more-link">[&#8230;]</a>]]></description>
										<content:encoded><![CDATA[<p>vscode 的调试，最开始的时候，要手写 launch.json，当时嫌麻烦，没学习。搁了一阵子，发现变容易了，不管是 golang 还是 Python，找那个长得像播放键的按钮点就好了，通常会直接针对当前编辑区内的文件开始调试。</p>
<p>上周的时候，远程调试一个 golang 的项目，因为要传入一些参数的原因，被逼着要用 launch.json 了，好在集成环境已经进化到可以自动生成一个模板了，大概长这样。</p><pre class="crayon-plain-tag">{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Launch",
            "type": "go",
            "request": "launch",
            "mode": "auto",
            "program": "${fileDirname}",
            "env": {},
            "args": []
        }
    ]
}</pre><p> </p>
<p>环境的智能化是有几个坑的。对于 go 的项目，尽管都是 <code>launch</code> 请求，然鹅，当你对 <code>program</code> 指定不一样的内容时，其实质是不同的，但在开着 main.go（或者等价的主源文件）进行调试时尤其容易产生作用相同的错觉。</p>
<p>如果 <code>program</code> 里指定为 <code>${fileDirname}</code>，那就是调试当前打开的源文件，而如果指定为 <code>${workspaceFolder}</code> 的话，那就是调试当前的项目。vscode 会把要调试的可执行文件自动编译为 <code>__debug_bin</code> 的文件名，截至本文，老夫尚且不知如何才能指定一个自己想要的文件名，而且，如果写一个不允许改变可执行文件名、在执行时会进行检查的程序的话，那调试还得想辙儿。</p>
<p>还有一个坑是偷懒时踏进去的，在 <code>args</code> 里。当将其指定为 <code>["-c /svc/sdaemon/sd.conf"]</code> 后，发现程序执行的行为不正常，能够感知到要指定配置文件，但却找不到配置文件。调试后得知，在寻找配置文件的时候，代码会试图打开 <code> /svc/sdaemon/sd.conf</code> 而不是 <code>/svc/sdaemon/sd.conf</code>（注意前面多了一个空格）。原本简直就要上手修改命令行解析器的我，在斥责其实现竟然如此幼稚脆弱的惊愕之余，幸而想到把 <code>args</code> 参数改为 <code>["-c", "/svc/sdaemon/sd.conf"]</code> 一试，果然河清海晏。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.somedoc.net/2020/05/18/vscode-%e8%b0%83%e8%af%95-go-%e9%a1%b9%e7%9b%ae%e7%9a%84%e4%b8%80%e4%ba%9b%e7%bb%86%e8%8a%82/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>go 流派的服务治理入门实践</title>
		<link>https://www.somedoc.net/2020/05/14/go-%e6%b5%81%e6%b4%be%e7%9a%84%e6%9c%8d%e5%8a%a1%e6%b2%bb%e7%90%86%e5%85%a5%e9%97%a8%e5%ae%9e%e8%b7%b5/</link>
					<comments>https://www.somedoc.net/2020/05/14/go-%e6%b5%81%e6%b4%be%e7%9a%84%e6%9c%8d%e5%8a%a1%e6%b2%bb%e7%90%86%e5%85%a5%e9%97%a8%e5%ae%9e%e8%b7%b5/#respond</comments>
		
		<dc:creator><![CDATA[张三太爷]]></dc:creator>
		<pubDate>Thu, 14 May 2020 10:10:31 +0000</pubDate>
				<category><![CDATA[Go]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[备忘录]]></category>
		<category><![CDATA[技术]]></category>
		<guid isPermaLink="false">https://www.somedoc.net/?p=4489</guid>

					<description><![CDATA[几天前用 Python 写的统计代码提交情况的小程序基本就绪 <a href="https://www.somedoc.net/2020/05/14/go-%e6%b5%81%e6%b4%be%e7%9a%84%e6%9c%8d%e5%8a%a1%e6%b2%bb%e7%90%86%e5%85%a5%e9%97%a8%e5%ae%9e%e8%b7%b5/" class="more-link">[&#8230;]</a>]]></description>
										<content:encoded><![CDATA[<p>几天前用 Python 写的统计代码提交情况的小程序基本就绪后，需要让它定时执行。最简单实用的方法是系统自己的 cron 来调度。</p>
<p>由于业务上后端已经有 go 的服务在线上，所以考虑看一下这个流派里，对于服务治理有没有什么现成的工具可以用。入门的需求有两个：<br />
1. 定时执行；<br />
2. 对启动、停止、异常等的值守。</p>
<p>前者选出一个名为 gocron 的项目（<a href="https://github.com/ouqiang/gocron">https://github.com/ouqiang/gocron</a>）。在结构上，gocron 分为两个部分，一个是控制管理中心，一个是任务执行节点，即使在同一台设备上，这两者也是分离的。</p>
<p>gocron 依赖 MySQL 数据库，所以在投入使用前要准备一下。命令行下执行 <code>gocron web</code> 即可，此时会默认监听于 <code>5920</code> 端口，如果要从其他设备远程访问的话，记得 <code>iptables -I INPUT -p tcp --dport 5920 -j ACCEPT</code> 把端口放开。用浏览器通过 ip:port 访问，第一次进入显然要把数据库什么的配置好。接着再开展配置用户、增加节点、添加定时任务等工作。</p>
<p>它的定时设置采用 cron 语法，可以精确到秒。我希望每天晚上九点半执行一次任务，配了个 <code>0 30 21 * * */1</code>，最后那个 <code>/1</code> 是多此一举，只不过为了提醒我测试阶段结束后要改一下，改成一周一次即可。对 <code>crontab</code> 的时间周期表达格式不熟练的可以去 <a href="https://tool.lu/crontab">https://tool.lu/crontab</a> 检测结果。</p>
<p>执行节点的启动，命令行 <code>gocron-node</code> 就够了，默认监听于 <code>5921</code>。</p>
<p>接下来需要一个保证它们可以一直在工作的守护者。由于之前用过业内知名的 <code>supervisor</code>，又出于想多接触一下 go 的应用，选择了 go 语言版的 <code>supervisord</code>，项目地址为 <a href="https://github.com/ochinchina/supervisord">https://github.com/ochinchina/supervisord</a>。这个项目没有预编译好的 binary 可用，只好把代码 clone 到本地（恰好服务器上竟然原来有 go 的开发工具链什么的），直接 <code>go build</code> 就顺利通过了。</p>
<p><code>supervisord</code> 的命令行参数一看是很简单的，想让它跑起来的话，<code>supervisord -c configfile</code> 就可以，如果想让它以 Daemon 的方式运行，加个 <code>-d</code> 参数就好。在此认知的基础上，我的配置文件如下：</p><pre class="crayon-plain-tag">[supervisord]
logfile=/srv/supervisor/supervisord.log
logfile_maxbytes=50MB
logfile_backups=10
loglevel=debug
pidfile=/tmp/supervisord.pid

[inet_http_server]
port=127.0.0.1:9090

[program:gocron]
command = /srv/gocron/centre/gocron web

[program:gocron-node]
command = /srv/gocron/node/gocron-node
user = dummy</pre><p>执行起来后服务的效果是没有大问题的，但后续用命令行查询服务状态时发现，所有的命令都不能正确响应。<code>supervisord ctl status</code> 无输出，<code>supervisord ctl pid processname</code> 则都是返回 <code>0</code>。</p>
<p>至此踏入郁闷的远程调试之途，其中曲折容后再总结，到最后的结论是，上述这些控制命令均需后附额外的服务地址参数，针对前面的配置文件，具体参数就是 <code>-s http://127.0.0.1:9090</code> 或者 <code>-s http://localhost:9090</code>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.somedoc.net/2020/05/14/go-%e6%b5%81%e6%b4%be%e7%9a%84%e6%9c%8d%e5%8a%a1%e6%b2%bb%e7%90%86%e5%85%a5%e9%97%a8%e5%ae%9e%e8%b7%b5/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>[转载]Go 语言之 panic 与 recover</title>
		<link>https://www.somedoc.net/2020/02/04/%e8%bd%ac%e8%bd%bdgo-%e8%af%ad%e8%a8%80%e4%b9%8b-panic-%e4%b8%8e-recover/</link>
					<comments>https://www.somedoc.net/2020/02/04/%e8%bd%ac%e8%bd%bdgo-%e8%af%ad%e8%a8%80%e4%b9%8b-panic-%e4%b8%8e-recover/#respond</comments>
		
		<dc:creator><![CDATA[张三太爷]]></dc:creator>
		<pubDate>Tue, 04 Feb 2020 04:16:45 +0000</pubDate>
				<category><![CDATA[Go]]></category>
		<category><![CDATA[技术]]></category>
		<guid isPermaLink="false">http://www.somedoc.net/?p=4286</guid>

					<description><![CDATA[转载自：https://blog.csdn.net/a418 <a href="https://www.somedoc.net/2020/02/04/%e8%bd%ac%e8%bd%bdgo-%e8%af%ad%e8%a8%80%e4%b9%8b-panic-%e4%b8%8e-recover/" class="more-link">[&#8230;]</a>]]></description>
										<content:encoded><![CDATA[<p>转载自：https://blog.csdn.net/a41888313/article/details/79691728，作了使信息更加清晰化的修订。</p>
<h2>error 类型</h2>
<p>官方文档介绍：</p>
<p><img decoding="async" src="https://img-blog.csdn.net/20180326102505435?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2E0MTg4ODMxMw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70" alt="error 官方文档介绍" /><br />
error 这一内建的接口类型，是表示错误状况（可为 nil）的常规接口。</p>
<h2>panic 函数</h2>
<p>官方文档介绍：</p>
<p><img decoding="async" title="" src="https://img-blog.csdn.net/20180325214215920?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2E0MTg4ODMxMw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70" alt="panic 官方文档介绍" /></p>
<p>内建的 panic 函数停止当前 goroutine（协程）的正常执行。当函数 F 调用 panic 时，F 的正常执行立即停止。但是被 F 推迟的函数还都会执行，然后 F 返回到调用者。对于调用者 G 来说，F 的调用就像是调用了 panic 一样，终止 G 的执行并运行任何推迟（带有 defer 关键字）的函数。 这种情况会持续下去，直到正在执行的 goroutine 中所有的函数都以相反的顺序停止。 至此，程序终止，并报告错误情况（包括 panic 的参数值）。这种终止序列被称作 panicking，且可以通过内建的 recover 函数来控制。</p>
<h2>recover 函数</h2>
<p>官方文档介绍：</p>
<p><img decoding="async" title="" src="https://img-blog.csdn.net/20180325215607569?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2E0MTg4ODMxMw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70" alt="函数func 官方解释" /><br />
recover 内置函数允许一段程序管理一个正在 paincking 的 goroutine 的行为。在被 defer 的函数（而非由其调用的任何函数）内部执行 recover 函数，可以恢复正常执行并获取到传递给 panic 调用的错误值，以此来停止 panicking 序列函数的执行。 如果在被 defer 的函数之外调用 recover，则不会停止 panicking 序列的执行。在此情况下，或者是在 goroutine 没有 panicking 时，或者是提供给 panic 的参数为 nil 时，recover 返回 nil。也就是说，recover 函数的返回值报告了协程是否正在遭遇 panicking 。</p>
<p>panic 函数就是往外扔错误，一层接一层往上扔直到当前程序不能运行为止，如果不想让 panic 函数扔的错误导致程序挂掉，就得使用 recover 函数来接收 panic 错误或者说是阻挡 panicking。recover  函数可以将错误转化为 error 类型。panic 错误并不会让 defer 关键字声明的函数也停止运行，也就是说，defer 关键字声明的函数或者代码即使遇到错误也会执行。</p>
<p>一个函数里面有 defer 关键字声明一个函数（假设叫 catch 函数）和会运行出错的代码，在 catch 函数里面调用 recover 函数，recover 会拦截错误，不让错误往上扔，返回给调用者 error（里面有错误的信息）类型 ，从而使 goroutine 不挂掉。</p><pre class="crayon-plain-tag">package main

import (
    "fmt"
    "errors"
)

func main() {
    testPanic()
    afterPanic()
}

func testPanic() {
    // defer catch()
    panic(" \"panic 错误\"")
    fmt.Println("testPanic(): panic() 后的代码")
}

func catch() {
    if r := recover(); r != nil {
        fmt.Println("catch(): recover() 捕获到：", r)

        var err error
        switch x := r.(type) {
        case string:
            err = errors.New(x)
        case error:
            err = x
        default:
            err = errors.New("")
        }

        if err != nil {
            fmt.Println("catch(): 转为 error 后：", err)
        }
    }
}

func afterPanic() {
    fmt.Println("afterPanic(): panic() 后的函数调用")
}</pre><p>运行结果：</p><pre class="crayon-plain-tag">panic: "panic 错误"

goroutine 1 [running]:
main.testPanic()
E:/goCode/src/MyTestGo/src/com.dylan.main/panic/testpanic.go:16 +0x40
main.main()
E:/goCode/src/MyTestGo/src/com.dylan.main/panic/testpanic.go:10 +0x27

Process finished with exit code 2</pre><p>当 panic 函数执行的时候导致后面函数 afterPanic() 不能执行，main 函数也抛出一个错误，整个程序异常退出。</p>
<p>取消代码中注释掉的 defer 关键字调用 catch 函数，程序运行结果：</p><pre class="crayon-plain-tag">catch(): recover() 捕获到："panic 错误"
catch(): 转为 error 后："panic 错误"
afterPanic(): panic() 后的函数调用
Process finished with exit code 0</pre><p>分析：程序正常结束，没有因为 panic（错误）而到导致程序终止挂掉，且后面的 afterPanic() 也执行了。错误被 recover 函数捕获，转化为 error 类型的错误后输出“catch(): 转为 error 后：&#8221;panic 错误&#8221; ”。</p>
<p>一般情况下，不会采用上面这种显式定义 catch 函数的写法，而是在发生 panic 的函数里面写一个匿名的 defer 函数，就可以拦截 panicking， 并且不让程序挂掉和显示错误信息：</p><pre class="crayon-plain-tag">func main() {
    testPanic()
    afterPanic()
}

func testPanic() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("recover() 捕获到：", r)
        }
    }()

    panic(" \"panic 错误\"")
    fmt.Println("testPanic(): panic() 后的代码")
}

func afterPanic() {
    fmt.Println("afterPanic(): panic() 后的函数调用")
}</pre><p>最后如果想将错误信息返回给调用者，可以改为如下：</p><pre class="crayon-plain-tag">func main() {
    err := testPanic()
    if err != nil {
        fmt.Println("main(): testPanic() 返回错误：", err)
    }
    afterPanic()
}

func testPanic() (err error) {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("recover() 捕获到：", r)

            switch x := r.(type) {
            case string:
                err = errors.New(x)
            case error:
                err = x
            default:
                err = errors.New("")
            }
        }
    }()

    panic(" \"panic 错误\"")
    fmt.Println("testPanic(): panic() 后的代码")
    return nil
}

func afterPanic() {
    fmt.Println("afterPanic(): panic() 后的函数调用")
}</pre><p>提前声明一个 error 类型的返回值变量，把错误信息转换到 error 变量，再将之返回给调用者即可。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.somedoc.net/2020/02/04/%e8%bd%ac%e8%bd%bdgo-%e8%af%ad%e8%a8%80%e4%b9%8b-panic-%e4%b8%8e-recover/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>[转载]Go 语言字符串 string 与字符数组 []byte 高效转换</title>
		<link>https://www.somedoc.net/2020/02/04/%e8%bd%ac%e8%bd%bdgo-%e8%af%ad%e8%a8%80%e5%ad%97%e7%ac%a6%e4%b8%b2-string-%e4%b8%8e%e5%ad%97%e7%ac%a6%e6%95%b0%e7%bb%84-byte-%e9%ab%98%e6%95%88%e8%bd%ac%e6%8d%a2/</link>
					<comments>https://www.somedoc.net/2020/02/04/%e8%bd%ac%e8%bd%bdgo-%e8%af%ad%e8%a8%80%e5%ad%97%e7%ac%a6%e4%b8%b2-string-%e4%b8%8e%e5%ad%97%e7%ac%a6%e6%95%b0%e7%bb%84-byte-%e9%ab%98%e6%95%88%e8%bd%ac%e6%8d%a2/#respond</comments>
		
		<dc:creator><![CDATA[张三太爷]]></dc:creator>
		<pubDate>Tue, 04 Feb 2020 02:15:21 +0000</pubDate>
				<category><![CDATA[Go]]></category>
		<category><![CDATA[技术]]></category>
		<guid isPermaLink="false">http://www.somedoc.net/?p=4288</guid>

					<description><![CDATA[转载自：https://www.cnblogs.com/sh <a href="https://www.somedoc.net/2020/02/04/%e8%bd%ac%e8%bd%bdgo-%e8%af%ad%e8%a8%80%e5%ad%97%e7%ac%a6%e4%b8%b2-string-%e4%b8%8e%e5%ad%97%e7%ac%a6%e6%95%b0%e7%bb%84-byte-%e9%ab%98%e6%95%88%e8%bd%ac%e6%8d%a2/" class="more-link">[&#8230;]</a>]]></description>
										<content:encoded><![CDATA[<p>转载自：https://www.cnblogs.com/shuiyuejiangnan/p/9707066.html</p>
<p>string 与 []byte 的直接转换是通过底层数据 copy 实现的，</p><pre class="crayon-plain-tag">　　var a = []byte("hello boy")
　　var b = string(a)</pre><p>这种操作在并发量达到十万百万级别的时候会拖慢程序的处理速度。</p>
<p>通过 gdb 调试来看一下 string 和 []byte 的数据结构：</p>
<div class="cnblogs_code">
<pre class="crayon-plain-tag">(gdb) l main.main
2
3 import (
4     "fmt"
5 )
6
7 func main() {
8     s := "hello, world!"
9     b := []byte(s)
10
11     fmt.Println(s, b)
(gdb) b 11
Breakpoint 1 at 0x487cd9: file /export/home/machao/src/test/strbytes.go, line 11.
(gdb) r
Starting program: /export/home/machao/src/test/test1 

Breakpoint 1, main.main () at /export/home/machao/src/test/strbytes.go:11
11     fmt.Println(s, b)
(gdb) info locals
s = {
  str = 0x4b8ccf "hello, world!level 3 resetload64 failednil stackbaseout of memorys.allocCount=srmount errorstill in listtimer expiredtriggerRatio=unreachable: value method xadd64 failedxchg64 failed nmidlelocked= on "..., len = 13}
b = {array = 0xc4200140e0 "hello, world!", len = 13, cap = 16}
(gdb) ptype s
type = struct string {
    uint8 *str;
    int len;
}
(gdb) ptype b
type = struct []uint8 {
    uint8 *array;
    int len;
    int cap;
}</pre>
</div>
<p>转换后 [ ]byte 底层数组与原 string 内部指针并不相同，以此可确定数据被复制。那么，如不修改数据，仅转换类型，是否可避开复制，从而提升性能？</p>
<p>从 ptype 输出的结构来看，string 可看做 [2]uintptr，而 []byte 则是 [3]uintptr，这便于我们编写代码，无需额外定义结构类型。如此，str2bytes 只需构建 [3]uintptr{ptr, len, len}，而 bytes2str 更简单，直接转换指针类型，忽略掉 cap 即可。</p>
<p>通过 unsafe.Pointer（指针转换）和 uintptr（指针运算）实现转换：</p>
<div class="cnblogs_code">
<pre class="crayon-plain-tag">package main

import (
    "fmt"
    "strings"
    "unsafe"
)

func str2bytes(s string) []byte {
    x := (*[2]uintptr)(unsafe.Pointer(&amp;s))
    h := [3]uintptr{x[0], x[1], x[1]}
    return *(*[]byte)(unsafe.Pointer(&amp;h))
}

func bytes2str(b []byte) string {
    return *(*string)(unsafe.Pointer(&amp;b))
}

func main() {
    s := strings.Repeat("abc", 3)
    b := str2bytes(s)
    s2 := bytes2str(b)
    fmt.Println(b, s2)
}</pre>
</div>
<p></p><pre class="crayon-plain-tag">ZBMAC-C02VQ2BTH:test XXX$ go build -gcflags “-m" -o test main.go
# command-line-arguments
./main.go:9:6: can inline str2bytes
./main.go:15:6: can inline bytes2str
./main.go:21:16: inlining call to str2bytes
./main.go:22:17: inlining call to bytes2str
./main.go:9:28: str2bytes s does not escape
./main.go:10:36: str2bytes &amp;s does not escape
./main.go:12:35: str2bytes &amp;h does not escape
./main.go:15:26: leaking param: b to result ~rl level=0
./main.go:16:35: bytes2str &amp;b does not escape
./main.go:23:13: b escapes to heap
./main.go:23:13: s2 escapes to heap
./main.go:21:16: main &amp;s does not escape
./main.go:21:16: main &amp;h does not escape
./main.go:22:17: main &amp;b does not escape
./main.go:23:13: main ... argument does not escape</pre><p>没有出现逃逸现象。</p><pre class="crayon-plain-tag">package main

import (
    "testing"
    "io/ioutil"
    "time"
    "fmt"
)

var s, _ = ioutil.ReadFile("mydata4vipday.720.datx")

func test() {
    b := string(s)
    _ = []byte(b)
}

func test2() {
    b := bytes2str(s)
    _ = str2bytes(b)
}

func BenchmarkTest(b *testing.B) {
    t1 := time.Now()
    for i := 0; i &lt; b.N; i++ {
        test()
    }
    fmt.Println("test", time.Now().Sub(t1), b.N)
}

func BenchmarkTestBlock(b *testing.B) {
    t1 := time.Now()
    for i := 0; i &lt; b.N; i++ {
        test2()
    }
    fmt.Println("test block", time.Now().Sub(t1), b.N)
}</pre><p>对比一下优化前后的性能差异</p><pre class="crayon-plain-tag">ZBMAC-C02VQ2BTH:test XXX$ go test -v -bench . -benchmem
test 26.11237ms 1
goos: darwin
goarch: amd64
BenchmarkTest-8         test 206.728605ms 50
test 1.100685701s 300
     300           3669038 ns/op       39075857 B/op         2 allocs/op
test block 756ns 1
BenchmarkTestBlock-8    test block 1.04µs 100
test block 33.199µs 10000
test block 3.24411ms 1000000
test block 322.757612ms 100000000
test block 1.614465444s 500000000
500000000               3.23 ns/op            0 B/op         0 allocs/op
PASS
ok       _/Users/XXX/Go/src/test        3.298s</pre><p>没有额外开辟内存：0B/op，执行效率：5 亿次耗时 1.6 秒；而不用 unsafe.Pointer 和 uintptr 转换 300 次耗时就达到了 1.1 秒，效率对比高下立判。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.somedoc.net/2020/02/04/%e8%bd%ac%e8%bd%bdgo-%e8%af%ad%e8%a8%80%e5%ad%97%e7%ac%a6%e4%b8%b2-string-%e4%b8%8e%e5%ad%97%e7%ac%a6%e6%95%b0%e7%bb%84-byte-%e9%ab%98%e6%95%88%e8%bd%ac%e6%8d%a2/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>[转载]Go 语言之 const 内的 iota</title>
		<link>https://www.somedoc.net/2020/02/03/%e8%bd%ac%e8%bd%bdgo-%e8%af%ad%e8%a8%80%e4%b9%8b-const-%e5%86%85%e7%9a%84-iota/</link>
					<comments>https://www.somedoc.net/2020/02/03/%e8%bd%ac%e8%bd%bdgo-%e8%af%ad%e8%a8%80%e4%b9%8b-const-%e5%86%85%e7%9a%84-iota/#respond</comments>
		
		<dc:creator><![CDATA[张三太爷]]></dc:creator>
		<pubDate>Mon, 03 Feb 2020 13:46:44 +0000</pubDate>
				<category><![CDATA[Go]]></category>
		<category><![CDATA[技术]]></category>
		<guid isPermaLink="false">http://www.somedoc.net/?p=4284</guid>

					<description><![CDATA[转载自：https://www.cnblogs.com/mi <a href="https://www.somedoc.net/2020/02/03/%e8%bd%ac%e8%bd%bdgo-%e8%af%ad%e8%a8%80%e4%b9%8b-const-%e5%86%85%e7%9a%84-iota/" class="more-link">[&#8230;]</a>]]></description>
										<content:encoded><![CDATA[<p>转载自：https://www.cnblogs.com/mingbai/p/goConstItoa.html</p>
<h4>iota 是什么</h4>
<p>const 内的 iota 是 golang 的常量计数器，只能在常量的表达式中使用，即 const 内。</p>
<p>iota 在 const 关键字出现时将被重置为 0（const 内部的第一行之前），const 中每新增一行常量声明将使 iota 计数一次。</p>
<p>可以参照行号理解，也就是说将 iota 理解为 const 语句块中的行索引。</p>
<h4>iota 的特性</h4>
<p>通过例子说明其各种特性。</p>
<p>1、每次 const 出现时，都会让 iota 初始化为 0。</p><pre class="crayon-plain-tag">const a = iota // a=0
const (
    b = iota   // b=0
    c          // c=1
)</pre><p>2、自定义类型</p>
<p>自增长常量经常包含一个自定义枚举类型，允许你依靠编译器完成自增设置。</p><pre class="crayon-plain-tag">type Newtype int

const (
    T1 Newtype = iota // 0
    T2                // 1
    T3                // 2
    T4                // 3
)</pre><p>3、可跳过的值</p><pre class="crayon-plain-tag">type AudioOutput int

const (
    OutMute AudioOutput = iota // 0
    OutMono                    // 1
    OutStereo                  // 2
    _
    _
    OutSurround                // 5
)</pre><p>4、位掩码表达式</p><pre class="crayon-plain-tag">type Allergen int

const (
    IgEggs Allergen = 1 &lt;&lt; iota // 1 &lt;&lt; 0 which is 00000001
    IgChocolate                 // 1 &lt;&lt; 1 which is 00000010
    IgNuts                      // 1 &lt;&lt; 2 which is 00000100
    IgStrawberries              // 1 &lt;&lt; 3 which is 00001000
    IgShellfish                 // 1 &lt;&lt; 4 which is 00010000
)</pre><p>5、定义数量级</p><pre class="crayon-plain-tag">type ByteSize float64

const (
    _ = iota                       // ignore first value by assigning to blank identifier
    KB ByteSize = 1 &lt;&lt; (10 * iota) // 1 &lt;&lt; (10*1)
    MB                             // 1 &lt;&lt; (10*2)
    GB                             // 1 &lt;&lt; (10*3)
    TB                             // 1 &lt;&lt; (10*4)
    PB                             // 1 &lt;&lt; (10*5)
    EB                             // 1 &lt;&lt; (10*6)
    ZB                             // 1 &lt;&lt; (10*7)
    YB                             // 1 &lt;&lt; (10*8)
)</pre><p>6、定义在一行的情况</p>
<p>跟普通形式 没什么不同</p><pre class="crayon-plain-tag">const (
    Apple, Banana = iota + 1, iota + 2
    Cherimoya, Durian
    Elderberry, Fig
)</pre><p>iota 在下一行增长，而不是立即取得它的引用。</p><pre class="crayon-plain-tag">// Apple: 1
// Banana: 2
// Cherimoya: 2
// Durian: 3
// Elderberry: 3
// Fig: 4</pre><p>7、中间插队</p>
<p>中间插队时，iota 会被覆盖掉 ，不再继续自增。但是用另一个 iota 接一下，又会继续自增。 示例如下，中间插入了 5 和 6， 5 下面有 iota 接，6 没有。</p><pre class="crayon-plain-tag">const (
    a = iota
    b = 5
    c = iota
    d = 6
    e
    f
)</pre><p>那么打印出来的结果是 <pre class="crayon-plain-tag">0 5 2 6 6 6</pre>。</p>
<h4>好处和坏处</h4>
<p>使用 iota 能简化定义，在定义枚举时很有用。当代码需要改动的时候，也比较易于拓展或者修改。另外看起来也是有些逼格在里边的。</p>
<p>但是 itoa 令代码相对的不那么的明了易懂。会加大理解代码的负担。如果刚好遇上一个同事不太懂 itoa 的用法又不愿意学习，会加大挖坑的可能性。</p>
<p>所以建议适当的用，即只用其简单的特性就好了。 主张：代码的基本需求之一是 给人看的。机器跑起来没问题，让人能易于看懂才是好代码。</p>
<p>参考：<a class="uri" href="https://blog.csdn.net/xuduorui/article/details/78653895">https://blog.csdn.net/xuduorui/article/details/78653895</a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.somedoc.net/2020/02/03/%e8%bd%ac%e8%bd%bdgo-%e8%af%ad%e8%a8%80%e4%b9%8b-const-%e5%86%85%e7%9a%84-iota/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>[转载]Go 语言之指针</title>
		<link>https://www.somedoc.net/2020/02/03/%e8%bd%ac%e8%bd%bdgo-%e8%af%ad%e8%a8%80%e4%b9%8b%e6%8c%87%e9%92%88/</link>
					<comments>https://www.somedoc.net/2020/02/03/%e8%bd%ac%e8%bd%bdgo-%e8%af%ad%e8%a8%80%e4%b9%8b%e6%8c%87%e9%92%88/#respond</comments>
		
		<dc:creator><![CDATA[张三太爷]]></dc:creator>
		<pubDate>Mon, 03 Feb 2020 13:25:26 +0000</pubDate>
				<category><![CDATA[Go]]></category>
		<category><![CDATA[技术]]></category>
		<guid isPermaLink="false">http://www.somedoc.net/?p=4282</guid>

					<description><![CDATA[转载自：http://c.biancheng.net/vie <a href="https://www.somedoc.net/2020/02/03/%e8%bd%ac%e8%bd%bdgo-%e8%af%ad%e8%a8%80%e4%b9%8b%e6%8c%87%e9%92%88/" class="more-link">[&#8230;]</a>]]></description>
										<content:encoded><![CDATA[<p>转载自：http://c.biancheng.net/view/21.html。</p>
<p>与 Java 和 .NET 等编程语言不同，Go 语言为程序员提供了控制数据结构指针的能力，但是，并不能进行指针运算。Go 语言允许控制特定集合的数据结构、分配的数量以及内存访问模式，这对于构建运行良好的系统是非常重要的。指针对于性能的影响不言而喻，如果想要做系统编程、操作系统或者网络应用，指针更是不可或缺的一部分。</p>
<p>指针（pointer）在  Go语言中可以被拆分为两个核心概念：</p>
<ul>
<li>类型指针，允许对这个指针类型的数据进行修改，传递数据可以直接使用指针，而无须复制数据，类型指针不能进行偏移和运算。</li>
<li>切片，由指向起始元素的原始指针、元素数量和容量组成。</li>
</ul>
<p>受益于这样的约束和拆分，Go 语言的指针类型变量既拥有指针高效访问的特点，又不会发生指针偏移，从而避免了非法修改关键性数据的问题。同时，垃圾回收也比较容易对不会发生偏移的指针进行检索和回收。</p>
<p>切片比原始指针具备更强大的特性，而且更为安全。切片在发生越界时，运行时会报出宕机，并打出堆栈，而原始指针只会崩溃。</p>
<h4>C/C++ 中的指针</h4>
<p>说到 C/C++ 中的指针，会让许多人“谈虎色变”，尤其是对指针的偏移、运算和转换。</p>
<p>其实，指针是 C/C++ 语言拥有极高性能的根本所在，在操作大块数据和做偏移时既方便又便捷。因此，操作系统依然使用  C语言及指针的特性进行编写。</p>
<p>C/C++ 中指针饱受诟病的根本原因是指针的运算和内存释放，C/C++ 语言中的裸指针可以自由偏移，甚至可以在某些情况下偏移进入操作系统的核心区域，我们的计算机操作系统经常需要更新、修复漏洞的本质，就是为解决指针越界访问所导致的“缓冲区溢出”的问题。</p>
<p>要明白指针，需要知道几个概念：指针地址、指针类型和指针取值，下面将展开详细说明。</p>
<h2>认识指针地址和指针类型</h2>
<p>一个指针变量可以指向任何一个值的内存地址，它所指向的值的内存地址在 32 和 64 位机器上分别占用 4 或 8 个字节，占用字节的大小与所指向的值的大小无关。当一个指针被定义后没有分配到任何变量时，它的默认值为 nil。指针变量通常缩写为 ptr。</p>
<p>每个变量在运行时都拥有一个地址，这个地址代表变量在内存中的位置。Go 语言中使用在变量名前面添加<pre class="crayon-plain-tag">&amp;</pre>操作符（前缀）来获取变量的内存地址（取地址操作），格式如下：</p><pre class="crayon-plain-tag">ptr := &amp;v    // v 的类型为 T</pre><p>其中 v 代表被取地址的变量，变量 v 的地址使用变量 ptr 进行接收，ptr 的类型为<pre class="crayon-plain-tag">*T</pre>，称做 T 的指针类型，<pre class="crayon-plain-tag">*</pre>代表指针。</p>
<p>指针实际用法，可以通过下面的例子了解：</p><pre class="crayon-plain-tag">package main

import (
    "fmt"
)

func main() {
    var cat int = 1
    var str string = "banana"
    fmt.Printf("%p %p", &amp;cat, &amp;str)
}</pre><p>运行结果：</p><pre class="crayon-plain-tag">0xc042052088 0xc0420461b0</pre><p>代码说明如下：</p>
<ul>
<li>第 8 行，声明整型变量 cat。</li>
<li>第 9 行，声明字符串变量 str。</li>
<li>第 10 行，使用 fmt.Printf 的动词<pre class="crayon-plain-tag">%p</pre>打印 cat 和 str 变量的内存地址，指针的值是带有<pre class="crayon-plain-tag">0x</pre>十六进制前缀的一组数据。</li>
</ul>
<p>提示：变量、指针和地址三者的关系是，每个变量都拥有地址，指针的值就是地址。</p>
<h2>从指针获取指针指向的值</h2>
<p>当使用<pre class="crayon-plain-tag">&amp;</pre>操作符对普通变量进行取地址操作并得到变量的指针后，可以对指针使用<pre class="crayon-plain-tag">*</pre>操作符，也就是指针取值，代码如下。</p><pre class="crayon-plain-tag">package main

import (
    "fmt"
)

func main() {
    // 准备一个字符串类型
    var house = "Malibu Point 10880, 90265"

    // 对字符串取地址, ptr 类型为 *string
    ptr := &amp;house

    // 打印 ptr 的类型
    fmt.Printf("ptr type: %T\n", ptr)

    // 打印 ptr 的指针地址
    fmt.Printf("address: %p\n", ptr)

    // 对指针进行取值操作
    value := *ptr

    // 取值后的类型
    fmt.Printf("value type: %T\n", value)

    // 指针取值后就是指向变量的值
    fmt.Printf("value: %s\n", value)
}</pre><p>运行结果：</p><pre class="crayon-plain-tag">ptr type: *string
address: 0xc0420401b0
value type: string
value: Malibu Point 10880, 90265</pre><p>代码说明如下：</p>
<ul>
<li>第 10 行，准备一个字符串并赋值。</li>
<li>第 13 行，对字符串取地址，将指针保存到变量 ptr 中。</li>
<li>第 16 行，打印变量 ptr 的类型，其类型为 *string。</li>
<li>第 19 行，打印 ptr 的指针地址，地址每次运行都会发生变化。</li>
<li>第 22 行，对 ptr 指针变量进行取值操作，变量 value 的类型为 string。</li>
<li>第 25 行，打印取值后 value 的类型。</li>
<li>第 28 行，打印 value 的值。</li>
</ul>
<p>取地址操作符<pre class="crayon-plain-tag">&amp;</pre>和取值操作符<pre class="crayon-plain-tag">*</pre>是一对互补操作符，<pre class="crayon-plain-tag">&amp;</pre>取出地址，<pre class="crayon-plain-tag">*</pre>根据地址取出地址指向的值。</p>
<p>变量、指针地址、指针变量、取地址、取值的相互关系和特性如下：</p>
<ul>
<li>对变量进行取地址操作使用<pre class="crayon-plain-tag">&amp;</pre>操作符，可以获得这个变量的指针变量。</li>
<li>指针变量的值是指针地址。</li>
<li>对指针变量进行取值操作使用<pre class="crayon-plain-tag">*</pre>操作符，可以获得指针变量指向的原变量的值。</li>
</ul>
<h2>使用指针修改值</h2>
<p>通过指针不仅可以取值，也可以修改值。</p>
<p>前面已经演示了使用多重赋值的方法进行数值交换，使用指针同样可以进行数值交换，代码如下：</p><pre class="crayon-plain-tag">package main

import "fmt"

// 交换函数
func swap(a, b *int) {
    // 取 a 指针的值, 赋给临时变量 t
    t := *a

    // 取 b 指针的值, 赋给 a 指针指向的变量
    *a = *b

    // 将 a 指针的值赋给b指针指向的变量
    *b = t
}

func main() {
    // 准备两个变量, 赋值 1 和 2
    x, y := 1, 2

    // 交换变量值
    swap(&amp;x, &amp;y)

    // 输出变量值
    fmt.Println(x, y)
}</pre><p>运行结果：</p><pre class="crayon-plain-tag">2 1</pre><p>代码说明如下：</p>
<ul>
<li>第 6 行，定义一个交换函数，参数为 a、b，类型都为 *int 指针类型。</li>
<li>第 9 行，取指针 a 的值，并把值赋给变量 t，t 此时是 int 类型。</li>
<li>第 12 行，取 b 的指针值，赋给指针 a 指向的变量。注意，此时<pre class="crayon-plain-tag">*a</pre>的意思不是取 a 指针的值，而是“a 指向的变量”。</li>
<li>第 15 行，将 t 的值赋给指针 b 指向的变量。</li>
<li>第 21 行，准备 x、y 两个变量，分别赋值为 1 和 2，类型为 int。</li>
<li>第 24 行，取出 x 和 y 的地址作为参数传给 swap() 函数进行调用。</li>
<li>第 27 行，交换完毕时，输出 x 和 y 的值。</li>
</ul>
<p><pre class="crayon-plain-tag">*</pre>操作符作为右值时，意义是取指针的值，作为左值时，也就是放在赋值操作符的左边时，表示 a 指针指向的变量。其实归纳起来，<pre class="crayon-plain-tag">*</pre>操作符的根本意义就是操作指针指向的变量。当操作在右值时，就是取指向变量的值，当操作在左值时，就是将值设置给指向的变量。</p>
<p>如果在 swap() 函数中交换操作的是指针值，会发生什么情况？可以参考下面代码：</p><pre class="crayon-plain-tag">package main

import "fmt"

func swap(a, b *int) {
    b, a = a, b
}

func main() {
    x, y := 1, 2
    swap(&amp;x, &amp;y)
    fmt.Println(x, y)
}</pre><p>运行结果：</p><pre class="crayon-plain-tag">1 2</pre><p>结果表明，交换是不成功的。上面代码中的 swap() 函数交换的是 a 和 b 的地址，在交换完毕后，a 和 b 的变量值确实被交换。但和 a、b 关联的两个变量并没有实际关联。这就像写有两座房子的卡片放在桌上一字摊开，交换两座房子的卡片后并不会对两座房子有任何影响。</p>
<h2>示例：使用指针变量获取命令行的输入信息</h2>
<p>Go 语言内置的 flag 包实现了对命令行参数的解析，flag 包使得开发命令行工具更为简单。</p>
<p>下面的代码通过提前定义一些命令行指令和对应的变量，并在运行时输入对应的参数，经过 flag 包的解析后即可获取命令行的数据。</p>
<p>【示例】获取命令行输入：</p><pre class="crayon-plain-tag">package main

// 导入系统包
import (
    "flag"
    "fmt"
)

// 定义命令行参数
var mode = flag.String("mode", "", "process mode")

func main() {
    // 解析命令行参数
    flag.Parse()

    // 输出命令行参数
    fmt.Println(*mode)
}</pre><p>将这段代码命名为 main.go，然后使用如下命令行运行：</p><pre class="crayon-plain-tag">go run main.go --mode=fast</pre><p>命令行输出结果如下：</p><pre class="crayon-plain-tag">fast</pre><p>代码说明如下：</p>
<ul>
<li>第 10 行，通过 flag.String，定义一个 mode 变量，这个变量的类型是 *string。后面 3 个参数分别如下：
<ul>
<li>参数名称：在命令行输入参数时，使用这个名称。</li>
<li>参数值的默认值：与 flag 所使用的函数创建变量类型对应，String 对应字符串、Int 对应整型、Bool 对应布尔型等。</li>
<li>参数说明：使用 -help 时，会出现在说明中。</li>
</ul>
</li>
<li>第 15 行，解析命令行参数，并将结果写入到变量 mode 中。</li>
<li>第 18 行，打印 mode 指针所指向的变量。</li>
</ul>
<p>由于之前已经使用 flag.String 注册了一个名为 mode 的命令行参数，flag 底层知道怎么解析命令行，并且将值赋给 mode*string 指针，在 Parse 调用完毕后，无须从 flag 获取值，而是通过自己注册的这个 mode 指针获取到最终的值。代码运行流程如下图所示。<br />
<img decoding="async" src="http://c.biancheng.net/uploads/allimg/180813/1-1PQ311430K50.jpg" alt="" /><br />
图：命令行参数与变量的关系</p>
<h2>创建指针的另一种方法——new() 函数</h2>
<p>Go 语言还提供了另外一种方法来创建指针变量，格式如下：</p><pre class="crayon-plain-tag">new(类型)</pre><p>一般这样写：</p><pre class="crayon-plain-tag">str := new(string)
*str = "Go语言教程"

fmt.Println(*str)</pre><p>new() 函数可以创建一个对应类型的指针，创建过程会分配内存，被创建的指针指向默认值。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.somedoc.net/2020/02/03/%e8%bd%ac%e8%bd%bdgo-%e8%af%ad%e8%a8%80%e4%b9%8b%e6%8c%87%e9%92%88/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>[转载]Go 语言之切片</title>
		<link>https://www.somedoc.net/2020/02/03/%e8%bd%ac%e8%bd%bdgo-%e8%af%ad%e8%a8%80%e4%b9%8b%e5%88%87%e7%89%87/</link>
					<comments>https://www.somedoc.net/2020/02/03/%e8%bd%ac%e8%bd%bdgo-%e8%af%ad%e8%a8%80%e4%b9%8b%e5%88%87%e7%89%87/#respond</comments>
		
		<dc:creator><![CDATA[张三太爷]]></dc:creator>
		<pubDate>Mon, 03 Feb 2020 10:37:18 +0000</pubDate>
				<category><![CDATA[Go]]></category>
		<category><![CDATA[技术]]></category>
		<guid isPermaLink="false">http://www.somedoc.net/?p=4278</guid>

					<description><![CDATA[转载自：https://blog.csdn.net/what <a href="https://www.somedoc.net/2020/02/03/%e8%bd%ac%e8%bd%bdgo-%e8%af%ad%e8%a8%80%e4%b9%8b%e5%88%87%e7%89%87/" class="more-link">[&#8230;]</a>]]></description>
										<content:encoded><![CDATA[<p>转载自：https://blog.csdn.net/whatday/article/details/98244776</p>
<h2>一、一般操作</h2>
<h3>1. 声明变量，go 自动初始化为 nil，长度 0，地址 0，nil</h3>
<p></p><pre class="crayon-plain-tag">func main() {
    var ss []string
    fmt.Printf("length: %v, addr: %p, isnil: %v.", len(ss), ss, ss == nil)
}</pre><p></p><pre class="crayon-plain-tag">Running...

length: 0, addr: 0x0, isnil: true.
Success: process exited with code 0.</pre><p></p>
<h3>2. 切片的追加、删除、插入</h3>
<p></p><pre class="crayon-plain-tag">func main() {
    var ss []string;
    fmt.Printf("[local print] length: %v, addr: %p, isnil: %v.\n", len(ss), ss, ss == nil)
    print("func print", ss)

    // 切片尾部追加元素/append elemnt
    for i:=0; i&lt;10; i++ {
        ss = append(ss, fmt.Sprintf("s%d", i))
    }
    fmt.Printf("[local print] length: %v, addr: %p, isnil: %v.\n", len(ss), ss, ss == nil)
    print("after append", ss)

    // 删除切片元素/remove element at index
    index := 5
    ss = append(ss[:index], ss[index+1:]...)
    print("after delete", ss)

    // 在切片中间插入元素/insert element at index
    // 注意：保存后部剩余元素，必须新建一个临时切片
    rear := append([]string{}, ss[index:]...)
    ss = append(ss[0:index], "inserted")
    ss = append(ss, rear...)
    print("after insert", ss)
}

func print(msg string, ss []string) {
    fmt.Printf("[%20s] length: %v, addr: %p, isnil: %v, content: %v.", msg, len(ss), ss, ss == nil, ss)
    fmt.Println()
}</pre><p></p><pre class="crayon-plain-tag">Running...

[local print] length: 0, addr: 0x0, isnil: true.
[func print] length: 0, addr: 0x0, isnil: true, content:[].
[local print] length: 10, addr: 0xc208056000, isnil: false.
[after append] length: 10, addr: 0xc208056000, isnil: false, content:[s0 s1 s2 s3 s4 s5 s6 s7 s8 s9].
[after delete] length: 9, addr: 0xc208056000, isnil: false, content:[s0 s1 s2 s3 s4 s6 s7 s8 s9].
[after insert] length: 10, addr: 0xc208056000, isnil: false, content:[s0 s1 s2 s3 s4 inserted s6 s7 s8 s9].
Success: process exited with code 0.</pre><p></p>
<h3>3. copy 的使用</h3>
<p>在使用 copy 复制切片之前，要保证目标切片有足够的大小，注意是大小，而不是容量，还是看例子：</p><pre class="crayon-plain-tag">func main() {
    var sa = make([]string, 0)
    for i:=0; i&lt;10; i++ {
        sa = append(sa, fmt.Sprintf("%v", i))
    }

    var da = make([]string, 0, 10)
    var cc = 0
    cc = copy(da, sa)
    fmt.Printf("copy to da(len=%d), %v.\n", len(da), da)
    da = make([]string, 5)
    cc = copy(da, sa)
    fmt.Printf("copy to da(len=%d), copied = %d %v.\n", len(da), cc, da)
    da = make([]string, 10)
    cc = copy(da, sa)
    fmt.Printf("copy to da(len=%d), copied = %d %v.\n", len(da), cc, da)
}</pre><p></p><pre class="crayon-plain-tag">Running...

copy to da(len=0), [].
copy to da(len=5), copied = 5 [0 1 2 3 4].
copy to da(len=10), copied = 10 [0 1 2 3 4 5 6 7 8 9].</pre><p>从上面运行结果，明显看出，目标切片大小 0，容量 10，copy 不能复制。目标切片大小小于源切片大小，copy 就按照目标切片大小复制，不会报错。</p>
<h2>二、初始大小和容量</h2>
<p>当我们使用 make 初始化切片的时候，必须给出 size。go 语言的书上一般都会告诉我们，当切片有足够大小的时候，append 操作是非常快的。但是当给出初始大小后，我们得到的实际上是一个含有这个 size 数量切片类型的空元素，看例子：</p><pre class="crayon-plain-tag">func main() {
    var ss = make([]string, 10)
    ss = append(ss, "last")
    print("after append", ss)
}</pre><p></p><pre class="crayon-plain-tag">Running...

[after append] length: 11, addr: 0xc20804c000, isnil: false, content: [last].</pre><p>实际上，此时我们应该先用下标为切片元素赋值。但是如果我们既想有好的效率，又想继续使用 append 函数而不想区分是否有空的元素，此时就要请出 make 的第三个参数：容量，也就是我们通过传递给 make，0 的大小和足够大的容量数值就行了。</p><pre class="crayon-plain-tag">func main() {
    var ss = make([]string, 0, 10)
    ss = append(ss, "last")
    print("after append", ss)
}</pre><p></p><pre class="crayon-plain-tag">Running...

[after append] length: 1, addr: 0xc20804a000, isnil: false, content: [last].</pre><p></p>
<h2>三、切片的指针</h2>
<p>1. 当我们用 append 追加元素到切片时，如果容量不够，go 就会创建一个新的切片变量，看下面程序的执行结果：</p><pre class="crayon-plain-tag">func main() {
    var sa []string
    fmt.Printf("addr: %p, len: %v, content: %v.\n", sa, len(sa), sa)
    for i:=0; i&lt;10; i++ {
        sa = append(sa, fmt.Sprintf("%v", i))
        fmt.Printf("addr:%p, len: %v, content: %v.\n", sa, len(sa), sa)
    }
    fmt.Printf("addr: %p, len: %v, content: %v.\n", sa, len(sa), sa)
}</pre><p></p><pre class="crayon-plain-tag">Running ...

addr: 0x0, len: 0, content: [].
addr: 0x1030e0c8, len: 1, content: [0].
addr: 0x10328120, len: 2, content: [0 1].
addr: 0x10322180, len: 3, content: [0 1 2].
addr: 0x10322180, len: 4, content: [0 1 2 3].
addr: 0x10342080, len: 5, content: [0 1 2 3 4].
addr: 0x10342080, len: 6, content: [0 1 2 3 4 5].
addr: 0x10342080, len: 7, content: [0 1 2 3 4 5 6].
addr: 0x10342080, len: 8, content: [0 1 2 3 4 5 6 7].
addr: 0x10324a00, len: 9, content: [0 1 2 3 4 5 6 7 8].
addr: 0x10324a00, len: 10, content: [0 1 2 3 4 5 6 7 8 9].
addr: 0x10324a00, len: 10, content: [0 1 2 3 4 5 6 7 8 9].</pre><p>很明显，切片的地址经过了数次改变。</p>
<p>2. 如果，在 make 初始化切片的时候给出了足够的容量，append 操作不会创建新的切片：</p><pre class="crayon-plain-tag">func main() {
    var sa = make([]string, 0, 10)
    fmt.Printf("addr: %p, len: %v, content: %v.\n", sa, len(sa), sa)
    for i:=0; i&lt;10; i++ {
        sa = append(sa, fmt.Sprintf("%v", i))
        fmt.Printf("addr: %p, len: %v, content: %v.\n", sa, len(sa), sa)
    }
    fmt.Printf("addr: %p, len: %v, content: %v.\n", sa, len(sa), sa)
}</pre><p></p><pre class="crayon-plain-tag">Running...

addr: 0x10304140, len: 0, content: [].
addr: 0x10304140, len: 1, content: [0].
addr: 0x10304140, len: 2, content: [0 1].
addr: 0x10304140, len: 3, content: [0 1 2].
addr: 0x10304140, len: 4, content: [0 1 2 3].
addr: 0x10304140, len: 5, content: [0 1 2 3 4].
addr: 0x10304140, len: 6, content: [0 1 2 3 4 5].
addr: 0x10304140, len: 7, content: [0 1 2 3 4 5 6].
addr: 0x10304140, len: 8, content: [0 1 2 3 4 5 6 7].
addr: 0x10304140, len: 9, content: [0 1 2 3 4 5 6 7 8].
addr: 0x10304140, len: 10, content: [0 1 2 3 4 5 6 7 8 9].
addr: 0x10304140, len: 10, content: [0 1 2 3 4 5 6 7 8 9].</pre><p>可见，切片的地址一直保持不变。</p>
<p>3. 如果不能准确预估切片的大小，又不想改变变量（如：为了共享数据的改变），这时候就要请出指针来帮忙了。下面程序中，sa 就是 osa 这个切片的指针，我们共享切片数据和操作切片的时候都使用这个切片地址就 OK 了。其本质上是：append 操作亦然会在需要的时候构造新的切片，不过是将地址都保存到了 sa 中，因此我们通过该指针始终可以访问到真正的数据。</p><pre class="crayon-plain-tag">func main() {
    var osa = make([]string, 0)
    sa := &amp;osa
    for i:=0; i&lt;10; i++ {
        *sa = append(*sa, fmt.Sprintf("%v", i))
        fmt.Printf("addr of osa: %p, addr: %p， content: %v.\n", osa, sa, sa)
    }
    fmt.Printf("addr of osa: %p, addr: %p, content: %v.\n", osa, sa, sa)
}</pre><p></p><pre class="crayon-plain-tag">Running...

addr of osa: 0xc20800a220, addr: 0xc20801e020, content: &amp;[0].
addr of osa: 0xc20801e0a0, addr: 0xc20801e020, content: &amp;[0 1].
addr of osa: 0xc20803e0c0, addr: 0xc20801e020, content: &amp;[0 1 2].
addr of osa: 0xc20803e0c0, addr: 0xc20801e020, content: &amp;[0 1 2 3].
addr of osa: 0xc208050080, addr: 0xc20801e020, content: &amp;[0 1 2 3 4].
addr of osa: 0xc208050080, addr: 0xc20801e020, content: &amp;[0 1 2 3 4 5].
addr of osa: 0xc208050080, addr: 0xc20801e020, content: &amp;[0 1 2 3 4 5 6].
addr of osa: 0xc208050080, addr: 0xc20801e020, content: &amp;[0 1 2 3 4 5 6 7].
addr of osa: 0xc208052000, addr: 0xc20801e020, content: &amp;[0 1 2 3 4 5 6 7 8].
addr of osa: 0xc208052000, addr: 0xc20801e020, content: &amp;[0 1 2 3 4 5 6 7 8 9].
addr of osa: 0xc208052000, addr: 0xc20801e020, content: &amp;[0 1 2 3 4 5 6 7 8 9].</pre><p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.somedoc.net/2020/02/03/%e8%bd%ac%e8%bd%bdgo-%e8%af%ad%e8%a8%80%e4%b9%8b%e5%88%87%e7%89%87/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>[转载]Go 标准库之文件读写</title>
		<link>https://www.somedoc.net/2020/02/03/%e8%bd%ac%e8%bd%bdgo-%e6%a0%87%e5%87%86%e5%ba%93%e4%b9%8b%e6%96%87%e4%bb%b6%e8%af%bb%e5%86%99/</link>
					<comments>https://www.somedoc.net/2020/02/03/%e8%bd%ac%e8%bd%bdgo-%e6%a0%87%e5%87%86%e5%ba%93%e4%b9%8b%e6%96%87%e4%bb%b6%e8%af%bb%e5%86%99/#respond</comments>
		
		<dc:creator><![CDATA[张三太爷]]></dc:creator>
		<pubDate>Mon, 03 Feb 2020 07:55:53 +0000</pubDate>
				<category><![CDATA[Go]]></category>
		<category><![CDATA[备忘录]]></category>
		<category><![CDATA[技术]]></category>
		<guid isPermaLink="false">http://www.somedoc.net/?p=4275</guid>

					<description><![CDATA[转载自：https://www.cnblogs.com/gu <a href="https://www.somedoc.net/2020/02/03/%e8%bd%ac%e8%bd%bdgo-%e6%a0%87%e5%87%86%e5%ba%93%e4%b9%8b%e6%96%87%e4%bb%b6%e8%af%bb%e5%86%99/" class="more-link">[&#8230;]</a>]]></description>
										<content:encoded><![CDATA[<p>转载自：https://www.cnblogs.com/guigujun/p/10206230.html。</p>
<h3>创建一个空文件</h3>
<p></p><pre class="crayon-plain-tag">package main

import (
    "log"
    "os"
)

func main() {
    file, err := os.Create("empty.txt")
    if err != nil {
        log.Fatal("create file err", err)
    }

    log.Println(file)
    file.Close()
}</pre><p></p>
<h3>获取文件的信息</h3>
<p></p><pre class="crayon-plain-tag">package main

import (
    "fmt"
    "log"
    "os"
)

func main() {
    fileInfo, err := os.Stat("main.go")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("File Name:", fileInfo.Name())
    fmt.Println("Size in bytes:", fileInfo.Size())
    fmt.Println("Permissions:", fileInfo.Mode())
    fmt.Println("Last modified:", fileInfo.ModTime())
    fmt.Println("Is Directory:", fileInfo.IsDir())

    fmt.Printf("System interface type:%T\v\n", fileInfo.Sys())
    fmt.Printf("System info:%+v\n\n", fileInfo.Sys())
}</pre><p></p>
<h3>重命名和移动文件</h3>
<p></p><pre class="crayon-plain-tag">package main

import (
    "log"
    "os"
)

func main() {
    originalPath := "empty.txt"
    newPath := "test.txt"

    err := os.Rename(originalPath, newPath)
    if err != nil {
        log.Fatal(err)
    }
}</pre><p></p>
<h3>删除文件</h3>
<p></p><pre class="crayon-plain-tag">package main

import (
    "log"
    "os"
)

func main() {
    err := os.Remove("empty.txt")
    if err != nil {
        log.Fatal(err)
    }
}</pre><p></p>
<h3>打开关闭文件</h3>
<p></p><pre class="crayon-plain-tag">package main

import (
    "log"
    "os"
)

func main() {
    // 简单地打开文件
    file, err := os.Open("test.txt")
    if err != nil {
        log.Fatal(err)
    }

    file.Close()

    file, err = os.OpenFile("test.txt", os.O_APPEND, 0666)
    if err != nil {
        log.Fatal(err)
    }

    file.Close()
}</pre><p></p>
<h3>打开文件的一些其他的操作</h3>
<p></p><pre class="crayon-plain-tag">os.O_RDONLY // 只读
os.O_WRONLY // 只写
os.O_RDWR   // 读写文件
os.O_APPEND // 追加文件
os.O_CREATE // 不存在时创建文件
os.O_TRUNC  // 打开时截断文件</pre><p></p>
<h3>检查文件是否存在</h3>
<p></p><pre class="crayon-plain-tag">package main

import (
    "log"
    "os"
)

var (
    fileInfo *os.FileInfo
    err error
)

func main() {
    fileInfo, err := os.Stat("test.txt")
    if err != nil {
        if os.IsNotExist(err) {
            log.Fatal("File does not exist.")
        }
    }

    log.Println("File does exist.File information: ")
    log.Println(fileInfo)
}</pre><p></p>
<h3>检查文件的读写权限</h3>
<p></p><pre class="crayon-plain-tag">package main

import (
    "log"
    "os"
)

func main() {
    file, err := os.OpenFile("test.txt", os.O_WRONLY, 0666)
    if err != nil {
        if os.IsPermission(err) {
            log.Println("Error: Write permission denied.")
        }
    }
    file.Close()

    file, err = os.OpenFile("test.txt", os.O_RDONLY, 0666)
    if err != nil {
        if os.IsPermission(err) {
            log.Println("Error: Read permission denied.")
        }
    }
    file.Close()
}</pre><p></p>
<h3>改变文件的权限、所有权和时间戳</h3>
<p></p><pre class="crayon-plain-tag">package main

import (
    "log"
    "os"
    "time"
)

func main() {
    // 改变权限
    err := os.Chmod("test.txt", 0777)
    if err != nil {
        log.Println(err)
    }

    // 改变所有权，适用于 Linux。Windows 不支持
    err = os.Chown("test.txt", os.Getuid(), os.Getegid())
    if err != nil {
        log.Println(err)
    }

    // 改变时间戳
    twoDaysFromNow := time.Now().Add(48 * time.Hour)
    lastAccessTime := twoDaysFromNow
    lastModifyTime := twoDaysFromNow
    err = os.Chtimes("test.txt", lastAccessTime, lastModifyTime)
    if err != nil {
        log.Println(err)
    }
}</pre><p></p>
<h3>复制文件</h3>
<p></p><pre class="crayon-plain-tag">package main

import (
    "io"
    "log"
    "os"
)

func main() {
    // 打开源文件
    originalFile, err := os.Open("test.txt")
    if err != nil {
        log.Fatal(err)
    }

    defer originalFile.Close()

    // 创建新文件
    newFile, err := os.Create("test_copy.txt")
    if err != nil {
        log.Fatal(err)
    }

    defer newFile.Close()

    // 文件复制
    bytes, err := io.Copy(newFile, originalFile)
    if err != nil {
        log.Fatal(err)
    }

    log.Printf("Copied %d bytes.", bytes)

    err = newFile.Sync()
    if err != nil {
        log.Fatal(err)
    }
}</pre><p></p>
<h3>移动位置</h3>
<p></p><pre class="crayon-plain-tag">package main

import (
    "fmt"
    "log"
    "os"
)

func main() {
    file, err := os.Open("test.txt")
    if err != nil {
        log.Fatal(err)
    }

    defer file.Close()

    var offset int64 = 5
    var whence int = 0

    newPos, err := file.Seek(offset, whence)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("Just moved to 5:", newPos)

    newPos, err = file.Seek(-2, 1)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("Just moved back two:", newPos)

    currentPos, err := file.Seek(0, 1)
    fmt.Println("current pos:", currentPos)

    newPos, err = file.Seek(0, 0)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("position after seek 0, 0: ", newPos)
}</pre><p></p>
<h3>向文件中写入字节</h3>
<p></p><pre class="crayon-plain-tag">package main

import (
    "log"
    "os"
)

func main() {
    file, err := os.OpenFile("test.txt", os.O_WRONLY | os.O_TRUNC | os.O_CREATE, 0666)
    if err != nil {
        log.Fatal(err)
    }

    defer file.Close()

    bytes := []byte("测试写入功能！")
    bw, err := file.Write(bytes)
    if err != nil {
        log.Fatal(err)
    }

    log.Printf("Wrote %d bytes.\n", bw)
}</pre><p></p>
<h3>快速写入文件</h3>
<p></p><pre class="crayon-plain-tag">package main

import (
    "io/ioutil"
    "log"
)

func main() {
    err := ioutil.WriteFile("test.text", []byte("测试快速写入功能!"), 0666)
    if err != nil {
        log.Fatal(err)
    }
}</pre><p></p>
<h3>在写入时使用缓存</h3>
<p></p><pre class="crayon-plain-tag">package main

import (
    "bufio"
    "log"
    "os"
)

func main() {
    file, err := os.OpenFile("test.txt", os.O_WRONLY, 0666)
    if err != nil {
        log.Fatal(err)
    }

    defer file.Close()

    buffer := bufio.NewWriter(file)
    bw, err := buffer.Write([]byte{65, 66, 67})
    if err != nil {
        log.Fatal(err)
    }

    log.Printf("Bytes written: %d\n", bw)

    bw, err = buffer.WriteString("\n写入字符串")
    if err != nil {
        log.Fatal(err)
    }

    log.Printf("Bytes written: %d\n", bw)

    unflushedBuffer := buffer.Buffered()
    log.Printf("Bytes buffered: %d\n", unflushedBuffer)

    ba := buffer.Available()
    log.Printf("Available buffer: %d\n", ba)

    buffer.Flush()
    buffer.Reset(buffer)

    ba = buffer.Available()
    log.Printf("Available buffer: %d\n", ba)

    buffer = bufio.NewWriterSize(buffer, 8000)
    ba = buffer.Available()
    if err != nil {
        log.Fatal(err)
    }

    log.Printf("Available buffer: %d\n", ba)
}</pre><p></p>
<h3>从文件中读取 n 个字节</h3>
<p></p><pre class="crayon-plain-tag">package main

import (
    "log"
    "os"
)

func main() {
    file, err := os.Open("test.txt")
    if err != nil {
        log.Fatal(err)
    }

    defer file.Close()

    // 从文件中读取 16 个字节
    bytes := make([]byte, 16)
    br, err := file.Read(bytes)
    if err != nil {
        log.Fatal(err)
    }

    log.Printf("number of bytes read: %d\n", br)
    log.Printf("Data read: %s\n", bytes)
}</pre><p></p>
<h3>读取文件中全部内容</h3>
<p></p><pre class="crayon-plain-tag">data, err := ioutil.ReadAll(file)</pre><p></p>
<h3>快速读取文件到内存中</h3>
<p></p><pre class="crayon-plain-tag">package main

import (
    "log"
    "io/ioutil"
)

func main() {
    data, err := ioutil.ReadFile("test.txt")
    if err != nil {
        log.Fatal(err)
    }

    log.Printf("Data read: %s\n", data)
}</pre><p></p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.somedoc.net/2020/02/03/%e8%bd%ac%e8%bd%bdgo-%e6%a0%87%e5%87%86%e5%ba%93%e4%b9%8b%e6%96%87%e4%bb%b6%e8%af%bb%e5%86%99/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
