善用 MVP 思维,5 天时间零成本搭建小报童排行榜

前言 你好,我是小柒。 Java 程序员,热衷自动化。 前段时间花了 5 天左右,借助 Notion + CloudFlare + Github Actions 零成本搭建了一个支持一键分销的小报童排行榜: https://xiaobot.osguider.com。在这里复盘一下过程,希望能够对你有所启发。 善用 MVP 思维,用最小的成本最快地实现最核心的功能,尽早触达用户,尽早获取反馈,不断迭代,不断升级。 什么是小报童排行榜? 小报童排行榜是一个罗列了(几乎)所有小报童专栏的导航网页。可以帮助你一键分销所有小报童专栏。 为什么要做小报童排行榜? 主要是因为小报童官方没有公开的专栏索引列表,但这个痛点确实是在的。既然有需求未被实现,机会不就来了? 实现足够简单,成本足够低,做这个游刃有余。 折腾副业,没自己的产品始终感觉没底气。所以,小报童排行榜可以是我的产品支撑,也可以是我向外链接的突破口。 能给我带来收益:哪怕是最基础的小报童分销,也能带来一些收益。且不说后面依据这个产品的放大升级。 小报童排行榜给我带来了什么? 我能做成一件事的信心 和我一起做事的朋友 借助小报童排行榜,链接到了更多朋友 副业生态的起点 一点点小钱 一些有趣的统计数据 迄今为止我收录了 308 位作者的 403 个小报童专栏。其中: 有 26 位作者的 38 个专栏订阅数量超过 3000,占比 9%。只要订阅数量超过 3000 就能跻身前 10%。 有 50 位作者的 72 个专栏订阅数量在 1000 和 3000 之间,占比 18%。只要订阅数量超过 1000 就能跻身前 30%。 有 97 位作者的 105 个专栏超过 1 年没有更新,算是已完结的状态。有人入场,有人离场。机会、永远都在那里等你,剩下的、就是你能否主动抓住它了。 15 亿国人,你的竞争对手只有 308 位。其实只要开始,就已经是成功了。 有很多数据不太好看的专栏(订阅数量为 0),其中绝大部分是订阅制而非买断式。因为小报童展示的订阅数=当前仍在订阅状态的用户数,当用户订阅到期不再续订时,这个数量就会减少。所以单从数据好看的角度考虑,买断式的专栏会更好一点。 上线至今,小报童排行榜分销佣金收入 371.61 元,给朋友们定制一键分销系统(扣除返现后)收入 210 元。对于在互联网上流浪多年的我和 Huazi,这个正反馈极佳。 我把手上已有的小报童专栏排名数据整理成了一个 Excel 表格,如果你想要自己做一些数据分析,可以通过 osguider 微信我领取。 搭建一个小报童排行榜,可以有多简单? 按照我以往程序员的思维,如果想要做一个小报童排行榜,至少要经历以下步骤: 需求分析 概要设计 数据库设计 爬虫&业务代码编写 前端代码编写 部署上线 带着完美主义的思维,最后可能是因为设计配色不满意、可能是因为前端代码没写好,也可能是爬虫做不出来……从而不了了之,前功尽弃。 这件事情,真的需要这么复杂吗? 需求分析 需求分析需要考虑两个层面:我想做什么?我能做什么?其中: 我想做的事情很简单:把所有小报童专栏汇聚到一个网页,供用户查阅和订阅。 我能做什么主要取决于我能从小报童官方拿到什么样的信息。经过简单的分析,我可以从小报童官方获取到以下信息: 专栏名称 专栏作者 专栏介绍 专栏订阅数量 专栏内容数量 专栏价格 专栏分销比例 专栏分销链接 专栏创建时间 专栏更新时间 小报童排行榜的核心功能是列举出所有的小报童专栏信息,Notion 数据库可以完美实现这个功能!说干就干! 数据库搭建 按照可以从小报童官方获取到的信息搭建 Notion 数据库,这个过程就像是给 Excel 添加多个列字段,非常简单。 这里不再赘述 Notion 的使用,如果你对 Notion 感兴趣,可以通过这几个链接学习。中文用户指南 | Notion Academy | Notion Help Center 一条 Notion 数据库记录长这个样子: 汇总的列表长这个样子: Notion 极其强大!支持任意的页面嵌套,支持把任意页面发布为公网可以访问的网页,甚至可以给同一个数据库定制不同的视图! 那么依据这个功能,我们实际上已经完成了最简单的 MVP:手动收集小报童专栏信息,并通过 Notion 展示。 小报童排行榜的第一个 MVP 长这个样子: 数据爬取 作为一个程序员、作为一名 RPA 教练,手动录数据多少有点说不过去了吧? 我们来看几个小报童专栏分销链接: 生财有术项目精选:https://xiaobot.net/p/shengcaiyoushu?refer=6f4ecc2b-70e9-4f58-82de-71e5b1f357ee IP合伙人·八年成事(百问百答):https://xiaobot.net/p/IP10?refer=6f4ecc2b-70e9-4f58-82de-71e5b1f357ee 稍加比较就能发现,分销链接由以下几个部分组成: 域名:https://xiaobot.net/ 小报童专栏 ID: shengcaiyoushu / IP10 分销标识:refer=6f4ecc2b-70e9-4f58-82de-71e5b1f357ee 其中:域名是不变的,分销标识只跟发起分销的用户有关系,所以在收集专栏信息的时候,我们只需要收集小报童专栏 ID 即可。这一个步骤,现在确实是手动收集和录入的。 但在这之后,我们可以通过拼接域名和小报童专栏 ID 得到指定小报童专栏的页面地址。访问专栏页面,可以获取到我们需要的几乎所有信息,通过 RPA/写代码 手段轻松获取(这里我的方案是通过 CloudFlare Workers 调用小报童接口获取数据,再使用 Notion API 写入数据库)。 请适当控制 RPA 运行频率,保护服务器负载。 到这里,内容的自动爬取和更新也搞定了。 页面编写 & 自动发布 Notion 也有缺点。毕竟是海外的产品,国内访问速度非常不稳定,根据我自己的测试,Notion Site 在极端情况下需要 10-20 秒才能加载出来,这个速度用户肯定是无法忍受的。在我的认知体系内,写一个静态页面是比较好的解决方案。 在看过我自己写的丑得不能再丑的 HTML Table 之后,我的设计师伙伴 @Huazi 终于还是忍不住亲自上场了。他负责页面样式,我负责内容填充,完美配合。 技术选型大概是这样的: 页面样式:HTML + CSS + JavaScript + Tailwind CSS 内容渲染:通过 JavaScript 脚本调用 Notion API 获取数据,再使用 eleventy 把数据渲染到 HTML。 自动化:通过 Github Actions 每天定时执行内容渲染逻辑,然后自动发布网页到服务器。 至此,第二版小报童排行榜问世:小报童排行榜。 赋能 前面的小报童排行榜只能帮助我自己分销所有小报童专栏,分销佣金也只能我自己拿,怎么让别人愿意帮我推广小报童排行榜呢?最简单的方法当然是让利! 通过给别人定制一键分销网页,页面里所有小报童专栏全部改成别人的分销链接。这样别人也可以通过自己的一键分销网页分销小报童专栏获取收益,我收取一定的技术维护费用,各取所需,各享其利。 实现也很简单,增加一个 Notion 数据库记录分销商信息: 通过 Cloudflare Workers 读取 Notion 分销商数据库信息,然后通过 eleventy 渲染子页面并发布即可。这里有几个一键分销页面,可供参考: https://xiaobot.osguider.com/osguider/ https://xiaobot.osguider.com/HeyHuazi/ https://xiaobot.osguider.com/76718400/ https://xiaobot.osguider.com/whyvincent/ https://xiaobot.osguider.com/Help000000/ https://xiaobot.osguider.com/fxm99960/ https://xiaobot.osguider.com/LSL021102/ https://xiaobot.osguider.com/yzhm111/ https://xiaobot.osguider.com/baojie_xmg/ https://xiaobot.osguider.com/liuliuzaish/ https://xiaobot.osguider.com/yi25390/ https://xiaobot.osguider.com/HX1747023971/ https://xiaobot.osguider.com/595683079/ https://xiaobot.osguider.com/1738407610/ 未来的计划 表单提交自动收录:小报童专栏作者填写金数据表单,cloudflare workers 自动爬取专栏信息并收录到 Notion 数据库。 表单提交自动生成一键分销链接:想要构建一键分销系统的朋友填写金数据表单,cloudflare workers 自动构建一键分销页面。 知识星球绑定销售:做一个知识星球强化个人 IP,小报童排行榜作为赠品而不是单独销售。这样可以把认可产品的朋友带入到我的 IP 圈,逐渐影响,让他们认可我这个人,进而创造更多合作的机会。 后话 当然,现在的小报童排行榜并不完美。 比如现在还不支持分页,比如现在只能用 Ctrl + F 做检索,比如不支持用户自定义要分销的专栏列表。 如果你也觉得有些不爽的地方,欢迎找我聊聊,万分感激。 致谢 感谢我的队友 @Huazi,没有他,小报童排行榜不会如此美观,不会如此受人欢迎。 关于我 重新认识一下。 你好,我是小柒,微信 osguider。 Java 程序员,热衷自动化。生财有术 RPA 航海教练(23 年 12 月)。 小报童排行榜 作者,支持一键分销所有小报童专栏,也可以给你定制专属分销链接,欢迎体验。 开源服务指南主理人,依托 Notion 搭建了完整的自动化工作流和图文生成体系,这个后面也会写复盘。 目前实现了: 每天自动从 Github 爬取开源项目信息 自动调用 ChatGPT 开源项目简介 手动审核 ChatGPT 生成的简介并标记审核状态 借助 Pipedream 和 Notion 把已审核的开源项目自动汇总成 Markdown 格式的文章 借助 mdnice 把 Markdown 文章自动转为微信公众号图文 手动分发(等后面抽出时间这里可以用 Automa 做自动分发) 了解开源服务指南: 官网 微信公众号:开源服务指南 做过一个不起眼的导航网站:看见导航 收藏了很多我见过且觉得值得推荐的网页和工具,可能会对你有所帮助。 写过一写 Java 开发工具的使用经验:Intellij IDEA 最佳实践 也欢迎访问我的个人博客:Seven’s blog

2024/7/20
articleCard.readMore

给 eleventy(11ty) 添加 sitemap.xml 和 robots.txt

配置过程 添加数据文件 _data/site.json,写入以下内容,定义站点信息和 sitemap 中的一些默认值: 1 2 3 4 5 6 7 8 9 { "baseUrl": "https://xiaobot.osguider.com", "robots": "/robots.txt", "sitemap": { "path": "sitemap.xml", "changefreq": "daily", "priority": 0.5 } } 添加 sitemap 模板文件,写入以下内容: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 --- permalink: "{{ site.sitemap.path }}" eleventyExcludeFromCollections: true --- <?xml version="1.0" encoding="UTF-8"?> <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> {% for page in collections.all %} {% unless page.data.sitemap.ignore %} <url> <loc>{{ site.baseUrl }}{{ page.url | url }}</loc> <lastmod>{{ page.date | date: '%Y-%m-%dT%H:%M:%S.%LZ' }}</lastmod> <changefreq>{{ site.sitemap.changefreq }}</changefreq> <priority>{{ page.data.sitemap.priority | default: site.sitemap.priority | default: 0.5 }}</priority> </url> {% endunless %} {% endfor %} </urlset> 【可选】配置不同页面的 sitemap 表现: 如果不希望某些页面在被包含在 sitemap 文件中,在页面元数据中添加 sitemap.ignore: true 即可; 可以对不同的页面设置不同的 sitemap 优先级,在页面元数据中添加 sitemap.priority: 0.5,取值范围 0-1; 对于分页数据,要设置 pagination.addAllPagesToCollections: true 才会在 sitemap.xml 文件中包含每一个分页页面。 添加模板文件 src/robots.txt,写入以下内容: 1 2 3 4 5 6 7 8 9 --- eleventyComputed: permalink: "{{ site.robots }}" eleventyExcludeFromCollections: true --- Sitemap: {{ site.baseUrl }} {{ site.sitemap.path }} User-agent: * Disallow: 重新编译,over! 参考文档 How to create sitemap.xml Sitemap xml liquid sitemap format

2024/6/14
articleCard.readMore

开源服务指南博客文章自动生成

GitHub Actions 可以添加运行参数。我只需要设置 filepath 和 content 两个参数,然后配合 shell 脚本就可以自动生成和提交博文到 GitHub 仓库,进而触发自动构建和发布。 GitHub Actions 提供了 REST API 来触发前面的工作流,这样我就可以通过 HTTP 请求来自动生成和发布博文。 开源服务指南数据库现在是建立在 Notion 上的,Notion 也提供了 REST API 的交互方式。所以我只需要定时扫描 Notion 数据库,获取状态刚变更为 “已发布” 的博文,提取文章内容,通过第 2 步中提到的 REST API 来触发第 1 步中提到的 GitHub Actions 即可自动生成和发布博文。这里我使用了 Cloudflare Workers 实现。 怎么监测 Notion 数据库文章状态变动呢?想要监测状态“变动”,我们需要知道变动前的状态和变动后的状态,进而需要有数据库缓存变动前的状态,能做,但麻烦。所幸,pipedream 帮我们做好了这个事情。它能够监测 Notion 数据库变动,并且触发工作流执行。 所以,最后的工作流程就是: Pipedream 监测开源服务指南 Notion 文章数据库变动,提取状态为“已完成”的文章,把文章 id 通过 HTTP 请求发送给 Cloudflare Workers; Cloudflare Workers 根据文章 id 查询文章内容,把文章路径和文章内容作为参数,发送请求给 Github Actions; Github Actions 把文章内容写入文章路径,提交文章源文件到 Github 仓库; Github Actions 监听代码提交,持续集成和发版; 嗯,云服务挺好用。 置于为什么不直接用 Pipedream 提取参数触发 Github Actions 工作流,个人主观意愿影响比较多:Pipedream 代码编写体验略差,稳定性欠佳,所以在逐步往 Cloudflare Workers 迁移。这个回头细讲。 附录: Github Actions 文档 Cloudflare Workers 文档 Pipedream 文档 第 1 步中提到的 Github Actions 代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 name: Create Post on: workflow_dispatch: inputs: path: description: 'File path' required: true content: description: 'File content' required: true permissions: contents: write jobs: create-file: runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4 - name: Create Post run: | cat << EOF > ./content/post/${{ github.event.inputs.path }} ${{ github.event.inputs.content }} EOF - name: Commit and push changes run: | git config user.name "username" git config user.email "email@osguider.com" git add ./content/post/${{ github.event.inputs.path }} git commit -m "add post: ${{ github.event.inputs.path }}" git push 第 2 步中提到的 HTTP 请求: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 import axios from 'axios'; const filePath = 'daily/daily-01.md'; const fileContent = 'Hello World'; const owner = "osguider"; const repo = "blog"; const workflow_file = "create-post.yml"; const url = `https://api.github.com/repos/${owner}/${repo}/actions/workflows/${workflow_file}/dispatches`; const headers = { 'Authorization': `Bearer ${process.env.GITHUB_REPO_PAT_BLOG}`, 'Accept': 'application/vnd.github.v3+json', 'Content-Type': 'application/json' }; const data = { 'ref': 'main', 'inputs': { path: filePath; content: fileContent; }, }; axios.post(url, data, { headers: headers }) .then((response) => { console.log('GitHub Action dispatched successfully!'); }) .catch((error) => { // TODO 错误通知 console.log(`Failed to dispatch GitHub Action: ${error}`); });

2024/4/21
articleCard.readMore

流程引擎技术调研

概念 工作流 BPMN DMN (Decision Model & Notation) CMMN (Case Management Model and Notation) 选型 后端 Activiti 官网 Github Star 9.1k Document | Activiti Core activiti 由 Alfresco 软件开发,目前最高版本 activiti 7。 activiti 有 5、6、7 几个主流版本。其中 5 和 6 的核心 Leader 是 Tijs Rademakers,后来由于内部分歧,Tijs Rademakers 在 2017 年离开团队,创建了 flowable。 现在 activiti 由 Salaboy 团队接管,5 和 6 两个版本已经暂停维护,activiti 7 仍然使用 activiti 6 的内核,并没有开发新的特性。只是在 activiti 上层封装了一些应用。 flowable 官网 Github Star 6k Document | 文档 使用 用户和用户组 | 中文 参考文档: Flowable实战(七)用户和组 Flowable用户权限体系 flowable动态设置办理人 Flowable 设置任务处理人的四种方式 前端 bpmn bpmn-js 官网 Demo Github dmn-js 官网 Demo Github form-js 官网 Demo Github 参考文档 常见的工作流方案对比 BPM、BPMN、BPMN2.0概念介绍 BPMN和DMN基本概念和使用案例

2024/1/2
articleCard.readMore

MySQL 数据库数据同步方案调研

名词 CDC(Change Data Capture): 数据变化捕获 ETL(Extract Transform Load):数据提取、转换、载入 工具 特色Canalmysql_streamergo-mysql-transferMaxwellDatabusDataX 开源方阿里巴巴Yelp-zendeskLinkedIn阿里巴巴 开发语言JavaPythonGolangJavaJavaJava 活跃是否是是否是 高可用支持支持支持-支持- 文档详细还行详细详细详细还行 数据源MysqlMysqlMysqlMysqlMysql OracleMySQL Oracle OceanBase SQLServer PostgreSQL DRDS ALL_RDBMS ODPS OSS OTS Hbase0.94 Hbase1.1 Phoenix4.x Phoenix5.x MongoDB Hive Cassandra TxtFile FTP HDFS TSDB TDengine OpenTSDB 接收端编码定制Kafka等(MQ)Redis MongoDB Elasticsearch RabbitMQ Kafka RocketMQ HTTP API 后续支持更多stdout Kafka Kinesis Nats Google Cloud Pub/Sub Google Cloud Bigquery RabbitMQ Redis SNS编码定制?MySQL Oracle OceanBase SQLServer PostgreSQL DRDS ALL_RDBMS ODPS OSS OTS Hbase0.94 Hbase1.1 Phoenix4.x Phoenix5.x MongoDB Hive Cassandra TxtFile FTP HDFS TSDB TDengine ADS OCS Elasticsearch 全量数据初始化不支持支持支持支持(maxwell-bootstrap)-支持 数据格式编码定制Json(固定格式)Json(规则配置) 模板语法 Lua脚本Json (固定)编码定制?- 备注需要写代码定制接受端可扩展性更强,但是上手难度更高数据同步非实时 实际使用配合 datax-web 体验更佳 alibaba/canal | 文档 | Docker 基于 MySQL 数据库增量日志解析,提供增量数据订阅和消费。 由Java开发,分为服务端和客户端,拥有众多的衍生应用,性能稳定,功能强大;canal 需要自己编写客户端来消费canal解析到的数据。 Yelp/mysql_streamer | 文档 wj596/go-mysql-transfer | 文档 zendesk/maxwell | 官网 | 文档 linkedin/databus | 文档 Databus是一种低延迟变化捕获系统,已成为LinkedIn数据处理管道不可或缺的一部分。Databus解决了可靠捕获,流动和处理主要数据更改的基本要求。Databus提供以下功能: 源与消费者之间的隔离 保证按顺序和至少一次交付具有高可用性 从更改流中的任意时间点开始消耗,包括整个数据的完全引导功能。 分区消费 源一致性保存 上手难度较高。 alibaba/DataX | 简介 | WeiYe-Jing/datax-web | qlangtech/tis DataX 是阿里云 DataWorks数据集成 的开源版本,在阿里巴巴集团内被广泛使用的离线数据同步工具/平台。DataX 实现了包括 MySQL、Oracle、OceanBase、SqlServer、Postgre、HDFS、Hive、ADS、HBase、TableStore(OTS)、MaxCompute(ODPS)、Hologres、DRDS 等各种异构数据源之间高效的数据同步功能。 配合 DataX-Web 可视化配置任务。 osheroff/mysql-binlog-connector-java | Maven 在 Java 中消费 Mysql Binlog。 参考文档 MySQL数据 实时同步到Kafka –Binlog canal、Maxwell、Kafka Connect 实现MySQL增量同步 实时数仓 | mysql的binlog实时同步工具对比

2024/1/2
articleCard.readMore

IC卡、ID卡、CPU卡、RFID 和 NFC 的区别与联系

RFID 卡 是指非接触式类电子卡片/标签,包括有ID卡、IC卡和NFC卡以及其它等电子卡/标签。他们主要的区别在于工作频段。 NFC NFC,全称是Near Field Communication,“近距离无线通信”,NFC本质信息双向交换。 NFC和RFID都是基于位置相近的两个物体之间的信号传输,NFC技术增加了点对点(P2P)通信功能,NFC设备彼此寻找对方并建立通信连接。P2P通信的双方设备是对等的,而RFID通信的双方设备是主从关系。 NFC 的工作频率是 13.56Mhz,所以只能读取和模拟 13.56Mhz 的 IC 卡。 ID 卡 全称身份识别卡(Identification Card),低频(频率有125Khz、250 Khz、375 Khz、500 Khz等)。 只能读卡,不可写入、不可存储、不可加密的感应卡,(卡面)含固定的编号,出厂时固化了ID。 特点 卡面上有 10 位或者 8 位数字(卡号)。 安全性 安全性较低,ID 卡不可存储,所以卡片持有者的权限、功能操作要完全依赖于网络系统。 很容易复制 应用 门禁系统、企业工牌 IC 卡 全称集成电路卡(Integrated Circuit Card),也称智能卡(Smart Card)。 可读写数据、容量大、有加密功能、数据记录可靠、使用更方便,如一卡通系统、消费系统等。 网络依赖性 IC 卡可记录用户资料,可脱离网络使用 安全性 IC 卡的加密和反复读些的特性,使其安全性远大于 ID 卡 应用 门禁卡、地铁、校园一卡通。 二代身份证属于 type B 射频 IC 卡。 CPU 卡 如果不强调的话CPU卡也是IC卡的一种,是高级版的IC卡,CPU卡有信息处理的功能,优点是存储空间大、读取速度快、支持一卡多用功能等特点 。如果不强调无线的话,电话卡中SIM卡就是典型的CPU卡。 安全性 CPU卡内含有随机数发生器,硬件加密算法等,配合芯片上的OS系统,达到金融级的安全级别,防止重复卡、仿制卡、卡上数据非法修改 应用 金融、保险、交警、政府行业等多个领域,小额支付行业 SIM 卡 参考资料 IC卡、ID卡、CPU卡、RFID和NFC的区别 ID / IC 卡基本原理介绍与门禁卡 DIY

2024/1/2
articleCard.readMore

2023 年终总结

前言 每一年都会发生很多很多的事情,能引发我去觉悟的,却寥寥无几。 2023,我称之为自己的开悟元年。 一些故事和感悟,与你共享。 见自己、见天地、见众生 认识了一位新朋友,毫无保留地跟我分享她的世界。音乐、美食、玩乐、世界观,等等等等。给我异常封闭的世界打开了一扇窗。 还有一位老朋友,劳心劳力带我四处游玩,没有一丁点怨言。见到了许多从未见过的风水,体验了许多从未体验过的人情。 读万卷书不如行万里路,行万里路不如阅人无数。 可能多亏了这些朋友们,才能让我愿意打开自己的心门,尝试着去接触这个世界。 当然,按照我现在的境况,距离“见自己、见天地、见众生”还相差甚远。但我相信自己终能解开枷锁,释放真我。 直面恐惧 克服恐惧最好的方法,是直面恐惧。 作为一名后端开发,我对前端一直有一种莫名其妙的恐惧。 究其根由,还是写得比较少。索性趁着元旦 3 天假期,通宵达旦写了自己挂念许久的静态页面 看见导航。 旁观只觉其易,亲历便知其难。 所幸、攻坚克难的过程,才是我们真正的成长。 直面恐惧,给了我莫大的信心和勇气,迎接属于我的 2023。 主观能动性 从毕业到现在工作 5 年,我所在的部门几乎年年拆分重组。以往是被动的随波逐流,公司安排到哪儿就去哪儿,今年恰逢前领导的部门扩招,索性主动换了一次部门。 现领导待我恩重如山(这个比喻没有丝毫夸张成份),提高了我的薪资待遇,也给了我管理团队的机会。我对他个人能力十分敬佩,只是当时对于整个团队氛围有点悲观,终究是做了个不义之徒,落荒而逃。 想着有始有终,鼓起勇气找领导坦白了要离开的事情。 我说我今年特别没有成就感,忙来忙去一整年,到头回顾发现自己好像啥也没做,没有任何成长。 领导的视角和观点对我产生了很大的冲击: 一个人不是技术成长了才算成长,管理能力成长了也是成长。 在管理的过程中,不是只有自己成长了才算成长,你帮助团队成员成长了也是成长。 不是结果成功了才算成长,走了失败的路线,知道那条路是错的也是成长。 不要给自己、和自己做的事情设限。你觉得你只能做到这种程度,那你就只能做到这种程度;你觉得你做的事情只值 10 万,那它就不可能做成 100 万的样子。 想要做成一件事情,需要自己去主动推进。有人告诉你这条路不行,你报告给领导说这条路不行。别人可能根本就没试过,你也根本就没争取过。然后呢?这条路真的不行了。 如果不知道自己该往哪里走,就看看比自己优秀的人到底好在哪里,怎样才能补全这些差距。选择道路的时候,也要想好这条路可能会帮助你获得什么成长。 要敢于突破自我,破除界限,坚守底线。好的产品是大家一起做好的,不是说某一个环节做好就能做好。作为一个开发,也要时刻想一想怎么把产品做得更好。需求有问题就跟需求聊,产品方向有问题就跟产品或者项目经理聊。不要因为你只是一个开发,就只做开发的事情,就只是别人说啥做啥,随波逐流。 一方面解答了我个人成长的困境,一方面阐释了团队滞后的原因。可能别人会觉得这是鸡汤,但对于当时的我来讲,真的是醍醐灌顶。 事后跟同事调侃说,如果我能早一点找领导聊天,可能就不会申请换部门了。 但如果终究是如果,流程走完,板上钉钉,最后还是带着满腔愧疚,搬到了新部门。 信仰崩塌 后面大概想了一下我为什么会走: 一方面是觉得当时的团队没有产品思维,客户说啥就是啥,朝令夕改,忙来忙去一场空,一年下来不知道自己做了什么,缺乏成就感。 另一方面是领导想培养我做管理,但是我却在计较技术上的得失。目标没对齐,难免有落差。 新部门的领导是我的旧识,我对他最大的印象:他是公司里我知道的唯一一个会过滤客户需求的人,不合理/会变动/没效益的需求,几乎都不会透传到开发层面,也就是我们几乎不会做无效开发。 但很快,现实给了我当头一击。 新领导现在是中心经理,不再劳心需求的事情。这边的需求一样是透传客户想法、一样是频繁变动,甚至连需求文档都写不明白。 一开始我还想推动一下变革,改一改团队的问题。但后面愈发疲惫,只觉蚍蜉撼树,螳臂当车,难有成效。 突觉程序员和建筑工人竟如此相似: 建筑工人按照工程师的蓝图一砖一瓦建造现实中的高楼大厦。 程序员按照需求的描述一字一句构筑网络上的虚拟系统。 人人都觉得拆楼重建成本巨大,人人都觉得代码重写信手拈来。加加班就能搞定的事情,不是很简单吗? 我对技术一直都有一种信仰:我一直觉得技术改变世界,科技构建未来。 突然间,我开始怀疑人生:我是谁?我在哪儿?我活着是为了什么?日复一日通宵达旦写一些没人用的垃圾系统的意义在哪里? 突然间,我对技术的信仰崩塌了。只觉程序员不过是个提线木偶,在别人的操作下苟活罢了。 按理说生活中不该有这么多抱怨的,要么忍受、要么改革、要么离开。 至于为什么不跳槽,这个问题我也没想明白。 信念重塑,独立开发 我从高中开始衷爱 IT 的原因,是幻想着有朝一日能够让电脑替我工作。 但现实却非常有趣,更像是我是在为电脑工作。 既然工作中不太如意,索性就开始尝试把曾经的幻想落实下来。试一试能不能走通自己梦想中的独立开发和自由职业的道路。 全网漫游指南 缘分使然,我关注到了 FutureForceDAO 和 全网漫游指南。 当时觉得指南跟我的导航目的相似,都是想把美好的工具分享给更多人知道。既然如此,为什么不一起把这件事情做好呢? 当即填写了申请表格,看看能否为社区做出一点点贡献。 今年最大的收获,当属于此。 互利共赢才能合作长久 我是一个头脑异常简单的人,很少去深入思考权衡利弊。申请加入 FutureForceDAO 的原因纯粹是觉得可以跟一群有着共同目标的人一起把一件非常有价值的事情完成得更好。 在加入 FutureForceDAO 的 OnBoarding 会议上,创始人 Augustus 问了我一个问题:“你希望社区能给你带来什么?”。 当时听到这个问题我愣了一下:我居然可以索取回报吗? (嗯,奴性确实是重了一点) 事后回想,一个社区如果想要稳定发展,势必要让成员在付出的同时获得回报。这些回报可以是能力成长、可以是归属感、可以是成就感。但是不能什么都没有,没有互利的合作是无法长久的。 这并不是一件很大的事情,但是足以让我认识到我的思维模式有问题,为以后的改变奠定了基础。 在生活工作中其实也有这样的问题: 你越是软弱,别人就越觉得你好欺负。 你越是越是加班,别人就越觉得你加班理所当然。 你越是谦让,世界上留给你的机会就越少。 你并不欠任何人任何东西,自己的权益终究是要自己去争取,自己的人设最后还是要自己来打造。 运营的重要性 后来正式加入 FutureForceDAO,开始在日常事务中逐渐了解它,这再一次颠覆了我的认知。 按照我以往的逻辑,如果我想要开发一款产品,那必定是去构思、去设计、去实现,埋头苦干几个月,憋出一个大招。 最后呢?可能市场并不买单。劳心劳力的结果,可能是竹篮打水一场空。 FutureForceDAO 采用了完全不同的方式:从产生想法开始,就积极与用户紧密交流。然后不断地开发 MVP,不断地迭代升级。最终产品研发完成,已经有了非常多的和产品一起成长起来的对产品有着深厚情感的用户。这些用户,无疑才是最大的宝藏。 当你凝望深渊的时候,深渊也在凝望你 『全网漫游指南』的 slogan 是“定义未来的同时,未来也在塑造我们”。这不禁让我想起了一句谚语:“当你凝望深渊的时候,深渊也在凝望你”。 初闻不识言中义,再见已是局中人。 万事万物皆有其“反身性”:你在影响事物的同时,事物也在影响着你。 当我们在躬身做事的时候,事情同时也在反哺我们经验。 当我们深入研究某个垂直领域的时候,这个垂直领域可能也在限制你的横向拓展。 <> 当我自以为在给『全网漫游指南』无私奉献的时候,『全网漫游指南』着实教会了我许多许多东西。 我自以为编程技能还算可以,但程序员的清高可能恰巧限制了我靠运营去赚钱的能力。 我们应该享受这种影响,也应该畏惧这种影响。 Learning by Doing Learning by Doing,是 FutureForceDAO 的口头禅。 在学习中实践,在实践中学习,相辅相成,彼此成就。 成长无非“觉、知、行”。认知提升了,行动没跟上,空想多于实践,就会造成欲求不满、极度内耗的情况。 在以往的生活中,我就是这样一个极度内耗的人。 好在遇到了 FutureForceDAO,让我在做事的过程中获益匪浅。 开源服务指南 眼睛看到的都是别人的故事,动手完成的才是自己的成长。 仿照着『全网漫游指南』,我开始学习借助 Notion 和 Pipedream 这些可能我之前可能看不起(因为数据库和代码其实更灵活)的 SaaS 产品,快速地构建属于自己的『开源服务指南』。 这并不是一件容易的事情,好在我们克服的每一个艰难,最终都会是自己的成长。 从刚开始决定要做『开源服务指南』,到真正写下第一篇文章,花了将近 3 个月的时间。 从我开始动手搭建『开源服务指南』,到第一篇文章发布,花了 1 周的时间。 没错,我又花费了两个多月的时间去构思、去梳理、去空想。我以为我设计了一个很完美的工作流程,但真正实践的时候,才发现事实与构想,所差甚远。 好在后面及时调整,让自己只专注当下最重要的一件事情。不断迭代,不断优化。 每完成一个优化点,对我来说都有着巨大的成就感。这些成就感,又不断地给我提供了继续前行的动力。 个人成长理论中把这个过程称为“最小正循环”:给自己最小的阻碍,让自己最轻松、最快速地克服阻碍,体会成长的快感。小步快走,飞速成长。 这就是 MVP 和迭代的魅力。 当然,我也会焦虑。因为是 MVP,必定与自己的终极目标相差甚远。我也五次三番地想要抛弃 Notion 和 Pipedream,通过自己写代码一步到位。 我总是会头脑发热、不记成本地去追逐自己的热情,这并不是一件好的事情。 好在这时候我意识到了投入产出比的问题,我给自己定了一个目标:公众号粉丝 3000 之前,不再考虑写代码的事情。焦虑随之消失。 截至现在,『开源服务指南』历时 8 个月,已经: 更新了168 篇文章 有 3141 位 公众号 粉丝 CSDN 访问 7万+,入围 博客之星 2023 掘金 优质作者周榜第 8 名 当然,也终于有了官网 开源服务指南 你看,空想全是困难,实践皆是成绩。『开源服务指南』夺走了我几乎所有业余时间,也给了我极大的成长。 至于『开源服务指南』是什么,以一言概之:“用中文推荐优质开源项目,让开发者更容易找到趁手的开源工具”。 如果你对开源服务指南感兴趣,欢迎参阅这篇文章 《开源服务指南使用手册(还没写呢,回头再点)》 副业 人总是贪心的,我也想在其他方向获得突破。花了 1960 元加入 生财有术,花了 9000 多元入局了『IDO 老徐』的八年合伙人。 我也总是懒惰的,买了等于会了的魔咒依然在生效,今年并没有很好地学习和实践。 亦仁说副业最重要的事情是“躬身入局、把手弄脏”。 老徐也总是提醒我们“去执行,出结果”。 我却还是放不下程序员的假清高,没能放下姿态、迈出脚步去赚钱。 消费总有冲动的成分,但我并不后悔。影响很多时候是潜移默化的,先埋下一颗种子,它自会在合适的时候生根发芽。 后记 相信我的明年会比今年更好,祝愿所有的读者都能找到自己的心安。

2024/1/1
articleCard.readMore

URL 网页截图技术选型

平台收费策略API自定义宽高完整页面截图说明 ApiFlash免费方案:100 截图/月✔️✔️✔️ ScreenshotMachine免费方案:100 截图/月✔️✔️✔️ htmlcsstoimage免费方案:50 截图/月✔️✔️✔️ Browserless免费方案:1000 单位/月✔️✔️✔️ Screenshotlayer免费方案:100 截图/月✔️✔️✔️ GrabzIt7天试用✔️✔️✔️ ScreenshotAPI7天试用✔️✔️✔️ urlbox7天试用✔️✔️✔️ GetScreenshot付费使用✔️✔️✔️ Selenium开源免费✖️未测试

2023/12/10
articleCard.readMore

月刊-202310

开发技巧 如何在chrome中实时修改JS 给目标网站持久化注入 JS 脚本 MySQL 参考资料 一份还不错的 MySQL 参考资料 UI Events JS 中的事件调度和 DOM 事件流程 JavaScript 事件顺序 JS 中的事件调度顺序 选择(Selection)和范围(Range) 选择(Selection)和范围(Range) 防抖与节流 防抖与节流 The Open Graph protocol Open Graph 协议,使任何网页都能成为社交图谱中的富文本对象。 看世界 vitorpamplona/amethyst Android Client for nostr Never write a commit message again (with the help of GPT-3) 使用 GPT-3 生成 git commit message BlinkDL/ChatRWKV AI 聊天软件 ChatGPT 的一个开源替代品,家用电脑就能跑。 sun0day/dns-detector 一个基于 Node.js 的命令行工具,从 DNS 服务器获取某个网站的所有 IP 地址,逐一进行延迟测试。 nackily/imglib 轻量级的 JAVA 图像处理库 Podman Desktop 一个跨平台桌面应用,可以使用图形界面进行 Docker 容器管理。 liuxiaojun666/ID-Photo-miniapp-wechart 一款开源的证件照制作小程序 huajian-pro/resume-design 开源免费的简历制作应用 Open Source Alternatives 企业日常运营工具的开源替代方案 Meta360笔记 内容丰富的笔记 10分钟云部署Stable Diffusion Figma-Linux/figma-linux-font-helper 适用于 Linux 操作系统的非官方 Figma Helper 客户端,可以让你尽情使用本地字体 人工智能 yzfly/LangGPT 可以更好地使用 GPT 的 Prompt

2023/11/13
articleCard.readMore

待办清单:化被动主动

我有一个天赋,脑海时常能蹦出来许许多多的奇思妙想,如漫天星海,美妙无穷。 我有一个烦恼,思维总是会迷失在纵横交错的巨大迷宫,似瀚海孤舟,无所适从。 我把事情装在脑子里,经常需要冥思苦想: 接下来要做哪件事? 我是不是忘记了哪件事? 我今天到底做了什么事? 这些思虑,既消耗能量,也消耗情绪。效率大打折扣,精神苦不堪言。 我经常会这样否定自己:忙时碌碌无为,闲时无所事事。 碌碌无为:是记不得自己一整天到底忙完了什么事情; 无所事事:是想不起自己接下来还有什么事情没有做。 昨天实践了一下屡次被我捡起,又屡次被我忘记的方法:待办清单。 不过是花费几分钟把事情从脑子里搬出来,写入到待办清单。生活便发生了天翻地覆的变化: 我从被动地等待事情从脑子里蹦出来,变换为主动地把待办事项从清单中挑出来。 我可以清晰地知道自己还有什么事情没做; 可以自由地给它们安排优先级和处理时间; 我可以轻松地回顾自己在什么时间做了什么事情。 对生活的掌控感、对已完成的任务的自豪感、对未来事项能够按时完成的自信感……油然而生。 放过大脑,让它专注于解决问题,而非不断选择。 武装自己,让我们主动安排,而非被动浮沉。 番外: 几乎我所有富有创造力的想法,都是在上下班路上、吃饭时突然迸发出来的。 所以,停下来,给自己一点放松的时间,也能有事半功倍的效果。 ​(And,不要骂我,为什么把上下班路上和吃饭称为放松时间。)

2023/11/12
articleCard.readMore

“终有一天,我要抛却一切枷锁。踏上旅程,环游世界。” “健康永远是第一要务,是时候好好规划一下了。” “等我调研完所有的时间管理理念并且总结出一套适合自己的方法论再去执行。” “我想开个自媒体帐号分享心得,倒逼自己成长。但是…..等想好名字再开始吧。” “完美主义不可取,得先完成再谈优化呀。这个毛病得改改。” …… 嗯,你说的对。然后呢? 不了了之。 “等”,是一个在我的生活中高频出现的字眼。 总想着等时机成熟之后再去行动,可是不行动又怎会促使时机成熟呢? 在漫长的等待中丧失热情,失去动力。然后、再等待下一个等待的目标,再等待下一次失去动力。 不满足现状,寄希望于将来。碌碌终日,郁郁寡欢。 成长大概 觉、知、行 三字。 行动是进步的拦路虎,也是突破的登云梯。 不妨就从今天开始吧,迈步踏上行程,再辨路在何方。 计划、执行、复盘、迭代,周而复始,终至终途。

2023/11/11
articleCard.readMore

记一次 Notion 油猴脚本的编写经历

起因 最近借助 Notion 作为数据库搭建了《开源服务指南》的工作流,Notion 是真的好用,无奈复制出来的 Markdown 文本中图片部分格式不对(不知道是不是我使用方法有问题),需要手动修正。一回生二回熟三回咱可不就烦了嘛,所以想着写个油猴插件解决 把 Notion Page 内容复制为标准 Markdown 文本 这个问题。 折腾 获取页面 HTML,然后 HTML 转 Markdown 先查了一下资料,在 GreasyFork 搜索 notion 和 markdown 关键字,找到了这么个脚本:复制为Markdown格式 使用类似的思路,先获取目标 DOM 的 HTML 代码,然后 HTML 转 Markdown。但是理想很丰满,显示很骨感。 实际上 Notion Page 内容并不是标准的语义化的 HTML 标签,而是使用了大量的 div 和自定义样式。所以转换效果并不理想,会有很多样式丢失。 比如无序列表变成了普通文本,代码块也会变成普通文本等。这个问题也不是不能修复,只是要把对应的 HTML DOM 替换成标准格式,略微繁琐,遂放弃。 以下是相关代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 // ==UserScript== // @name Notion-Copy-Then-Fix-Markdown-v1 // @name:zh-CN Notion 复制 Markdown 格式修复 v1 // @namespace http://tampermonkey.net/ // @version 0.1 // @description 修复 Notion 复制内容后 Markdown 格式问题 // @author Seven // @match *://www.notion.so/* // @icon https://www.google.com/s2/favicons?sz=64&domain=notion.so // @require https://unpkg.com/turndown/dist/turndown.js // @grant none // ==/UserScript== (function() { 'use strict'; const urlProtocol = document.location.protocol; const urlOrigin = document.location.origin; const urlPath = (document.location.pathname.substring(0, document.location.pathname.lastIndexOf('/'))) + '/'; function initCopyButton() { let copyButton = document.createElement('div'); copyButton.style.position = "fixed"; copyButton.style.width = "44px"; copyButton.style.height = "22px"; copyButton.style.lineHeight = "22px"; copyButton.style.top = "14%"; copyButton.style.right = "1%"; copyButton.style.background = "#0084ff"; copyButton.style.fontSize = "14px"; copyButton.style.color = "#fff"; copyButton.style.textAlign = "center"; copyButton.style.borderRadius = "6px"; copyButton.style.zIndex = 10000; copyButton.style.cursor = "pointer"; copyButton.style.opacity = 0.6; copyButton.innerHTML = "Copy Content"; copyButton.addEventListener('click', copyPageContent); console.log('initCopyButton'); document.body.prepend(copyButton); } function getContentElement() { let pageContent = document.querySelector('main.notion-frame .notion-page-content'); if (!pageContent) { return; } return pageContent.innerHTML; } function html2Markdown(htmlText) { if (!htmlText) { return; } htmlText = htmlText .replace(/<figure[\s\S]+?<\/figure>/gi, processFigure) .replace(/<img[^>]+>/gi, processImg) .replace(/(<a.+?href=")(.*?")(.*?<\/a>)/gi, parseHref) ; // 引入 turndown.js 库 let turndownService = new TurndownService(); let markdownText = turndownService.turndown(htmlText); markdownText = markdownText.replace(/<img.+?>/g, ""); return markdownText; } function processFigure(str) { str = str.replace(/<noscript>[\s\S]*<\/noscript>/, ''); let img = str.match(/<img[^>]+?>/); if (img) { return img[0]; } return str; } function processImg(imgStr) { let src = (imgStr.match(/\ssrc=(["'])(.*?)\1/) || [])[2]; if (!src) { return ''; } let original = (imgStr.match(/\sdata-original=(["'])(.*?)\1/) || [])[2]; if (original) { src = original; } if (src.toLowerCase().indexOf('http') === 0) { return '<img src="'+src+'" />'; } else if (src.indexOf('//') === 0) { src = urlProtocol + src; } else if (src.indexOf('/') === 0) { src = urlOrigin + src; } else { src = urlPath + src; } return '<img src="'+src+'" />'; } function parseHref(match, head, link, tail){ if (link.substr(0, 4) === 'http') { return head + link.replace(/#.*/,"") + tail; } var path = document.location.pathname.split('/'); path.pop(); if (link[0] === '#' || link.substr(0, 10) === 'javascript' || link === '"') { // "#" "javascript:" "" return head + '#"' + tail; } else if (link[0] === '.' && link[1] === '/'){ // "./xxx" return head + document.location.origin + path.join('/') + link.substring(1) + tail; } else if (link[0] === '.' && link[1] === '.' && link[2] === '/') { // ../xxx var p2Arr = link.split('../'), tmpRes = [p2Arr.pop()]; path.pop(); while(p2Arr.length){ var t = p2Arr.pop(); if (t === ''){ tmpRes.unshift(path.pop()); } } return head + document.location.origin + tmpRes.join('/') + tail; } else if (link.match(/^\/\/.*/)) { // //xxx.com return head + document.location.protocol + link + tail; } else if (link.match(/^\/.*/)) { // /abc return head + document.location.origin + link + tail; } else { // "abc/xxx" return head + document.location.origin + path.join("/") + '/' + link + tail; } } function copyToClipboard1(text) { const input = document.createElement('textarea'); input.style.position = 'fixed'; input.style.opacity = 0; input.value = text; document.body.appendChild(input); input.select(); const res = document.execCommand('copy'); document.body.removeChild(input); return res; } function copyToClipboard(text) { navigator.clipboard.writeText(text) .then(() => { console.log('文本已成功复制到剪贴板'); }) .catch((err) => { console.error('复制操作失败', err); }); } function copyPageContent() { const innerHtmlOfPageContent = getContentElement(); let markdownContent = html2Markdown(innerHtmlOfPageContent); markdownContent = fixMarkdownContent(markdownContent); console.log(markdownContent); // 将转换后的 Markdown 文本写入剪贴板 navigator.clipboard.writeText(markdownContent) .then(() => { showMessage('复制成功'); }) .catch((err) => { showMessage('复制失败'); }); const copyResult = copyToClipboard(markdownContent); const message = copyResult ? '复制成功' : '复制失败'; // 添加提示信息(可选) } function fixMarkdownContent(markdown) { if (!markdown) { return; } const regex = new RegExp(`!\\[\\]\\(${urlOrigin}\\/image\\/(http.*?)\\?.*?\\)`, 'g'); return markdown.replaceAll(regex, (match, group1) => { const processedText = decodeURIComponent(group1); return `![picture](${processedText})`; }); } // 添加提示信息的函数(可选) function showMessage(message) { const toast = document.createElement('div'); toast.style.position = 'fixed'; toast.style.bottom = '20px'; toast.style.left = '50%'; toast.style.transform = 'translateX(-50%)'; toast.style.padding = '10px 20px'; toast.style.background = 'rgba(0, 0, 0, 0.8)'; toast.style.color = 'white'; toast.style.borderRadius = '5px'; toast.style.zIndex = '9999'; toast.innerText = message; document.body.appendChild(toast); setTimeout(function() { toast.remove(); }, 3000); } function init() { initCopyButton(); } init(); })(); 借用 clipboard.js 执行复制功能 无意间发现了一个叫作 clipboard.js 的工具,看起来很方便。试了一下,只能复制 DOM 的 innerText,并不能复制 DOM 元素(或者说触发 Notion 本身的复制方法),作罢。 以下是代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 // ==UserScript== // @name New Userscript // @namespace http://tampermonkey.net/ // @version 0.1 // @description try to take over the world! // @author You // @match https://www.notion.so/futureforcedao/No-42-a696f6e800174eeeac124f56cf2949da // @icon https://www.google.com/s2/favicons?sz=64&domain=notion.so // @grant none // @require https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.11/clipboard.min.js // ==/UserScript== (function() { 'use strict'; init(); /** * 初始化动作 */ function init() { waitFor('#notion-app .notion-page-content').then(([notionContentElement]) => { initCopyButton(); }); } /** * 初始化复制按钮 */ function initCopyButton() { let copyButton = document.createElement('div'); copyButton.id = 'copyButton'; copyButton.style.position = "fixed"; copyButton.style.width = "88px"; copyButton.style.height = "22px"; copyButton.style.lineHeight = "22px"; copyButton.style.top = "14%"; copyButton.style.right = "1%"; copyButton.style.background = "#0084ff"; copyButton.style.fontSize = "14px"; copyButton.style.color = "#fff"; copyButton.style.textAlign = "center"; copyButton.style.borderRadius = "6px"; copyButton.style.zIndex = 10000; copyButton.style.cursor = "pointer"; copyButton.style.opacity = 0.6; copyButton.innerHTML = "Copy Content"; // copyButton['data-clipboard-target']='#notion-app .notion-page-content'; console.log('initCopyButton'); document.body.prepend(copyButton); var clipboard = new ClipboardJS('#copyButton', { target: function(trigger) { return document.querySelector('#notion-app .notion-page-content'); } }); clipboard.on('success', function(e) { console.info('Action:', e.action); console.info('Text:', e.text); console.info('Trigger:', e.trigger); e.clearSelection(); }); clipboard.on('error', function(e) { console.error('Action:', e.action); console.error('Trigger:', e.trigger); }); } /** * 等待指定 DOM 元素加载完成之后再执行方法 */ function waitFor(...selectors) { return new Promise(resolve => { const delay = 500; const f = () => { const elements = selectors.map(selector => document.querySelector(selector)); if (elements.every(element => element != null)) { resolve(elements); } else { setTimeout(f, delay); } } f(); }); } })(); 直接调用系统复制功能,然后读取剪切板内容并进行替换 后面我发现这个复制为 Markdown 的功能应该是 Notion 自己实现的,并不是粘贴时编辑器把富文本变成了 Markdown。既然如此,为啥不直接调用 Notion 自己的复制功能,然后我们读取剪切板再做内容修正嘞? 说干就干,搞完之后还是有一个很奇怪的小问题:同一个页面第一次复制内容不对,但是从第二次开始就对了。 各种调测,最后发现好像(嗯,只是好像我也没深究)Notion 是在我执行复制操作之后花了一定时间才完成 Markdown 的转换工作,遂不得已加了一个延时操作,问题解决。 以下是代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 // ==UserScript== // @name Notion-Copy-Then-Fix-Markdown // @name:zh-CN Notion 复制 Markdown 格式修复 // @namespace http://tampermonkey.net/ // @version 0.1 // @description 修复 Notion 复制内容后 Markdown 格式问题。感谢 [复制 Notion 页面正文内容](https://greasyfork.org/zh-CN/scripts/398563-copy-notion-page-content/code) 为本脚本提供了诸多参考。 // @author Seven // @match *://www.notion.so/* // @icon https://www.google.com/s2/favicons?sz=64&domain=notion.so // @grant GM_setClipboard // ==/UserScript== (function() { 'use strict'; init(); /** * 初始化动作 */ function init() { waitFor('#notion-app .notion-page-content').then(([notionContentElement]) => { initCopyButton(); }); } /** * 初始化复制按钮 */ function initCopyButton() { let copyButton = document.createElement('div'); copyButton.style.position = "fixed"; copyButton.style.width = "88px"; copyButton.style.height = "22px"; copyButton.style.lineHeight = "22px"; copyButton.style.top = "14%"; copyButton.style.right = "1%"; copyButton.style.background = "#0084ff"; copyButton.style.fontSize = "14px"; copyButton.style.color = "#fff"; copyButton.style.textAlign = "center"; copyButton.style.borderRadius = "6px"; copyButton.style.zIndex = 10000; copyButton.style.cursor = "pointer"; copyButton.style.opacity = 0.6; copyButton.innerHTML = "Copy Content"; copyButton.addEventListener('click', copyPageContentAsync); console.log('initCopyButton'); document.body.prepend(copyButton); } /** * 复制 Notion Page 内容 */ function copyPageContentSync() { copyElementSync('#notion-app .notion-page-content'); // copyElementChildren('#notion-app .notion-page-content'); navigator.clipboard.readText() .then(clipboardContent => { console.log('clipboardContent', clipboardContent); const markdownContent = fixMarkdownFormat(clipboardContent); console.log('markdown', markdownContent); GM_setClipboard(markdownContent); showMessage('复制成功'); }); } async function copyPageContentAsync() { await copyElementAsync('#notion-app .notion-page-content'); // copyElementChildren('#notion-app .notion-page-content'); const clipboardContent = await readClipboard(); if (!clipboardContent) { showMessage('复制失败'); return; } console.log('clipboardContent', clipboardContent); const markdownContent = fixMarkdownFormat(clipboardContent); console.log('markdown', markdownContent); GM_setClipboard(markdownContent); showMessage('复制成功'); } /** * 修正 markdown 格式 */ function fixMarkdownFormat(markdown) { if (!markdown) { return; } // 给没有 Caption 的图片添加 ALT 文字 return markdown.replaceAll(/\!(http.*\.\w+)/g, (match, group1) => { const processedText = decodeURIComponent(group1); console.log('regex', processedText); return `![picture](${processedText})`; }); // TODO 给有 Caption 的图片去除多余文字 } /** * 读取系统剪切板内容 */ async function readClipboard() { try { const clipText = await navigator.clipboard.readText(); return clipText; } catch (error) { console.error('Failed to read clipboard:', error); } } function copyElementChildren(selector) { const dom = document.querySelector(selector); const range = document.createRange(); range.setStart(dom, 0); range.setEnd(dom, dom.childNodes.length); const selection = window.getSelection(); selection.removeAllRanges(); selection.addRange(range); document.execCommand('copy'); // selection.removeAllRanges(); } /** * 复制 DOM 元素(在 DOM 元素上执行复制操作) */ async function copyElementAsync(selector) { const pageContent = document.querySelector(selector); // pageContent.focus(); let range = document.createRange(); range.selectNodeContents(pageContent); let selection = window.getSelection(); selection.removeAllRanges(); selection.addRange(range); pageContent.focus(); await sleep(500); document.execCommand('copy'); selection.removeAllRanges(); } function copyElementSync(selector) { const pageContent = document.querySelector(selector); // pageContent.focus(); let range = document.createRange(); range.selectNodeContents(pageContent); let selection = window.getSelection(); selection.removeAllRanges(); selection.addRange(range); pageContent.focus(); document.execCommand('copy'); // selection.removeAllRanges(); } /** * 在页面显示提示信息 */ function showMessage(message) { const toast = document.createElement('div'); toast.style.position = 'fixed'; toast.style.bottom = '20px'; toast.style.left = '50%'; toast.style.transform = 'translateX(-50%)'; toast.style.padding = '10px 20px'; toast.style.background = 'rgba(0, 0, 0, 0.8)'; toast.style.color = 'white'; toast.style.borderRadius = '5px'; toast.style.zIndex = '9999'; toast.innerText = message; document.body.appendChild(toast); setTimeout(function() { toast.remove(); }, 3000); } /** * 等待指定 DOM 元素加载完成之后再执行方法 */ function waitFor(...selectors) { return new Promise(resolve => { const delay = 500; const f = () => { const elements = selectors.map(selector => document.querySelector(selector)); if (elements.every(element => element != null)) { resolve(elements); } else { setTimeout(f, delay); } } f(); }); } /** * 延迟执行 **/ function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } })(); 直接追加一个 EventListener 做格式修正 前面的代码,就算加了延时还是会有一个小 bug:一键复制的时候偶尔会少复制最后一个 Notion Block 内容。 更新:出现这个问题的原因不明,但是只要在 Notion 文章最后加入一个空行,就能避免这个问题 最后还是硬着头皮看了看 Notion 的代码,发现 Notion 的复制事件是放在 window 上的(怪不得我之前在 document 上面找不到),既然找到了 Notion 自己的复制事件就好办了。最简单的方法就是在 window 上面追加一个 copy 事件,做额外的 Markdown 格式修正工作。 以下是代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 // ==UserScript== // @name copy-notion-page-content-as-markdown // @name:en Copy Notion Page Content AS Markdown // @name:zh-CN 复制 Notion Page 内容为标准 Markdown 文本 // @description 复制 Notion Page 内容为标准 Markdown 文本。 // @description:zh-CN 复制 Notion Page 内容为标准 Markdown 文本。 // @description:en Copy Notion Page Content AS Markdown. // @namespace https://github.com/Seven-Steven/tampermonkey-scripts/tree/main/copy-notion-page-content-as-markdown // @version 0.2 // @license MIT // @author Seven // @match *://www.notion.so/* // @icon https://www.google.com/s2/favicons?sz=64&domain=notion.so // ==/UserScript== (function () { 'use strict'; init(); /** * 初始化动作 */ function init() { injectActions(); } function injectActions() { window.addEventListener('copy', fixNotionMarkdownInClipboard); } /** * 修正剪切板中 markdown 的格式 */ function fixNotionMarkdownInClipboard() { navigator.clipboard.readText().then(text => { const markdown = fixMarkdownFormat(text); navigator.clipboard.writeText(markdown).then(() => { }, () => { console.log('failed.'); }) }) } /** * 修正 markdown 格式 */ function fixMarkdownFormat(markdown) { if (!markdown) { return; } // 给没有 Caption 的图片添加默认 ALT 文字 markdown = markdown.replaceAll(/\!(http.*\.\w+)/g, (match, group1) => { const processedText = decodeURIComponent(group1); console.log('regex', processedText); return `![picture](${processedText})`; }); // 给有 Caption 的图片去除多余文字 const captionRegex = /(\!\[(?<title>.+?)\]\(.*?\)\s*)\k<title>\s*/g; return markdown.replaceAll(captionRegex, '$1'); } })(); 为了追求简单,这里移除了一键复制操作,后面有空再补。 完美方案 页面分析 对于 view / database 等页面:Notion Page 可能会显示在 Center Peek / Side Peek 中。需要动态监听最近的祖先元素子节点变动,动态装载/卸载插件。 页面结构如下: div#notion-app div.notion-app-inner div.notion-cursor-listener 到这里是一定会有,再往下是 Peek,不一定会有 div.notion-peek-renderer div.layout.layout-side-peek div.notion-page-content 对于正常的 Notion Page 页面,只需要装载插件就好,不需要卸载。 结构如下: div#notion-app div.notion-app-inner div.notion-cursor-listener main.notion-frame div.notion-page-content 插件装载时机 开局就找 .notion-page-content 如果找得到,装载插件,结束 如果找不到,不做任何事情,结束。 同步查找 #notion-app main.notion-frame .notion-page-content 如果找得到,代表当前页面是正常的 Notion Page 页面,装载插件 如果找不到,代表当前页面是 view / database 页面, 给 #notion-app 添加 observe,监听其子节点变动,根据子节点变动情况动态装载 / 卸载插件。结束。 插件行为 先给 window 追加一个类型为 copy 的 EventListener,事件触发时,读取剪切板内容并修正 Markdown 格式; 往页面注入一个“复制”按钮,用户点击按钮时,自动选中 Notion 页面内容并触发 copy 事件; 代码 Github 后记 技术不到位,就只能各种摸爬滚打。 按理说如果底子够硬的话是能找到 Notion 的转换方法的,直接调用就行了。 多看文档,了解一下浏览器有什么 API,每个 API 怎么调用、有什么能力,往往能起到事半功倍的效果。 脚本已发布至 Greasy Fork 可以自行下载使用。 参考文档 Event | Events | DOM-Level-3-Events | JavaScript 事件顺序 JS 中的事件接口、事件类型、以及事件执行顺序。 EventTarget MutationObserver MutationRecord MutationRecord/addedNodes MutationRecord/removedNodes ClipboardAPI ClipboardEvent copy_event 创建和触发事件 复制 Notion 页面正文内容 这里有个等待页面 DOM 元素加载完成之后执行操作的方法非常棒,学到就是赚到。 复制为Markdown格式 借助 turndown.js 实现 HTML 转 Markdown,开眼。 选择(Selection)和范围(Range) 概念讲解,写得很详细,但我没看完。 防抖与节流 防抖与节流

2023/10/9
articleCard.readMore

以前的我 可能是一把利刃 指哪儿打哪儿 削金如麻 人都说刀客 天下无敌 谁能见刀身 斑痕累累 现在这把刀有了刀灵 开始思索过去和未来 开始连接缘因和结果 开始想跳脱大道束缚 做自己的执刀人

2023/4/13
articleCard.readMore

裹挟

江河湖泊, 每一滴水都在往前走, 但它们决定不了自己的流向。 或许, 我们不应该一直低头猛冲。 偶尔抬头看看天, 看能否厚积薄发,升华自我, 变成一片云。 只是, 被风裹挟着的云, 和被河裹挟着的水, 又有什么区别呢?

2023/3/29
articleCard.readMore

蒲公英

我最近突然对自己有了相对来说更加清醒的认知。 在互联网的世界里 我把自己比作蒲公英。 乘风而行,能比更多人更早地见到更多更新鲜的事物。 同时也是随风漂泊,居无定所。风过之后,尽是虚无。 能乘风但不能御风,观万物但未能留痕。 当下之路,或许是择一地生根发芽,向阳处恣意生长。

2023/3/28
articleCard.readMore

月刊-202208

拓认知 学习,没有捷径 开发资源 CompVis/stable-diffusion 根据文字描述生成精美图片。 megaease/easegress 全功能型的流量调度和编排系统。官网 | 文档 alibaba/DataX 支持超多数据库的离线数据同步工具。 gerrit code review 工具 3D 白模生成 blender + 插件 domlysz/BlenderGIS 前端学习资料 入门:https://www.freecodecamp.org 补充:https://scrimba.com/learn/frontend JavaScript: https://zh.javascript.info MDN: https://developer.mozilla.org/en-US Vue: https://vuejs.org React: https://reactjs.org or https://beta.reactjs.org ES6 Standard: https://www.ecma-international.org/publications-and-standards/standards/ecma-262/ CSS 库 50projects50days 50 个 web 小 demo,用来学习再好不过了 30-seconds/30-seconds-of-css 一些 css 代码片段,可以拿来学习练手 Tailwind CSS 一个功能类优先的 CSS 框架 animate.css css 动效库 normalize.css 让你的 HTML 默认样式保持跨浏览器的一致性 bulma 基于 Flexbox 的现代化响应式 css 框架 IanLunn/Hover 给你的网站添加 hover 动效 tobiasahlin/SpinKit 有趣的页面加载动效 商城系统 wei-it/weiit-saas weiit-saas是一款Java开源项目,属于weiit团队自研产品,意在通过技术封装,让企业无需代码开发,帮助企业一键生成小程序、公众号,让企业拥有独立品牌的自营商城。 fuint会员营销系统 一款实体店铺会员管理和营销系统。 开店星开源小程序商城 Lilishop B2B2C商城系统 免费开源的B2B2C商城系统 看世界 10 个你没见过的 GitHub 的高效开源神器,YYDS! 盘点 7 个神级笔记开源应用! craiyon 根据文本描述生成图片

2023/2/26
articleCard.readMore

免费申请 JetBrains 全家桶开源授权

JetBrains 开源授权 JetBrains 针对部分群体推出了一系列特惠计划,其中包括 开源开发许可证。通过开源开发许可证,我们可以免费使用 JetBrains 旗下几乎所有开发工具。 本文讲述了如何申请 JetBrains 的 开源开发许可证。 申请条件 总结一下: 条件: 项目符合 开源 定义 开源项目正在积极开发且最近三个月内有更新 申请人是这个开源项目的项目所有者或主要贡献者,提交申请时所填写的邮箱要和项目贡献人的邮箱以及 Github Profile 邮箱保持一致(申请期间 Github Profile 页面的邮箱不能隐藏,工作人员需要通过这个邮箱复核申请者的身份) 不提供开源项目的付费版本,也不提供与该开源项目相关的任何付费服务(比如付费支持、咨询等) 开源项目未获得商业公司或组织(NGO、教育、研究或政府组织)的资助 不为项目的核心开发者支付工资 鼓励开源项目提供社区标准文档,比如在 README 文件中提供 项目描述、行为准则和贡献指南 暂时对项目质量没有要求 限制 许可证仅提供给项目负责人和核心项目提交者 许可证有效期为一年,如果一年后这个项目仍符合申请条件,可以继续申请 免费许可证只能用于开发非商业开源项目 不能与任何第三方共享许可证 申请过程 打开 JetBrains 开源开发许可证申请页面 Request for Open Source Development License 并填写申请资料: 有以下几点作特别说明: Do we Know you? 申请新的授权或者延续之前已经申请过的授权,如实填写即可; Tell us about your project Project website: 如果项目有独立主页,填写项目主页地址;如无,填写同 Repository URL 即可; License URL: 如有,填写项目开源协议地址;如无,在 Github 项目主页点击 “Add file” -> “Create new file”,文件名写 “LICENSE”,页面右侧会自动出现 LICENSE 选择提示,选择并添加适合自己的开源协议即可; No. of required licenses: 表示要申请的开源开发许可证数量,数量需小于或者等于项目活跃的核心开发者数量; Tell us about youself Email address: 这里所填写的邮箱要和项目贡献人的邮箱以及 Github Profile 邮箱保持一致(申请期间 Github Profile 页面的邮箱不能隐藏,工作人员需要通过这个邮箱复核申请者的身份) 资料填写完成后提交申请,会跳转到如下页面: 同时,我们会收到一封邮件,告诉我们 JetBrains 已经收到了刚刚提交的申请,团队大概会在一周内审查我们的开源项目。 耐心等待几天,JetBrains 会在审查完成之后发送另一封邮件。告知我们申请已通过审批并指引我们领取 License,或者告知你拒绝审批的原因。 如果审批通过,可以点击邮件中的 “Take me to my license(s)” 链接,将 Licenses 绑定到对应的帐号。 至此,我们已经免费拿到了 JetBrains 的开源开发许可证,许可证有效期一年,届时可视官方政策继续申请。 If you find that JetBrains software has been useful for your project, please consider mentioning JetBrains support on your project’s homepage. Feel free to use the JetBrains logo and a link to our website such as https://jb.gg/OpenSourceSupport. 参考文档 免费申请和使用IntelliJ IDEA商业版License指南

2022/8/7
articleCard.readMore

使用 dockerfile-maven-plugin 构建 docker 镜像

简介 本文介绍了 com.spotify:dockerfile-maven-plugin 的简单使用示例。 最终达成的目标是把 docker 镜像构建集成在 maven 打包过程中,可以使用 maven 命令构建 docker 镜像。 功能相似的插件有三个: com.spotify:dockerfile-maven-plugin 本文使用的插件,已经停止更新,但功能依旧稳定。 com.spotify:docker-maven-plugin 本文所用插件的同胞兄弟,官方不推荐使用,已停止更新。 io.fabric8io:docker-maven-plugin 支持在 pom.xml 中配置 Dockerfile 的各项内容,也支持自定义 Dockerfile,支持操作容器,功能强大,仍在更新。 使用 以 spring-boot-web 项目为例: 创建一个 spring-boot-web 项目并确保项目正常; 依据项目需要在合适的位置定制一个 Dockerfile,这里把 Dockerfile 放在了 src/main/docker 目录下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 FROM openjdk:8-jdk-alpine # 设置时区 ENV TZ Asia/Shanghai RUN apk --no-cache add tzdata && cp /usr/share/zoneinfo/${TZ} /etc/localtime \ && echo ${TZ} > /etc/timezone \ && apk del tzdata VOLUME /tmp # 定义变量 ARG JAR_FILE ADD target/${JAR_FILE} app.jar ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"] # 声明服务以 tcp 协议运行在 8080 端口 EXPOSE 8080/tcp 在 project 标签下添加插件配置: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 <build> <plugins> <!--dockerfile-maven-plugin 配置--> <plugin> <groupId>com.spotify</groupId> <artifactId>dockerfile-maven-plugin</artifactId> <version>1.4.13</version> <executions> <execution> <id>build</id> <!--绑定 maven 阶段--> <!--install 表示运行 mvn install 时会自动执行 docker 镜像构建--> <!--install 也可以改成 package,运行 mvn package 时会自动执行 docker 镜像构建--> <phase>install</phase> <goals> <!--执行插件的目标--> <goal>build</goal> </goals> </execution> </executions> <configuration> <!--指定 docker 镜像仓库--> <repository>cn/diqigan/${project.artifactId}</repository> <!--指定 docker 镜像标签--> <tag>${project.version}</tag> <!--指定 Dockerfile 路径--> <dockerfile>src/main/docker/Dockerfile</dockerfile> <!--设置 Dockerfile 中变量--> <buildArgs> <!--对应 Dockerfile 中的 JAR_FILE 变量--> <JAR_FILE>${project.build.finalName}.jar</JAR_FILE> </buildArgs> </configuration> </plugin> <!--dockerfile-maven-plugin 配置--> </plugins> </build> 构建镜像 镜像构建有两种方式: 绑定 maven 阶段自动构建: 以文中所示配置,执行 mvn install 指令会自动构建 docker 镜像。 显式运行构建指令 mvn package dockerfile:buld 构建 docker 镜像。 可以在 maven 控制台日志中看到 docker 镜像已经构建成功。 跳过镜像构建 如果前面在 pom.xml 插件配置中绑定了在 maven 阶段自动构建 docker 镜像,但是某次打包过程中又不希望自动构建 docker 镜像,可以通过 dockerfile.skip 参数跳过 docker 镜像构建。 1 mvn clean install -Ddockerfile.skip 后记 个人不推荐在 pom.xml 中配置 Dockerfile 的各项内容,有种配置侵入的感觉,建议另写一个 Dockerfile 文件; 持续集成过程中完全可以使用 docker 相关指令构建镜像,maven 插件有点画蛇添足的意思; 本文相关代码可在 Seven-Steven/spring-examples 查看; 参考文档 官方使用文档 官方使用示例

2022/7/16
articleCard.readMore

学习笔记-专利写作第一课

找到 KeyPerson 利益点,提高专利通过率 好专利的四个标准 创新性:人无我有,人有我优; 侵权认定:公开(专利内容)换取独占(20年),需要用公开的内容判定他人是否侵权; 市场价值: 以量取胜,专利需要在某个领域积累一定的数量。才能够在这个领域形成威慑力; 聚焦业务,专利需要和公司核心业务相关; 专利内容最好和实体设备相关(方便计算侵权价值); 不可替代性:需要考虑别人是否有可能通过优化绕过我们的专利; 高通过率的专利申请策略 核心角色:业务部门老大、法务代表 业务部门老大:这个专利和我们部门有什么关系? 公司法务代表:公司核心业务 >= 未来发展方向 >= 发明人所在的部门的技术方向 专利发明人选题策略:本职工作相关 >= 公司核心业务 >= 公司未来方向 撰写专利交底书 对创意进行足够的抽象,才能覆盖更多的范围(比如手机闹钟,可以抽象成一种提醒服务); 将概念中的逻辑结构进行标准化论述和概念明确; 专利交底书、专利文书和内部审批流程的关系: 名: 核心成员应该是第一、第二、第三发明人之一; 利: 必须俺劳分配,切忌平均。核心成员 80%; 专利不一定要应用在现有业务中,也可以是未来的布局; 发明专利的前因:从因果论的角度出发,分析行业现有技术的发展情况、普及相关知识、发现不足,提出创新方案; 名称、关键词以及解释 将专利的核心理念进行具象描述,力求把专利讲清楚; 用一句话来概括这个发明,无论多长,只用一句话来组织; 提取这句话中的关键名词或者动名词,去除所有的无关词汇,得到关键词。如果有些关键词是不常见的技术属于,就补充相关解释; 现有技术方案:用来解释相对生辟的技术,也可以不写; 现有技术的不足以及创新解决思路:这一部分非常关键,是专利中说明书的主要部分,代理要写得很清楚,知识产权局需要看; 发明创意的后果:核心思路和示意图。前者是抽象的概念和方法,后者是据悉那个的可行性方案。 核心思路和示意图 用语言来描述核心思路,并配上效果图 用流程图和示意图来表达 专利,应该是一个常见功能的简化和抽象,需要屏蔽功能设计时和工程化时要考虑到的各种极限情况,把本次创意中最核心的环节表达出来。 其他替代方案:系统化地描述更多可能性 不要被参数限制,要更多地描述创意本身 不要通过“和”限制,要通过“或”来扩大 合理扩展各种可能性,提高保护范围 专利示例 预设光标样式与用户输入文本样式保持一致(来源:参考了 PS 的实现,把专利从图形迁移到文本) 滑动解锁 提醒用户节假日关闭闹钟 QA 怎么查询专利是否已经被申请了 伯腾网 国内、WIPO 联合国知识产权、欧美专利 Google 专利 国外专利 国家知识产权局 国内专利,最权威、最新

2022/7/4
articleCard.readMore

Linux + picgo 图床自动化实践

前言 因为 PicGo 在 Linux 下的 GUI 体验并不是很好,索性自己通过 Shell 脚本和快捷键的方式实现了较好的用户体验,记录下操作流程,希望能帮到各位。 环境 笔者测试的相关软件环境如下: Manjaro Linux: 5.15.46-1-MANJARO (64位) Node.js: v18.3.0 picgo: 1.5.0-alpha.0 [可选] picgo-plugin-web-uploader: 1.1.1 picgo-plugin-autocopy: 1.0.5 xclip: 0.13 notify-send: 0.7.12 [可选] espeak: eSpeak text-to-speech: 1.48.03 04.Mar.14 安装 picgo 安装 Node.js 1 yay -S nodejs 安装 picgo 1 2 3 4 5 npm install picgo -g # or yarn global add picgo 安装 picgo 插件 可以借助 picgo 插件实现一些特殊功能。 picgo-plugin-autocopy 图片上传完毕后自动拷贝链接到剪切板。 1 picgo install autocopy [可选] picgo-plugin-web-uploader 用来实现自定义 web 图床,如果不需要自定义 web 图床,可以忽略这个插件。 1 picgo install web-uploader picgo 的配置文件默认存储在 ~/.picgo/config.json,picgo-plugin-web-uploader 的配置可以参考 官方文档。 这里是一份 美日小站 的自定义图床配置: 1 2 3 4 5 6 7 8 9 10 "picBed": { "current": "web-uploader", "web-uploader": { "customBody": null, "customHeader": null, "url": "https://www.ladydaily.com/tools/upload/${token}", "paramName": "file", "jsonPath": "data.o_url" } } 插件配置完成之后,可以使用 picgo upload 命令验证配置是否正确。 [可选] picgo-plugin-s3 用来支持 s3 协议的图片上传,如果不需要使用 s3 协议,可以忽略这个插件。 picgo 的配置文件默认存储在 ~/.picgo/config.json,picgo-plugin-s3 的配置可以参考 官方文档。 这里是一份 美日小站 的自定义 s3 图床配置: 1 2 3 4 5 6 7 8 "aws-s3": { "accessKeyID": ${accessKeyId}, "secretAccessKey": ${secretAccessKey}, "bucketName": ${bucketName}, "uploadPath": "{year}/{md5}.{extName}", "endpoint": "s3.ladydaily.com", "urlPrefix": "https://${bucketName}.s3.ladydaily.com/" } 编写 shell 脚本 借助 xclip 实现了剪切板相关操作,manjaro 下安装 xclip 的命令为 sudo pacman -Sy xclip。 [可选] 借助 espeak 实现了语音提示功能,manjaro 下安装 xclip 的命令为 yay -S espeak。如果不需要语音提示,删除脚本中的相关代码即可。 我这里把脚本写在了 ~/.script/app/picog-upload.sh 文件中,内容如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #!/bin/zsh source $HOME/.zshrc picgo_upload_result=`picgo upload` if [ $? -ne 0 ]; then # 系统提示图片上传失败并显示失败原因 notify-send -u normal -t 5000 -a picgo 'picture upload failed' $picgo_upload_result # 语音提示图片上传失败 # espeak 'picture upload failed' exit else # 系统提示图片上传成功 notify-send -u normal -t 5000 -a picgo 'picture puload succees' `xclip -o -selection clipboard` # 删除 xclip 自动添加的换行符 xclip -o -selection clipboard | sed '/^[[:blank:]]*$/d' | xclip -selection clipboard -r # 语音提示图片上传成功 # espeak 'picture upload succeed' fi 给脚本添加可执行权限: 1 chmod +x ~/.script/app/picgo-upload.sh 现在,可以使用命令 ~/.script/app/picgo-upload.sh 测试脚本是否正常。 配置快捷键 打开 manjaro 系统设置,选择 “工作区”-“快捷键”-“自定义快捷键”-点击鼠标右键-选择“新建组”-创建一个 “custom” 分组: 鼠标右键 “custom” 分组,选择“新建”-“全局快捷键”-“命令/URL:”,创建一个名为 “picgo-upload”的快捷键: 点击“触发器”,设置自己喜欢的快捷键,我这里设置的是 Ctrl+Alt+U: 点击 “操作”,在 “命令/URL:”输入框中填写脚本路径 ~/.script/app/picgo-upload.sh; 勾选 “custom” 分组和 “picgo-upload” 快捷键,点击“应用”使配置生效: 到这里,快捷键就配置完成了,拷贝一张图片,然后使用按下 Ctrl+ Alt+U 测试快捷键,不出意外的话,图片会上传成功并且在右上角弹窗提示: 同时,图片的 url 会自动复制到你的剪切板中。

2022/6/15
articleCard.readMore

使用 git 备份和恢复 dotfiles

前言 Unix 用户的配置文件一般存储在以 . 开头的文件中,这些文件被统称为 “dotfiles”。 本文讲述了一种极其优雅的通过 git 备份和恢复 dotfiles 的方法。 备份 初始化 git 仓库 1 2 3 4 5 6 # 初始化 git 仓库 git init --bare $HOME/.dotfiles # 指定 git 仓库和工作树路径并创建指令别名,简化操作 cp -a .bashrc{,.bak} && echo "alias dotfiles='git --git-dir=$HOME/.dotfiles/ --work-tree=$HOME'" >> .bashrc && source .bashrc # git status 不显示未跟踪的文件 dotfiles config status.showUntrackedFiles no 创建远程仓库,比如 git@github.com/seven/dotfiles 添加文件 1 2 3 4 dotfiles add .zshrc dotfiles commit -m "add .zshrc" dotfiles remote add origin ${git_repo} dotfiles push 恢复 1 2 3 4 5 6 7 8 9 10 # 把 dotfiles 克隆到本地临时目录 git clone --separate-git-dir=$HOME/.dotfiles ${git_repo} $HOME/dotfiles-tmp # 用临时目录中的文件覆盖本地文件 cp $HOME/dotfiles-tmp/.* $HOME # 删除临时目录 rm -r $HOME/dotfiles-tmp/ # 指定 git 仓库和工作树路径并创建指令别名,简化操作 cp -a .bashrc{,.bak} && echo "alias dotfiles='git --git-dir=$HOME/.dotfiles/ --work-tree=$HOME'" >> .bashrc && source .bashrc # git status 不显示未跟踪的文件 dotfiles config status.showUntrackedFiles no git 参数讲解 git init --bare ${path} Create a bare repository. If GIT_DIR environment is not set, it is set to the current working directory. 在 ${path} 目录创建一个空的 git 仓库。 git --git-dir=${git_dir} Set the path to the repository (“.git” directory). This can also be controlled by setting the GIT_DIR environment variable. It can be an absolute path or relative path to current working directory. Specifying the location of the “.git” directory using this option (or GIT_DIR environment variable) turns off the repository discovery that tries to find a directory with “.git” subdirectory (which is how the repository and the top-level of the working tree are discovered), and tells Git that you are at the top level of the working tree. If you are not at the top-level directory of the working tree, you should tell Git where the top-level of the working tree is, with the –work-tree=${git_dir} option (or GIT_WORK_TREE environment variable) If you just want to run git as if it was started in ${git_dir} then use git -C ${git_dir}. 指定 git 仓库的路径。 git --work-tree=${path} Set the path to the working tree. It can be an absolute path or a path relative to the current working directory. This can also be controlled by setting the GIT_WORK_TREE environment variable and the core.worktree configuration variable (see core.worktree in git- config(1) for a more detailed discussion). 指定工作树的路径。 git clone --separate-git-dir=${git_dir} Instead of placing the cloned repository where it is supposed to be, place the cloned repository at the specified directory, then make a filesystem-agnostic Git symbolic link to there. The result is Git repository can be separated from working tree. 把 git 仓库克隆到指定的目录 ${git_dir} 下,然后做一个与文件系统无关的 git 符号链接到该目录。从而使 git 仓库和工作树分离。 这里需要搞懂 git 的两个概念: “git 仓库” 和 “工作树”。 假设我们使用 git clone git@github.com/seven/dotfiles 把远程仓库克隆到了本地的 dotfiles 目录。dotfiles 目录就是“工作树”的路径,dotfiles/.git 目录就是 git 仓库的路径。 举一反三 可以为不同的系统或者不同的电脑创建不同的分支 可以通过灵活指定 git 仓库和工作树路径,达到备份其他文件的目的 参考文档 Hacker News The best way to store your dotfiles: A bare Git repository Dotfiles - ArchWiki 用 Chezmoi 管理配置文件

2022/3/12
articleCard.readMore

学习笔记-持续交付 36 讲

持续交付 36 讲 持续交付是提升软件交付速率的一套工程方法和一系列最佳实践的集合。它的关注点可以概括为:持续集成构建、测试自动化和部署流水线。 持续交付最核心的几个部分包括:配置管理、环境管理、构建集成、发布及监控、测试管理。 基本概念 持续集成、持续交付和持续部署的关系 持续集成 从编码到构建再到测试的反复持续过程。“持续集成”一旦完成,代表产品处在一个可交付但并不是最优的状态。 持续交付 在“持续集成”之后,获取外部对软件的反馈,再通过“持续集成”进行优化的过程叫作“持续交付”。“持续交付”是“持续集成”的自然延续。 持续部署 将可交付的产品,快速且安全地交付用户使用的一套方法和系统。它是“持续交付”的最后一公里。 “持续交付”是一个承上启下的过程,它使“持续集成”形成闭环,有了实际业务价值;又为将来达到“持续部署”的高级目标做好了铺垫。 持续交付的价值不仅仅局限于简单的提高产品交付的效率,它还通过统一标准、规范流程、工具化、自动化等等方式,影响着整个研发生命周期。 影响持续交付的因素 组织和文化 紧密配合,集思广益,自我驱动 流程因素 持续交付一定会打破的三类流程 耗时较长的流程 完全人工的流程 信息报备类的流程 系统架构 部署架构 要有统一的部署标准和方式 在不同的环境和设备上,应用的部署方式和标准应该是一样的。 要考虑发布的编排次序 比如灰度发布。 markdown 与 markup 机制 部署应用时做到业务无损。 应用预热与自检 有些软件在启动后需要处理加载缓存等预热过程,并不是启动完成就可以正常提供服务了。 配置管理 代码分支策略的选择 主干开发 特性分支开发 依赖管理 Maven Enforcer 插件 环境管理 测试环境 环境隔离 测试环境 / 联调环境 <=> 公共环境 环境自描述 / 约定大于配置 环境标准化 规定公司的主流语言栈 统一服务器安装镜像 提供默认的运行时配置模板 统一基础软件的版本以及更新方式 在架构层面统一解决环境路由问题 自动化环境产生过程 技术规范 代码及依赖规范 命名规范 开发规范 配置规范 部署规范 安全规范 测试规范 让服务器自己说话 定义服务器自描述文件 记录这台服务器的所有身份信息,包括:ISC、型号、归属环境、作用、所属应用、服务类型、访问路径等。 解决配置中心寻址 中间件根据服务器自描述文件的描述,寻找到它所在环境对应的配置中心,从而进一步获取其他配置。如数据库连接字符串,短信服务地址等。 完成服务自发现 根据服务类型,访问路径等,还可以自动生成对应的路由配置、负载均衡配置等。 配置 配置管理:通过技术或行政手段对软件产品及其开发过程和生命周期进行控制、规范的一系列措施。它的目标是记录软件产品的演化过程,确保软件开发者在 软件生命周期的各个阶段都能得到精确的产品配置信息。 配置:指独立于程序之外,但又对程序产生作用的可配变量。同一份代码在不同的配置下,会产生不同的运行结果。 配置中心: Apollo 环境构建 虚拟机环境准备 调用 Nova 进行无立即基本的硬件配置 使用 OpenStack 做物理机和虚拟机的初始化工作 使用 Puppet、Chef、Ansible、SaltStack 等开源配置管理工具自动化安装中间件 应用部署流水线 力求做到所有应用可以一次性并行部署 环境变更 构建集成 构建提速 提升硬件资源 搭建私有仓库 createrapp - CentOS 的 yum 仓库 Nexus - java 的 Maven 仓库 cnpm - NodeJS 的 npm 仓库 pypiserver - Python 的 pip 仓库 GitLab - 代码仓库 Harbor - Docker 镜像仓库 使用本地缓存 可以把本地依赖缓存目录 mount 到多台宿主机上。 规范构建流程 比如 Sonar 只做增量代码的检查,节省时间。 善用构建工具 以 Maven 为例: 设置合适的堆内存参数。 可以使用 -Dmaven.test.skip=true / -DskipTests 跳过单元测试。 在发布阶段,不使用 Snapshot 版本的依赖。避免因检查依赖更新而浪费时间。 使用 -T 2C 命令进行并行构建。 局部构建。使用 -pl 参数指定编译某一个或者几个模块。 正确使用 clean 参数。 构建检测 使用 Maven Enforcer 限定依赖版本 Apache Maven Enforcer Built-In Rules Extra Enforcer Rules Docker 安全合规检查 CoreOS Clair, Docker Security Scanning, Drydock 等工具可以对 Docker 镜像进行安全扫描 发布及监控 发布 携程开源灰度发布系统 Tars: 综合了滚动发布和金丝雀发布 发布流程: Markdown -> Download -> Install -> Verify -> Markup 利用软负载或者服务通讯中间件的多个状态位,简单直接地解决多角色对服务 Markup 和 Markdown 的冲突问题(运维工程师手动 Markdown, 持续集成 Markdown 等) 监控 用户侧监控,关注的是用户真正感受到的访问速度和结果。这些监控数据既可以通过打点的方式,也可以通过定期回收日志的方式收集。 端到端的监控,主要包括包括一些访问量、访问成功率、响应时间、发包回包时间等等监控指标。同时,我们可以从不同维度定义这些指标,比如:地区、运营商、App 版本、返回码、网络类型等等。因此,通过这些指标,我们就可以获得用户全方位的感受。 移动端的日志。我们除了关注系统运行的日志外,还会关注系统崩溃或系统异常类的日志,以求第一时间监控到系统故障。 设备表现监控,主要指对 CPU、内存、温度等的监控,以及一些页面级的卡顿或白屏现象;或者是直接的堆栈分析等。 唯一用户 ID 的监控。除了以上三种全局的监控维度外,用户侧的监控一定要具备针对唯一用户 ID 的监控能力,能够获取某一个独立用户的具体情况。 网络监控,即 CDN 与核心网络的监控 公网监控。这部分监控,可以利用模拟请求的手段(比如,CDN 节点模拟、用户端模拟),获取对 CDN、DNS 等公网资源,以及网络延时等监控的数据。当然,你也可以通过采样的方式获取这部分数据。 内网监控。这部分监控,主要是对机房内部核心交换机数据和路由数据的监控。如果你能打造全局的视图,形成直观的路由拓扑,可以大幅提升监控效率。 业务监控,关注的是核心业务指标的波动 监控企业的核心业务指标,能够以最快的速度反应系统是否稳定。 应用监控,即服务调用链的监控。服务与服务之间的调用关系、速度和结果等等。 调用链监控一般需要收集应用层全量的数据进行分析,要分析的内容包括:调用量、响应时长、错误量等;面向的系统包括:应用、中间件、缓存、数据库、存储等;同时也支持对 JVM 等的监控。 也可以结合日志监控。 系统监控,即基础设施、虚拟机及操作系统的监控 系统监控,指的是对基础设施的监控。我们通常会收集 CPU、内存、I/O、磁盘、网络连接等作为监控指标。采用定期采样的方式进行采集,一般选取 1 分钟、3 分钟或 5 分钟的时间间隔。 Tips: 测试环境的监控需要视作用而定,如果不能帮助分析和定位问题,则不需要很全面的监控。 应用发布后持续监控 30 分钟,尽早发现因持续集成引发的服务异常。 完整的运维事件记录体系,可以帮你定位某次故障是否是由发布引起的。 测试管理 代码静态检查 工具: SonarQube 效率提升: 异步检查 增量检查 注意点: 权衡好强制规范检查的度 破坏性测试 破坏性测试就是通过有效的测试手段,使软件应用程序出现奔溃或失败的情况,然后测试在这样的情况下,软件运行会产生什么结果,而这些结果又是否符合预期。 混沌工程: 将正常系统的一些正常行为的可测量数据定义为“稳定态”; 建立一个对照组,并假设对照组和实验组都保持“稳定态”; 引入真实世界的变量,如服务器崩溃、断网、磁盘损坏等等; 尝试寻找对照组和实验组之间的差异,找出系统弱点。 Mock 价值 使测试用例更独立、更解藕 提升测试用例的执行速度 提高测试用例准备的效率 框架选型 基于对象和类的 Mock, 推荐使用 Mockito / EasyMock。适用于单元测试阶段模拟 DAO 层数数据操作和复杂逻辑。 基于微服务的 Mock, 推荐使用 Weir Mock 和 Mock Server。这两个框架都可以很好地模拟 API、http 形式的对象。 回放 记录用户操作,必要时重放用户操作,模拟真实用户请求,用以排查问题。 持续交付平台化 持续交付平台设计 确定模块及其范围 代码管理模块,往往会和代码审核、静态扫描和分支管理等模块相联系; 集成编译模块,也会与依赖管理、单元测试、加密打包等模块相生相随的; 环境管理模块,离不开配置管理、路由管理等模块; 发布部署模块,还需要监控模块和流控模块的支持。 学会做加减法,排优先级 制定标准。对外衔接标准、对内沟通标准、质量标准、速度标准等 选择合适的驱动器 / 技术栈 抽象公共能力 账户与权限,包括单点登录,以及鉴权、授权体系等等; 用户行为日志,即任何行动都要能够被追溯; 消息通知,即提供统一的通知能力; 安全与故障处理,即系统级的防护能力和故障降级。 考虑用户入口 / 用户体验 聚力而成,调动团队的力量 计算资源交付 利用云计算,能够很容易地打破持续交付反模式; 利用云计算,可以使持续交付的编译模块变得更为灵活,提升利用率; 利用云计算,可以自由地发挥想象力,简化环境管理的工作; 利用云计算,可以使用存储服务,使持续交付工作更便捷。 持续交付过程中的数据指标 选择合适的数据指标 数据既要抓全局,也要看细节。注意个别异常数据 数据可以推动持续交付 常见指标 稳定性相关指标 性能相关指标 push 和 fetch 代码的速度 环境创建和销毁的速度 产生仿真数据的速度 平均编译速度及排队时长 静态检查的速度 自动化测试的耗时 发布和回滚的速度 持续交付能力成熟度指标 代码管理子系统相关的指标: commit 数量、code review 的拒绝率、并行开发的分支数量 与环境管理子系统相关的指标包括: 计算资源的使用率、环境的平均大小 与集成编译子系统相关的指标包括: 每日编译数量、编译检查的数据 与测试管理子系统相关的指标包括: 单元测试的覆盖率、自动化测试的覆盖率 与发布管理子系统相关的指标包括: 周发布数量、回滚比率 实践案例 GitLab 代码仓库 Sonar 静态检查 Jenkins 持续集成 Pipline LDAP gitlab-plugin TestNG 自动化测试 Ansible 自动化运维 Inventory 做好部署目标的管理 对于被 Ansible 管理的机器清单,我们可以通过 Inventory 文件,分组管理其中一些集群的机器列表分组,并为其设置不同变量 PlayBook 编写部署过程的具体逻辑 PlayBook 是 Ansible 的脚本文件,使用 YAML 语言编写,包含需要远程执行的核心命令、定义任务具体内容,等等 PlayBook 复用文件 Ansible Tower 是 Ansible 的中心化管理节点,既提供了 Web 页面以达到可是花能力,也提供了 Rest API 以达到调用 Ansible 的 PlayBook 的目的 灰度发布 通过 Inventory 定义灰度分批策略,再利用 Pipeline 驱动 PlayBook,就是一个典型的灰度发布的处理过程。其实,这只是将原子化的单机操作批量化了而已 Spinnaker 处理 Docker 发布支持多个云平台,比如 AWS EC2、Microsoft Azure、Kubernetes 等 支持集成多个持续集成平台,包括 Jenkins、Travis CI 等 Netflix 是金丝雀发布的早期实践者,Spinnaker 中已经天然集成了蓝绿发布和金丝雀发布这两种发布策略,减少了开发发布系统的工作量 答疑 数据库变更 业务相关表只能新增字段,不能删除字段,也不能修改已有字段的定义,并且新增字段必须有默认值 对于必须要修改原有数据库结构的场景,必须由 DBA 操作,不纳入持续交付流程 持续交付之“痛” 要比架构师懂得多 要比开发人员动作快 要比 OA 团队眼睛尖 要比运营人员“心脏大” 要比产品经理会“吹”

2022/1/2
articleCard.readMore

IntelliJ IDEA 最佳实践 - 精选插件

前言 IntelliJ IDEA 提供了强大的插件系统,并且有着完善的插件生态。 成千上万追求极致的开发者们为 IDEA 开发了不计其数的插件,可以极大程度地帮助开发者提升开发速度、代码质量、以及开发的舒适度等等。 本文收集了最受开发者喜欢的一些插件,罗列至此,希望能够对你有所帮助。 插件的安装和卸载章节被我放在了文章较后的位置,如果你需要查看这些内容,翻看文章后面的 安装 和 卸载 章节即可。 精选插件 Rainbow Brackets 这款插件名为彩虹括号,可以帮助我们更好地匹配括号和定位代码块。 主要功能包括: 使用不同颜色渲染多层嵌套的括号; 通过高亮显示帮助我们更好地定位代码块(ctrl + 鼠标右键单击 触发); 相关资料 izhangzhihao/intellij-rainbow-brackets AceJump AceJump 插件可以让我们在脱离鼠标的情况下在代码编辑器的可视区域快速移动光标位置。 SequenceDiagram SequenceDiagram 是一款代码时序图插件,可以帮助大家更好地梳理代码逻辑。 主要有以下功能: 生成代码时序图; 导出时序图; 导出 PlantUML 文件; 相关资料 Vanco/SequencePlugin SequenceDiagram SonarLint SonarLint 是一款代码质量检查插件,主要功能包括: 帮助我们发现和修复 bug; 检查代码坏味道,并提供修复建议。 SonarLint 更侧重代码质量检查。 Alibaba Java Coding Guidelines 阿里巴巴代码规约插件,可以帮助我们养成良好的代码规范和编程风格。 插件有实时检测和主动检测两种模式,如果该插件导致 IDEA 卡顿,可以关闭实时检测功能,在必要时手动检测修复即可。 Alibaba Java Coding Guidelines 更侧重代码规范检查。 相关资料 alibaba/p3c Alibaba Cloud Toolkit Alibaba Cloud Toolkit 插件允许我们通过一系列的配置来制定代码的一键发布策略。 可以极大得简化代码发布流程。主要功能包括: 管理服务器,快速上传文件、执行命令、连接服务器、查看服务器文件等; 定制一键发布策略,完成一键发布功能; 插件植入了很多阿里云自己的产品,介意的话可以卸载这款插件。 Key Promoter X Key Promoter X 是一款快捷键提示插件,如果你进行了可以使用快捷键但是并没有使用快捷键触发的操作,插件会自动在屏幕右下角提示可以使用哪些快捷键来触发同样的操作。 如果你觉得某个提示对你造成了打扰,也可以单独禁用指定快捷键的提示。 Presentation Assistant Presentation Assistant 是一款快捷键展示插件,适合在直播或者录屏的场景使用。 如果你触发了某一个操作(不管是不是使用快捷键触发的),这款插件都会在屏幕上展示你可以使用哪些快捷键触发同一操作,方便他人复现操作步骤。 Grep Console Grep Console 可以帮助我们更好地处理打印在 IDEA 控制台的日志,主要功能有: 对打印出来的日志信息进行过滤和筛选(支持正则匹配); 自定义不同等级日志的配色方案; 快速定位异常日志位置; Translation Translation 是一款翻译插件,主要功能包括: 直接在 IDEA 中进行中英互译; 直接把中文文字替换为对应的英文变量名; Maven Helper Maven Helper 可以帮助我们在 IDEA 中更好地开发 maven 项目,主要功能包括: 更方便地执行 maven 命令; 分析和排除 Maven 冲突; 自定义 Maven goals; 在当前 Module 所在的路径中打开 Terminal; MybatisX MybatisX 插件可以让我们更方便地使用 Mybatis, 主要功能包括: 关联 Mapper 文件和 xml 文件,并且支持快速跳转; 代码提示; MyBatis Log Plugin Mybatis Log Plugin 可以把 Mybatis 的 log 替换成可执行的 SQL, 帮助我们更快更直观地进行代码调试工作。主要功能包括: 还原 MyBatis 输出的日志为完整的 SQL 语句; 把 SQL 日志里面的 ? 替换为真正的参数值; 相关资料 kookob/mybatis-log-plugin Git Commit Template 这款插件为 IDEA 提供了版本控制工具 commit message 的模板,可以帮助我们规范 commit message 信息。 CodeGlance Code Glance 插件会在编辑器右侧嵌入一个 Sublime 风格的代码缩略图,帮助我们快速定位代码。 如果你能熟练地使用快捷键,这款插件就没有多大的优势了。 IDEA Mind Map IDEA Mind Map 插件允许我们在 IDEA 中绘制思维导图。 安装 在线安装 进入 IDEA 设置页面; 点击 Plugins 进入插件设置面板; 选择 “Marketplace”; 在输入框填写自己想要安装的插件名称并回车搜索; 在搜索结果中选中自己想要安装的具体插件; 点击页面右侧插件详情页中的 “Install” 即可安装成功; 安装完成之后,需要重启 IDEA 才能使插件生效。 离线安装 首先在 JetBrains 插件市场 搜索并下载自己想要的插件; 打开 IDEA,进入 插件设置 页面; 点击 “Installed” 选项卡右侧的 “齿轮图标”,然后选择 “Install Plugins from Disk…“; 选择自己刚才下载好的插件,确认安装即可; 安装完成之后,需要重启 IDEA 才能使插件生效。 卸载 进入 IDEA 设置页面; 点击 Plugins 进入插件设置面板; 选择 “Installed” 进入已安装插件列表页; 在输入框填写自己想要安装的插件名称并回车搜索; 在搜索结果中选中自己想要卸载的具体插件; 点击页面右侧插件详情页中的 “齿轮图标”, 然后选择 “**Uninstall…**” 即可卸载成功; 卸载完成之后,需要重启 IDEA 才能使操作生效。 后记 最近花了一周时间系统整理了 Intellij IDEA 的最佳实践,内容涵盖了 基础配置、原生能力和精选插件几个模块。 地址为: **idea.diqigan.cn**,相信会对你有所帮助。 如果你有更好的插件推荐,欢迎参与文档编写,或者直接与我联系。

2021/8/3
articleCard.readMore

Intellij IDEA 最佳实践-注释模板

IDEA 的设置项中提供了 Live Templates 和 File and Code Templates 的设置。其中: Live Templates 用于配置方法注释模板和代码模板,这类模板需要在写代码的过程中通过关键字手动触发; File and Code Templates 用于配置文件模板,新建文件时 IDEA 会自动填入我们配置的模板内容。常用于配置类注释、接口注释等。 Live Templates 借助 IDEA 的 Live Templates 功能,我们可以实现代码模板和注释模板的配置。 Live Templates 设置页面的路径为:“File” -> “Settings” -> “Editor” -> “Live Templates”。 模板组 IDEA 的 Live Template 中有模板组(Template Group)和模板(Live Template)两个概念。Live Template 模板以分组的形式存在于 Template Group 之下。 新建模板组的步骤非常简单: 点击 Live Templates 面板右方的 + 号并选择 “Template Group” ; 在弹出的输入框中输入模板组名称,点击 ”OK” 确认; 方法注释模板 在 Live Templates 面板选择自己刚刚新建的模板组; 点击面板右侧的 + 号并选择 “Live Template” 新建模板; 填写方法模板信息: 在 ”Abbreviation” 输入框写入 m,这里是模板的简写,也可以叫做触发模板的关键字; 在 “Description” 输入框写入 方法注释,这里写入的是模板描述,方便我们自己确认模板功能; 在 ”Template text” 文本框写入以下模板: 1 2 3 4 5 6 ** * $description$ * $params$ $returns$ * @author $user$ * @date $date$ $time$ */ 可以看到,我们在模板中定义了许多变量。 点击 “Edit variables” 并填写变量表达式: 在 ”params” 后的 “Expression” 输入框填入以下内容并勾选 ”Skip if defined“ 复选框: 1 2 3 4 5 6 7 8 9 10 11 12 13 groovyScript( "def params=\"${_1}\".replaceAll('[\\\\[|\\\\]|\\\\s]', '').split(',').toList(); def result=''; for(i = 0; i < params.size(); i++) { if (params[i] == '') { continue; }; result += '\\n * @param ' + params[i]; }; return result == '' ? '' : result", methodParameters() ) 这段 groovy 脚本的作用是生成参数注释。 勾选 ”Skip if defined“ 复选框的作用是如果表达式已经为对应变量生成了值,那么就跳过手动输入步骤。 在 ”returns” 后的 “Expression” 输入框填入以下内容并勾选 ”Skip if defined“ 复选框: 1 2 3 4 5 groovyScript( "def returnType = \"${_1}\"; return returnType == 'void' ? '' : '\\n* @return ' + returnType + ''", methodReturnType() ) 这段 groovy 脚本的作用是生成i返回值注释。 在 ”user” 后的 “Expression” 输入框填入 user() 并勾选 ”Skip if defined“ 复选框; 这里的 user() 是 IDEA 内置的方法,取当前操作系统登录用户的账号。此处也可以为固定字符串,取值为自己想要展示在方法注释中的名字。 在 ”date” 后的 “Expression” 输入框填入 date("yyyy-MM-dd") 并勾选 ”Skip if defined“ 复选框; 在 ”time” 后的 “Expression” 输入框填入 time("HH:mm:ss") 并勾选 ”Skip if defined“ 复选框; 点击 ”OK“ 确认变量设置; 配置可选项: 把页面上方 ”By default expand with” 和页面右下角 “Options” 栏中的 ”Expand with” 设置为 “Tab”; Live Template 的触发方式为 关键词 ”Abbreviation” + “Expand with”。 按照我们上面的设置,如果想插入方法注释,只需要在方法上面一行输入 m 然后下 Tab 键,IDEA 就会把我们设置好的 “Template text” 插入到代码中。 勾选页面右下角的 ”Reformat according to style”, IDEA 会在我们插入代码模板后自动根据当前设置的代码风格将其格式化; 勾选页面右下角的 “Shorten FQ names”,IDEA 会自动截断完整标识符并导入对应语句; 配置适用范围: 点击页面左下角的 “Define” / “Change” 并在弹出面板中选择 ”Java”,表示此模板只会在 Java 文件中生效。 至此,配置完成。 看下效果: 这里之所以在关键字 m 前加了斜线 / 是因为我在模板文字中没有写注释的斜线 /。添加 / 之后,注释才能完整。 举一反三 属性/成员变量注释模板 属性 / 成员变量注释模板如图所示,此处不再赘述。 构造器注释模板 构造器注释模板相对于方法注释模板只是删除返回值: SpringBootTest 注解模板 SpringBootTest 注解模板如图所示: 原生模板 IDEA 也预置了许多代码模板,以 Java 为例: sout 可以生成 System.out.println() 语句; fori 可以生成 for 循环; 此处不一一列举,感兴趣的话还请自行查看。 File and Code Templates File and Code Templates 用于配置文件模板,新建文件时 IDEA 会自动填入我们配置的模板内容。常用于配置类注释、接口注释等。 Files 这里可以对文件和代码模板进行配置,其中: “Description” 栏罗列了 IDEA 预置的变量,可以自行选择组合使用。 Class Class 可以对 Java 的类模板进行配置: 在模板输入框填入以下内容: 1 2 3 4 5 6 7 8 9 10 11 #if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end #parse("File Header.java") /** * ${NAME} * * @author ${USER} * @date ${YEAR}-${MONTH}-${DAY} ${HOUR}:${MINUTE} */ public class ${NAME} { } 之后新建的 Java Class 文件会自动填充携带有 @author 和 @data 的 Java Doc 注释: Interface Interface 配置页可以对 Java 的接口模板进行配置。 在模板输入框填入以下内容: 1 2 3 4 5 6 7 8 9 10 11 #if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end #parse("File Header.java") /** * ${NAME} * * @author ${USER} * @date ${YEAR}-${MONTH}-${DAY} ${HOUR}:${MINUTE} */ public interface ${NAME} { } 之后新建的 Java Interface 文件会自动填充携带有 @author 和 @data 的 Java Doc 注释: 题外话 最近花了一周时间系统整理了 Intellij IDEA 的最佳实践,内容涵盖了 基础配置、原生能力和精选插件几个模块。 地址为: idea.diqigan.cn,相信会对你有所帮助。

2021/8/2
articleCard.readMore

使用 Intellij IDEA 创建 maven 多模块项目

前言 之前接触过的项目大都是单体应用,各应用之间有功能重合但无代码共用。同样的功能,要么重复开发,要么代码拷贝,开发繁琐且效率低下。一但在多个应用中均有使用的相同的代码出现了 bug,开发人员更是要在多个应用中逐个修复,劳神伤身。 故引入模块化开发概念,把相对独立的功能抽离成组件开发,完成之后再组合到一起服务业务。一来开发有了积淀,新项目不必从零开发;二来 bug 的修复也会简单很多,只需修复一次,升级版本即可。提高了工作效率,才会有更多的时间去做技术研究和价值创造。 简单查询了使用 Intellij IDEA 进行 maven 多模块项目的创建过程,只是模块化开发中很小的一部分,记录于此,用以备忘。 操作过程 创建父模块 在 IDEA 中选择 “new project”: 直接点击 “Next”: 填写项目信息,然后点击 “Finish”: 修改 pom.xml 文件: 在 project 标签下添加 packaging 标签,取值为 pom; 在 project 标签下添加 version 标签,取值为 ${revision}; 在 properties 标签下添加 revision 标签,取值为项目版本号。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.seven.module</groupId> <artifactId>common</artifactId> <packaging>pom</packaging> <version>${revision}</version> <properties> <revision>1.0-SNAPSHOT</revision> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <build> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>flatten-maven-plugin</artifactId> <version>${flatten-maven-plugin.version}</version> <configuration> <updatePomFile>true</updatePomFile> <flattenMode>resolveCiFriendliesOnly</flattenMode> </configuration> <executions> <execution> <id>flatten</id> <phase>process-resources</phase> <goals> <goal>flatten</goal> </goals> </execution> <execution> <id>flatten.clean</id> <phase>clean</phase> <goals> <goal>clean</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project> 删除父模块的 “src” 目录。 至此,父模块创建完成。 创建子模块 鼠标右键项目名,然后依次选择 ”New” -> “Module”: 点击 “Next” 去往下一步: 填写模块信息并点击 “Finish”: 修改 pom.xml 文件: 为了保持父子模块版本号一致,我们需要修改如下配置: 将 project 标签下的 parent 标签下的 version 取值修改为 ${version}; 在 project 标签下添加 version 标签,取值为 ${revision}; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>common</artifactId> <groupId>com.seven.module</groupId> <version>${revision}</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>util</artifactId> <version>${revision}</version> </project> 至此,子模块创建完成,直接开始模块开发即可。 参考文献 Maven多模块结构下版本管理的正确姿势(CI Friendly Versions) - ${revision} — 空无

2021/7/3
articleCard.readMore

通过代理连接 ssh 服务器

前言 碍于各种复杂的网络环境,有些情况下我们并不能直接访问 SSH 服务器。 这时,网络代理和 SSH 隧道就成了救命稻草。 本文记录了通过网络代理和 SSH 隧道连接远程服务器的几种方法,希望对你有所帮助。 稻草堆 使用 SSH 隧道连接服务器 常用于跳板机场景。 1 ssh ${ssh-user}@${ssh-host} -o ProxyCommand="ssh ${jump-host-user}@${jump-host} -p ${jump-host-port} -W %h:%p" SSH + ncat 使用 HTTP(s) 代理连接服务器 安装 ncat 1 sudo apt install ncat 通过代理连接远程服务器 1 ssh ${ssh-user}@${ssh-host} -o ProxyCommand="ncat --proxy ${proxy-ip}:${proxy-port} --proxy-type http --proxy-auth ${proxy-account}:${proxy-password} %h %p" SSH + ncat 使用 Socks 代理连接服务器 安装 ncat 1 sudo apt install ncat 通过代理连接远程服务器 1 ssh ${ssh-user}@${ssh-host} -o ProxyCommand="ncat --proxy ${proxy-ip}:${proxy-port} --proxy-type socks5 --proxy-auth ${proxy-account}:${proxy-password} %h %p" SSH + corkscrew 使用 HTTP(s) 代理连接服务器 安装 corkscrew 1 sudo apt install corkscrew 通过代理连接远程服务器 1 ssh ${ssh-user}@${ssh-host} -o "ProxyCommand corkscrew ${proxy-ip} ${proxy-port} %h %p [${auth-file-path}]" 上述命令中 [] 包裹的部分代表可选参数。 如果 HTTP 代理设有权限校验,需要将 HTTP 代理的账号密码以 account:password 的格式写入到文件中,然后在上述指令中指定 [${auth-file-path}] 即可。 SSH + nc 使用无身份校验 Socks 代理连接服务器 nc 工具的优点是多数服务器自带,无需额外安装;缺点是不支持有身份校验的 socks 代理。 1 ssh ${user}@${host} -o ProxyCommand="nc -X 5 -x ${proxy-ip}:${proxy-port} %h %p" 持久化配置 我们可以通过编辑 ~/.ssh/config 文件把 SSH 连接配置持久化,配置内容如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 Host ${ssh-host-alias} HostName ${ssh-host-ip} Port ${ssh-port} User ${ssh-user} ProxyCommand ${proxy-command} Host * ServerAliveCountMax 3 ServerAliveInterval 30 ExitOnForwardFailure yes Compression yes GSSAPIAuthentication no ControlMaster auto ControlPersist 4h 配置完成之后,就可以通过 ssh ${ssh-host-alias} 命令连接服务器啦。 后记 上述命令都可以通过灵活多变的指令来实现不同的效果,还请各位看官自行尝试,此处不再赘述。 SSH 功能非常强大,包括但不限于连接远程服务器、架设 socks 代理、文件传输、远程执行命令等等等等,建议深挖。 SSH config 也非常强大,适当的配置可以帮助我们很好得进行多服务器管理(甩 XShell 几条街)。 参考文献 让你的SSH通过HTTP代理或者SOCKS5代理 — 神田长雨

2021/6/8
articleCard.readMore

Java 清空控制台输出

前言 本文通过两种方法讲述了 Java 如何清空控制台输出,达到类似 Linux 中 top 命令的效果。 两种方法均在 Linux 环境中测试通过,Windows 环境请自行测试,理论可行。 下列代码需要在控制台执行才会有清除控制台效果,在 IDE 中执行无效。 使用 ASCI 控制码清空控制台 通过输出 ASCI 控制码清空控制台,代码如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 /** * ConsoleClear * * @author seven * @date 2021-04-14 21:49 */ public class ConsoleClear { public static void main(String[] args) { for (int i = 0; i < 100; i++) { System.out.println(i); } // 清空控制台并把光标停留在起始位置 System.out.print("\033[H\033[2J"); // 效果同上 // System.out.print("\033[0;0H\033[2J"); System.out.fulsh(); } } 另外,ASCI 控制码还可以实现前景色、背景色、下划线、消隐、闪烁效果的设置以及光标控制,具体方法请阅读参考文档。 通过 ProcessBuilder 清空控制台 ProcessBuilder 本质上是调用了系统命令来达到清空控制台的效果,代码如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 import java.io.IOException; /** * ConsoleClear * * @author seven * @date 2021-04-14 21:49 */ public class ConsoleClear { public static void main(String[] args) { for (int i = 0; i < 100; i++) { System.out.println(i); } // 清空控制台 try { final String os = System.getProperty("os.name"); // 根据不同环境执行不同命令 if (os.contains("Windows")) { // 新建一个 ProcessBuilder,Windows 下要执行的命令是 cmd.exe,参数是 /c 和 cls new ProcessBuilder("cmd", "/c", "cls") //将 ProcessBuilder 对象的输出管道和 Java 的进程进行关联,这个函数的返回值也是一个 ProcessBuilder .inheritIO() //开始执行 ProcessBuilder 中的命令 .start() //等待 ProcessBuilder 中的清屏命令执行完毕 //如果不等待则会出现清屏代码后面的输出被清掉的情况 .waitFor(); } else { // 新建一个 ProcessBuilder,Linux 下要执行的命令是 clear new ProcessBuilder("clear") //将 ProcessBuilder 对象的输出管道和 Java 的进程进行关联,这个函数的返回值也是一个 ProcessBuilder .inheritIO() //开始执行 ProcessBuilder 中的命令 .start() //等待 ProcessBuilder 中的清屏命令执行完毕 //如果不等待则会出现清屏代码后面的输出被清掉的情况 .waitFor(); } } catch (final IOException | InterruptedException e) { System.out.println("Clear Console Failed."); } } } 可以将 ProcessBuilder 部分代码封装一下,这里为了便于理解,直接写了两次。 不存在的方法三 网上还盛传另一种方法,核心代码是: 1 Runtime.getRuntime().exec("cls"); 我自己亲测无效,各位看官感兴趣的话还请自行踩坑 / 避坑。 参考文档 终端/控制台设置颜色字体、光标定位和清屏 — 印林泉 java 控制台输出 颜色代码 — 夜半听风吟 java 在Cmd命令行实现清屏 — 蓝蓝223

2021/4/14
articleCard.readMore

红米 K40 Pro 安装 Magisk(非 TWRP)

前言 这篇文章记录了 RedMi K40 Pro 手机非 TWRP 方式刷入 Magisk 的过程、OTA 升级的注意事项和一些常用 Magisk 模块推荐。希望能够对你有所帮助。 安装 Magisk 解锁 BL,参照 官网教程 操作即可。 找到与当前手机 MIUI 版本相同 的卡刷包或者线刷包。 最保险的方式是把当前手机系统更新到最新,然后打开 “设置” - “我的设备” - “MIUI 版本” - “右上角三个点” - “下载最新完整包”。下载完成之后可以在手机存储/Download/downloaded_rom 文件夹下找到完整的 OTA 升级包。 (不要在意三张图版本不一样,后期补图没办法,见谅) 如果通过上述方法无法获取刷机包,可以从以下几个途径查找,但请务必保证拿到的刷机包和手机当前系统版本一致!!! 小米官网稳定版 ROM 下载地址 部分新机型可能暂未更新 MIUI OTA 升级包 全部是官方升级包,但是更新可能会稍微迟一点 第三方的小米官方 ROM 收集网站 可以找到完整线刷包,救砖用 从刷机包中提取 boot.img 文件 把步骤2中拿到的刷机包拷贝到电脑并解压,会得到一个 payload.bin 的文件。如果你有 Python 基础,可以使用 vm03/payload_dumper 解压 payload.bin。如果没有,也可以使用别人打包好的 payload_dumper.exe 提取。下载地址:payload_dumper.exe,密码:8h2f。 这里以 payload_dump.exe 为例进行讲解,下载文件解压后会得到如下目录结构: 将 payload.bin 文件放到 payload_input 文件夹下,运行 payload_dumper.exe 程序。程序执行完毕后,可以在 payload_output 文件夹下找到 boot.img 文件,把 boot.img 文件拷贝到手机。 给 boot.img 文件打补丁 从 Magisk Release 下载最新的 Magisk.apk 安装包,并且安装到手机。 打开 Magisk APP,点击 “安装” - “选择并修补一个文件”,选中我们拷贝回来的 boot.img 文件,点击 “开始” 即可。 打补丁完成之后会生成一个 magisk_patched_XXX.img 的补丁包,记住这个文件的路径。 刷入 Magisk 这里需要使用 adb 工具进行刷机。adb 工具下载地址:adb 工具,密码:fpky。 下载并解压 adb 工具。 把第 4 步拿到的 magisk_pached_XXX.img 补丁包拷贝到 adb 工具目录。 会得到如下的目录结构: 重启手机并长按 **音量-**,直到手机进入 fastboot 模式。 双击 “打开CMD命令行.bat”, 执行命令 fastboot devices,出现设备列表时,执行刷机命令: 1 2 fastboot flash boot_a magisk_patched_XXX.img fastboot flash boot_b magisk_pached_XXX.img 这里需要将 “magisk_patched_XXX.img” 替换为你本机的真实文件名。 如果上述两天命令执行成功,输入 fastboot reboot 重启手机即可。 至此,如无意外,magisk 已经安装成功。 OTA 升级 系统 OTA 升级可能会把 magisk 洗掉,但是 A/B 分区的手机可以避免这个问题,可以在 Magisk 首页查看自己的手机是不是 A/B 分区。 在手机系统更新页面正常更新系统,但是在重启手机之前,打开 Magisk APP,点击 “安装” - “安装到未使用的槽位(OTA后)” - “开始”,安装完成后,点击右下角的 “重启” 即可。 Magisk 模块推荐 Riru(Riru - Core) Riru - EdXposed Taichi 太极和 Riru 二选一即可。 Wifi Bonding (Qcom) 加快 WIFI 速率。 QTI memory optimization 内存优化 Uperf CPU 调度 Ainur Sauron & Narsil 刷完即用提升音质 Sytemless hosts & EnergizedProtection 去广告 Xposed 模块推荐 lataclysm 强大的虚拟定位模块 应用变量 系统参数伪装 参考文档 红米K40刷面具(root)的教程 — weixin_47166516 红米 K40 Pro+ (haydn) 国行版 Fastboot 线刷包 刷成砖的话可能会用到这个线刷包 MIUI OTA 升级包 收藏备用 vm03/payload_dumper 从卡刷包 payload.bin 中解析 boot.img 文件的工具 topjohnwu/Magisk Magisk 开源仓库 minimal-adb-fastboot adb 工具 Magisk各版本国内更新源与下载源

2021/4/3
articleCard.readMore

Hexo + Typora + PicGo-Core 写作三件套

前言 使用 Hexo 写博客已经很久了,各方面体验还算顺滑,唯一感觉不是那么爽的地方在于图床的使用。 如果在写作的过程中需要插入图片,我需要先中断写作,打开图床 web 页面,上传图片,复制图片链接,再回到编辑器粘贴图片链接。整个过程极其繁琐,令人苦不堪言。直到我遇到了 PicGo-Core。 PicGo-Core 可以和 Typora 无缝连接,在几乎无感的情况下完成图片的上传与 markdown 格式的转换,极大得提高了写作的流畅程度。 本文将结合我的自身实践,讲述 Hexo + Typora + PicGo-Core 的联合配置过程,希望能够对你有所帮助。 文章适合已经把玩过 Hexo 或者对 Node 有所了解的用户理解,如果您没有接触过对应知识,还请自行补充。 文中 “Hexo + Typora 联动” 章节与 “Typora + PicGo-Core 联动” 章节并无关联,可以只阅读自己感兴趣的部分。 环境说明 本文讲述的所有操作均在以下环境中完美运行,其他环境大同小异,烦请看官自行钻研。 操作系统:Ubuntu 20.04 node:v12.16.3 yarn:1.22.5 hexo: ^5.0.0 Typora:version 0.9.98(beta) for Linux PicGo-Core: 1.4.12 Hexo + Typora 联动 默认情况下 hexo new ${post} 命令只会创建一个名为 ${post}.md 的 markdown 文件,我们需要自行使用编辑器打开这个文件,然后才可以进行编辑写作。Hexo 提供的 Scripts 能力可以合并这两个步骤,在创建文件之后自行调用编辑器打开文件供我们编辑。 操作步骤非常简单,只需要在 Hexo 博客根目录的 “scripts” 文件夹下新建一个 editArticle.js 文件并写入如下内容即可: 1 2 3 4 5 // 新建文档之后用 typora 打开文件 var spawn = require('child_process').spawn; hexo.on('new', function(data){ spawn('${path-to-editor}', [data.path]); }); 其中: ${path-to-editor} 代表编辑器可执行文件的位置,可以根据实际情况更改,比如我这里写的是 /usr/bin/typora; Typora + PicGo-Core 联动 安装 Typora 直接在 Typora 官网下载安装即可。 安装 PicGo-Core Typora 支持一键安装 PicGo-Core 和一键打开 PicGo-Core 的配置文件,位置如图: 如果你喜欢手动安装 | 参考文档,执行以下命令即可: 1 yarn global add picgo 配置 PicGo-Core | 参考文档 PicGo-Core 官方支持 sm.ms,七牛,腾讯云 COS,阿里云 OSS,Github 图床,又拍云,imgur 等图床。选择自己喜欢的图床并按照文档进行配置即可。 如果你使用的图床不在 PicGo-Core 支持列表中,也可以使用插件 picgo-plugin-web-uploader 来实现自定义图床。 安装插件 | 参考文档 1 picgo install web-uploader 配置插件 | 参考文档 编辑 PicGo-Core 的配置文件,写入以下内容: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 { "picBed": { "current": "web-uploader", "web-uploader": { "customBody": null, "customHeader": null, "url": "https://www.ladydaily.com/tools/upload/${token}", "paramName": "file", "jsonPath": "data.o_url" } }, "picgoPlugins": { "picgo-plugin-web-uploader": true } } 我这里使用的是 多吉图床,${token} 表示多吉图床的 token,其他图床大同小异,自行配置即可。 设置 Typora 在 Typora 中依次点击 “文件” - “偏好设置” - “图像”: 在 “插入图片时” 设置中选择 “上传图片”,并勾选 “对本地位置的图片应用上述规则”,“对网络位置的图片应用上述规则”,“允许根据 YAML 设置自动上传图片”,“插入时自动转义图片 URL” 等选项; 在 “上传服务” 设置中选择 “PicGo-Core (command line)” ; 然后点击 “验证图片上传选项” 按钮,出现 “验证成功” 提示即可。 后记 至此,写作三件套的联动配置已经全部完成。如果您有更好的想法或者实践,欢迎与我交流,万分感谢! One More Thing 如果你也在从事微信公众号写作,相信你会喜欢这个可以帮你排版的工具:MarkDown Nice。 参考文档 PicGo-Core 官方文档 Typora配合上传插件picgo自动上传图床chevereto — umr

2020/12/20
articleCard.readMore

月刊-202012

优秀文章 和软件开发相关的一些思考 deadline 带来的生产力一定程度上是以牺牲质量为代价的; 在软件开发或者是学习过程中,需要不断地根据实际情况去调整计划。不要一刀切得使用固定的 ddl; 大多数开发者都会高估自己的开发速度,低估软件工程的复杂程度; 单一技术不能解决所有问题,争论某种技术最好并没有实际意义。关键是什么工具和技术更适合解决你现在的问题; 技术总归是要为人服务的,产品的出发点应该是“人”。“人”需要什么功能,什么技术更适合完成这个功能; 多和人沟通,了解不同人的想法、不同人思维的差异,可以让我们更容易地去接受这个世界存在着的不同,让我们更谦虚。 给你一份时间管理秘籍 无事心不空,有事心不乱,大事心不畏,小事心不慢。 专注可以解决世上绝大多数问题,剩下的需要一点运气。 合理合法地多赚钱 关于金钱,说到底也是一种资源,这种资源和其他的资源没有太大区别,资源越多,你的自由度越大。 说 6 点,关于高效学习 这是个意外 生活没有起承转合。 影响王兴的一本书 有限游戏在边界内玩,无限游戏却是在和边界,也就是和 “规则” 玩,探索改变边界本身。 有限游戏是有明确的边界和目的,参与者的目的就是为了获取最后的胜利,结束游戏。 无限游戏是持久的,创新的,建设性的,充满奇迹的,参与者的目标是为了继续玩这个游戏,无限博弈是一种开放的心态。 无限是一个过程,有限是个瞬间。 怎么才走到这一步的呢? 面对复杂性、不确定性,计划是无法完美的,这是极限。但不断制定计划,并根据反馈修改计划,正是价值的来源。在这个过程中,锻炼出估算能力、对风险的敏感度、收集信息的方法论、控制平衡的艺术、对业务本质的思考、对因果逻辑的理解,才是胜出的关键。 工具 奶牛快传 不限速文件传输工具。 推荐22款终端生产力工具,效率快到飞起! 一个涵盖了多款优秀终端工具的清单。 DBeaver 开源跨平台的数据库管理工具,支持多种数据库。 开发过程中必不可少的5个开源工具 介绍了几种 Linux 下比较好用的工具。 screenity 一款开源免费的 Chrome 录屏插件。 自选基金助手 可以实时查看基金走势的 Chrome 浏览器插件。 PaddleHub 无需深度学习背景、无需数据与训练过程的 AI 框架。 技术点 听说你的资源被盗用了,那你知道 Nginx 怎么防盗链吗? 解决Alpine缺少字体的问题 java 后台实现 WebSocket 客户端: Spring-Boot快速集成WebSocket服务端 客户端(客户端消息同步回调) SpringBoot+WebSocket实现服务端、客户端 Jenkins export and import jobs 迁移导出导入任务实践小结 Jenkins 配置秘钥时报错的原因以及解决方案 看世界 Tiffany色的集装箱 一位选择在山上住集装箱房的姐姐,记录了自己的生活点滴。 Kaspersky Cyberthreat Map 卡巴斯基做的全球网络攻击图,3D Map 做得很好。 你脖子痛不痛? 很多时候我们会认为意外永不来临,但改变总是突如其来,命运拿走你曾经拥有的东西,不会和你打一声招呼。 Google 搜索成最大入口,简单谈下个人博客的 SEO 个人博客 SEO 简介。

2020/12/9
articleCard.readMore

月刊-202009

优秀文章 不行 得不到就是得不到,别说自己不想要。 不学就是不学,别说自己没时间。 别给年轻人提建议 每个人最终都将成为自己。 如何理解所谓的压力与红利 不要稀里糊涂的随波逐流,红利到来的时候,要知道红利是从哪里来的,怎么来的,这样才能更好的把握机会,争取实现自我突破和提升。挤压到来的时候,要知道挤压是怎么来的,这样才能未雨绸缪,更好的规避,更好的让自己处于相对安全和健康的位置。 学会真正的理解因果,理解逻辑,理解世界。 多读一点经济学的书,多理解相关经济学的逻辑,多关注政策面的新闻,更好的认识这个世界,认识自己所处的环境,不要简单归因,不要只看到表面,你会自己找到答案和真正的因果。 如果你不知道如何把握红利,不知道如何规避压力,唯一能做的,是不断提升自己的职场竞争力,提升自己的信用,口碑和影响力。永远不要认为,做对一次选择,就可以安稳一生。 快速学习秘诀:费曼学习法 确定目标、模拟教学、反复理解、复习简化。 如果高效学习有什么秘诀的话,那就都在这里了:) IT 领域学习的一些心得与建议,可作参考。 鸡毛蒜皮的事儿 我们是不是经常在“鸡毛蒜皮的小事”上面浪费了很多时间呢? 千万并发,阿里淘宝的 14 次架构演进之路! 文章以淘宝为例,介绍从一百个并发到千万级并发情况下服务端的架构的演进过程,同时列举出每个演进阶段会遇到的相关技术,可以让我们对架构的演进有一个整体的认知。 资源推荐 iptables 系列教程 这是我我见过的最好的 iptables 教程。 行文诙谐、通俗易懂、深入浅出、不失全面。 Github上 10 个开源免费且优秀的后台控制面板 一些优秀的后台前端解决方案。 FinalShell 一款小众但相对优雅的跨平台命令行终端工具,集成了 shell 和 ftp,有些许瑕疵,但让人眼前一亮。 面包多 面包多帮助创作者轻松获得收入。 看世界 9 块 9 包邮,商家真的赚钱吗? 看一下 9 块 9 包邮的商业逻辑。 我的一年独立开发经历 这是最好的时代: 人人都可以低成本的创造 这是最坏的时代: 人人都可以低成本的创造 让赚钱思路更加开阔的 7 个小建议 即时正反馈、利他、不设限、高质信息管道、商业思维小闭环、异常值思维、组合思维。 为什么你越攒钱越穷? 从投资的角度看负债。 年度种草!我的2019桌面工具清单! 不上班后,我成了月入2万的“废人” “自立门户”并非工作自由。

2020/9/1
articleCard.readMore

monthly-202005

优秀文章 去一个自带流量的团队真的不一样 No.189 没有谁的成功是一蹴而就的。以前我总是纳闷儿,为什么别人就可以这么厉害?看完这篇文章,我有了些许答案。 巨头过处,寸草不生 在某个成熟领域,做得比别人好一点点,UI 巧妙一点,是没用的。得好一百倍才有可能。 任何事物当中的百分之九十都是垃圾? 在摄入信息时,我们要像对待食物和药物一样挑剔、严格、谨慎。 看世界 连续一个月不吃早饭,会怎么样? 吃早餐和不吃早餐对身体到底有什么影响? 一文看懂,流量背后的逻辑 你要记住,信息是有信息差的,并不是所有人都能找到最有效的信息,特别是核心信息,谁离核心源头越近,谁就越赚钱。 一文看懂,赚钱的本质 赚钱的核心:产品,流量,变现 做生意不要看到表层现象,而是要透过现象本质。 你的痛点价值千金,你所有感到不爽的、痛苦的、难受的点,都有同样一批人与你感同身受。 生意的本质其实就四个字:低买高卖 当我们在谈理财的时候我们在谈什么? 关于理财入门的一些科普。 所谓“无用之学” 省自身 你工作开心吗? 自省和内观对一个人的成长非常重要。 你接受和学习的新鲜事物越多,你在工作中面对问题时的应对能力也就越强。 适当的激励可以让自己更有自信,也能帮助自己找到工作的意义和价值。 不断充电。 给自己留点时间。 先做成一件事,是打开世界的钥匙 空想千回,不如践行十遍;践行十遍,不如成事一件。 成事,是一种能力、一种动力、一种奖励。 字节跳动:高级人才的五个基本素质 仇恨只会蒙蔽你的双眼 人可以卑微如尘土,不要扭曲如蛆虫。 弱小和无知不是生存的障碍,傲慢才是。 工具 皮皮直连 安全便捷的电脑/手机互连,点对点文件传送、云剪贴板、屏幕共享、可视化交流工具。 资源 15个素材下载网站,从此不做「伸手党」! 花了一个星期,整理了80个懒人模板网站。

2020/5/30
articleCard.readMore

Ubuntu 20.04 装机手册

前言 期待已久的 Ubuntu 20.04 终于发布了,对其朝思暮想的我当然要前来尝试一番。 这篇文章是我从 16.04 至今一直在使用的装机手册,致力于让 Ubuntu 使用起来更加顺手。整理至此,供君采撷。 附装机图一张: 优化 设置 root 用户密码 在 Terminal 下输入 sudo passwd root 输入当前用户密码,回车 输入新密码,回车。这个密码就是 root 用户的密码。 设置使用 sudo 时免输密码 每次使用 sudo 时都需要输入密码确实烦人,毕竟是私人电脑,安全性有锁屏密码保护就可以了,为了使用方便,不妨取消“使用 sudo 时需要输入 root 用户密码”的设定: 同时按下 ctrl + alt + t 打开终端,输入 sudo visudo ,在打开的文件中,将 1 %sudo ALL=(ALL:ALL) ALL 改为: 1 %sudo ALL=(ALL:ALL) NOPASSWD:ALL 即可。 安装 apt-fast apt-fast 是一个为 apt-get 和 aptitude 做的“ shell脚本封装 ”,通过使用 ar2ac 并发能力来减少 APT 的下载时间。apt-fast 支持诸如 install,remove,update,upgrade,dist-upgrade 等几乎所有 apt-get 的功能,并且支持 proxy。 直白点说,apt-fast 就是一个多线程的 apt-get ,对于我们通过 apt-get 安装软件时尤其有用。 安装命令: 1 2 3 sudo add-apt-repository ppa:apt-fast/stable sudo apt-get update sudo apt-get -y install apt-fast 使用时,将对应命令中的 apt-get 替换为 apt-fast 即可。享受多线程飞一般的速度吧! 更改软件源 按 win 键召唤 bash 栏,搜索 update 并在搜索结果中打开 “软件更新器”,软件打开时会自动检查更新,点 “停止” 即可。 选择 “设置” - “Ubuntu 软件” ,在 “下载自” 列表中选择“其他站点”,然后点击弹窗中的“选择最佳服务器”,系统会自动选择一个相对较快的软件源。 更新软件 1 2 sudo apt update sudo apt upgrade 安装 Chrome 浏览器 Ubuntu 自带的是 Firefox 浏览器,不过我个人更喜欢 Chrome,直接下载 Chrome 安装包 Download | Chrome, 然后使用命令 sudo dpkg -i xxx.deb 安装即可。 中文输入法 搜狗输入法暂时还不支持 Ubuntu 20.04,我们可以使用 IBUS 的智能拼音输入法代替。 打开 “设置” - “区域与语言”,点击 “输入源” 下的 + 号,在 “添加输入源” 弹窗中选择 “汉语” - “智能拼音” 即可。 安装 v2rayL | Github v2ray 的一个 linux 客户端,自我感觉还是比较好用的。安装命令: 1 bash <(curl -s -L http://dl.thinker.ink/install.sh) uTools uTools是一个极简、插件化、跨平台的现代桌面软件。通过自由选配丰富的插件,打造你得心应手的工具集合。 下载地址:下载-uTools 安装 Linux 终端代理工具 proxychains4 有些时候我们需要在终端使用代理,proxychains4 可以很好地帮我们解决这个问题。 在终端输入以下命令安装: 1 sudo apt install proxychains4 修改 proxychains4 配置: 在终端输入命令: 1 sudo vim /etc/proxychains4.conf 打开配置文件,按照自己使用的代理方式配置即可。 使用方法: 在需要代理的命令前加上 proxychains4 即可,如: 1 proxychains4 curl ip.gs 安装 zsh / Vim / Git 1 sudo apt install zsh vim git oh-my-zsh oh-my-zsh 是 zsh 的一个超赞的配置文件,可以让终端更好看更好用。 安装了 zsh 的前提下,在终端运行以下命令即可安装: 1 sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)" 如果使用了 “agnoster” 主题,还需额外安装 powerline 字体: 1 sudo apt install fonts-powerline Vim 配置 主要是自己懒得配置 vim,所以安装别人配好的嘻嘻嘻… spf13-vim 安装命令: 1 curl http://j.mp/spf13-vim3 -L -o - | sh Git 优化 如果 git log 等命令中中文显示乱码,可以尝试设置 git config --global core.quotepath false 修复。 另外可以使用以下命令美化 git log : 1 git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit" 设置之后运行 git lg ,即可体验更好的 git log 效果。 隐藏 grub 引导菜单 1 sudo vim /etc/default/grub 修改内容为: 1 2 3 4 5 6 7 8 GRUB_DEFAULT=0 GRUB_HIDDEN_TIMEOUT=0 GRUB_HIDDEN_TIMEOUT_QUIET=true GRUB_TIMEOUT=0 GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian` GRUB_CMDLINE_LINUX_DEFAULT="quiet splash" GRUB_CMDLINE_LINUX="" GRUB_DISABLE_OS_PROBER=true 更新 grub: 1 sudo update-grub 之后开机就不会再出现 grub 引导界面啦。 安装 aria2 | GitHub 安装命令: 1 sudo apt-get install aria2 配置文件参考 fsaimon / aria2.conf. 图形界面可以使用: Uget chrome 扩展 YAAW for Chrome z.lua 快速路径切换工具。 一款快速切换工作目录的工具,可以根据 Github 的说明文档安装使用。 扩展 Gnome Shell Extensions 是 Gnome 的一系列插件,类似 Chrome extension,能够起到系统增强的作用,我们可以借助 Chrome浏览器进行安装。 具体步骤如下: 安装 Chrome 扩展程序 GNOME Shell integration 安装 主机连接器 sudo apt install chrome-gnome-shell 接下来我们就可以在网站 GNOME Shell Extensions 安装 gnome 扩展了。 通过搜索找到自己心仪的扩展程序,点击进入详情页面,切换详情页面的 “OFF” 按钮即可安装对应扩展. 我自己安装的扩展程序包括: dash to dock 优化 Ubuntu 默认的 dock 如果启用了 dash to dock,最好卸载掉系统自带的 gnome-shell-extension-ubuntu-dock: 1 sudo apt purge gnome-shell-extension-ubuntu-dock User Themes 自定义 shell 主题 Coverflow Alt-Tab 优化 Ubuntu 默认窗口切换动作 Gnome Global Application Menu 将当前程序的菜单项提取到状态栏 NetSpeed 显示网速插件 Clipboard Indicator 提供剪切板历史记录功能 Recent Items 快速打开最近打开过的文件 Hide top bar 隐藏顶栏,可以设置为鼠标靠近屏幕上边沿时显示顶栏 Top Panel Workspace Scroll 快速切换工作区 Gravatar 把你的 Ubuntu 用户头像设置成你的 Gravatar 头像. TopIcons Plus 将传统托盘图标移动到顶部面板 (Wine 程序救星) 按下 Alt + F2,输入 r,回车重启 gnome。 使用 alias 简化常用命令 在 Linux 我们可以使用 alias 别名来简化常用命令,直接在 terminal 下输入 alias 就可以查看系统现有别名。 因为我们使用的终端是 zsh,所以这里介绍一下 zsh 下 alias 的使用方法。 使用命令: 1 vim ~/.zshrc 来编辑 zsh 的配置文件,在其中加入需要的配置即可,格式为: 1 alias ${alias_name}="${command}" 其中:${alias_name} 表示你要为 linux 命令设置的别名,${command} 表示原先的 linux 命令。 比如我自己感觉每次写博客都要 cd 去 hexo 目录很繁琐,就设置了这样一个别名: 1 alias tohexo="cd ~/Documents/Coding/Web/Hexo" 编辑完成之后,保存 .zshrc 文件,在终端输入: 1 source ~/.zshrc 即可使刚刚的设置生效。 再举个例子,比如有时候我想在命令行直接打开文件管理器,那么就可以设置一条这样的别名: 1 alias o="nautilus ./" 可以极大地简化操作。 不建议新手设置过多别名,毕竟学习阶段还是多熟悉一下 linux 命令比较好。 设置快捷键 继承了我在 Windows 下的操作习惯,总是习惯用 win + E 快捷键来打开文件管理器,不妨在 Ubuntu 中设置一样的快捷键,方便日常操作: 依次打开 “设置” - “设备” - “键盘”,拉到页面最下方,添加自定义快捷键; “名称” 写 “打开文件管理器”, “命令” 写 “nautilus”; 最后设置快捷键即可; 其他命令同理。 下面罗列出我正在使用的快捷键,仅供参考: 名称命令键位 打开文件管理器nautilussuper + E 打开设置中心gnome-control-centersuper + I 系统监视器gnome-system-monitorshift + ctrl + esc 截图flameshot guictrl + alt + A 关机poweroffctrl + shift + del 修复双系统造成的 windows 时间错误 先在 ubuntu 下更新一下时间,确保时间无误: 1 2 sudo apt install ntpdate sudo ntpdate time.windows.com 然后将时间更新到硬件上 1 sudo hwclock --localtime --systohc 重启进入 Windows 系统,更新时间即可。 软件安装 想了一下,软件也算是系统的一部分。工欲善其事,必先利其器,这里收集了一些自我感觉良好的软件。 Motrix | Github 一款高颜值,跨平台,功能强大且开源的下载工具。下载地址:Release Motrix 。 flameshot flameshot 是 Ubuntu 下的一款比较优秀的截屏软件。 安装: 1 sudo apt-get install flameshot 使用: 1 flameshot gui 快捷键命令同上。 peek Ubuntu 下的屏幕录制工具,可以录制 GIF,MP4,APNG,WebM 等多种格式。安装方法: 1 2 3 sudo add-apt-repository ppa:peek-developers/stable sudo apt update sudo apt install peek 音视频软件 网易云音乐 网易云音乐算是目前为止 Linux 下最好用的音乐客户端了吧,直接到 网易云音乐官网 下载 deb 安装包,在安装包所在目录运行: 1 sudo dpkg -i ${网易云音乐安装包文件名} 即可。 视频播放器 VLC 支持倍速播放,界面相对来说也比较美观,安装命令: 1 sudo apt install vlc 办公软件 XMind ZEN 超赞的思维导图软件,下载对应的安装包安装即可. 下载地址:Download | XMind ZEN deb WPS Office 虽然不及 Windows 上面的 Office 那般强大,但这也确实是 Linux 下的最好选择了。 下载地址:Download | WPS Office 字体文件:IamDH4/ttf-wps-fonts 通过 dpkg 命令安装下载好的 deb 安装包即可。 百度云网盘 iikira / BaiduPCS-Go 这是一款用 Go 语言编写的跨平台百度网盘客户端,除了正常的多线程下载之外最最炫酷的一点是它可以在 CLI 模式下操作百度云盘里的文件,感觉与本地无异,强烈推荐尝试! MarkDown 编辑器 用户体验上来讲我个人首推 Typora,但是毕竟 Haroopad 支持 vim 快捷键,程序员可以尝试一下. Typora Typora 是一款轻量、优雅、跨平台、实时预览的 MarkDown 编辑器。并且可以将 Markdown 文件转化为多种格式输出。 下载地址:Download | Typora Vnote VNote 是一个受 Vim 启发开发的专门为 Markdown 而优化、设计的笔记软件。是一个更了解程序员和Markdown的笔记软件。 Vnote 的定义是一款笔记软件,配合 Github 或者 gitee 可以当做云笔记来使用. 下载地址:Download | Vnote Vnote 下载之后是一个 AppImage 文件,每次运行都会执行一次解包操作。我们可以通过提前解压来加快软件运行过程: 1 2 3 4 5 6 7 8 9 10 ## 给 AppImage 文件添加可执行权限 chmod +x ./xxx.AppImage ## 解压 AppImage 软件包 ./xxx.AppImage --appimage-extract ## 将解压后的软件移动到 /opt 目录 sudo mv ./squashfs-root /opt/vnote ## 运行 /opt/vnote/AppRun ## 添加快捷方式(放到后面讲) ## cp /opt/vnote/vnote.desktop ~/.local/share/applications/ 聊天软件 Telegram 1 sudo apt install telegram-desktop WeChat & Tim wszqkzqk/deepin-wine-ubuntu 这个项目是 Deepin-wine 环境的 Ubuntu 移植版,可以在 Ubuntu 上运行 Tim,微信,网易云音乐,百度云网盘,迅雷等 Windows 软件,可以说是很良心了,使用方法参见项目文档。 小玩具 edex-ui A science fiction desktop running everywhere。Awesome. 编译工程的时候拿出来摆桌面还是很不错的。 wkhtmltopdf | Github 可以通过命令行把指定网页转换为 pdf 或者图片. 编程软件集合 Intellij IDEA 下载地址:Download | Intellij IDEA Intellij Pycharm 下载地址:Download | IntelliJ Pycharm Navicat Premium 下载地址:Download | Navicat Premium Sublime Text 强大的文本编辑器。 1 2 3 4 5 wget -qO - https://download.sublimetext.com/sublimehq-pub.gpg | sudo apt-key add - sudo apt-get install apt-transport-https echo "deb https://download.sublimetext.com/ apt/stable/" | sudo tee /etc/apt/sources.list.d/sublime-text.list sudo apt-get update sudo apt-get install sublime-text 其他系统安装方法见官网:Linux Package Manager Repositories – Sublime Text 3 Documentation Java jdk 我自己开发用的是 openjdk-8,大佬可以按需选择。 1 sudo apt install openjdk-8-jdk openjdk-8-source openjdk-8-doc Maven 1 sudo apt install maven Docker Docker 的安装方法见:Install Docker Engine on Ubuntu | Docker Documentation 安装命令: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 ## 卸载旧版本 sudo apt-get remove docker docker-engine docker.io containerd runc ## 使 apt 工具支持 https sudo apt-get update sudo apt-get install \ apt-transport-https \ ca-certificates \ curl \ gnupg-agent \ software-properties-common ## 添加 Docker 官方的 GPG key curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - ## 校验 key sudo apt-key fingerprint 0EBFCD88 ## 添加 Docker 源 sudo add-apt-repository \ "deb [arch=amd64] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) \ stable" ## 安装 Docker sudo apt-get update sudo apt-get install docker-ce docker-ce-cli containerd.io ## 验证是否安装成功 sudo docker run hello-world Mysql 首先是 Mysql 客户端,安装命令: 1 sudo apt install mysql-client 然后是 Mysql-server,建议使用 Docker 安装,以 Mysql 5.7 为例: 1 sudo docker run --name mysql57 -e MYSQL_ROOT_PASSWORD=root -p 3306:3306 -d mysql:5.7 安装完成之后,就可以在客户端通过以下命令连接数据库了,密码为 “root”: 1 mysql -h 127.0.0.1 -P 3306 -u root -p Remmina 功能丰富的远程桌面共享工具,支持 RDP、VNC、NX、XDMCP 和 SSH。 1 sudo apt install remmina 创建软链 有一些软件下载之后就是可执行文件,每次运行都要 cd 到软件所在目录也是麻烦,除了 alias 别名之外还有一种方法就是创建软链,在 /usr/bin/ 目录下创建软链之后就可以在系统任何地方执行命令了。 创建软链的命令如下: 1 sudo ln -s ${file_path}/${file_name} /usr/bin/${new_command} 其中: ${file_path} 代表可执行文件所在的路径; ${file_name} 代表可执行文件的文件名; ${new_command} 代表新的命令; 之后,就可以在任意终端输入 ${new_command} 来打开软链指向的程序了。 软链还有很多有用的特性,感兴趣的可以自行查阅相关资料,此处不再赘述。 为应用添加启动图标 依然是针对极个别的可执行文件,安装之后在 dash 栏是搜索不到的,因为在 /usr/share/applications/ 目录下没有他们的 .desktop 文件呀,既然没有,创建一个便是。 以 vnote 为例: 1 vim ~/.local/share/applications/vnote.desktop 填入以下内容: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 [Desktop Entry] Type=Application Version=1.0 Name=VNote GenericName=Markdown Note Comment=Note-taking application for pleasant Markdown ## 指定应用图标位置 Icon=/opt/vnote/vnote.png Terminal=false ## 指定可执行文件位置 Exec=/opt/vnote/AppRun %F MimeType=text/markdown; Categories=Qt;Utility;TextEditor;Office; StartupNotify=false StartupWMClass=vnote 此处可能需要重启电脑生效。 其中: Name 是应用名称,也就是在 dash 栏搜索是需要输入的内容; Exec 是可执行程序路径; Icon 是应用图标,当 Type=Application 时有效; StartupWMClass 是图标分类依据,这个字段值相同的图标会自动被分为一组。 其他字段不多说,自行查找资料吧。 后记 以上,皆是我 Ubuntu 装机后的各种优化方案。杂乱堆砌,毫无章法,若有纰漏,还请见谅。 我的个人微信是:diqidan,微信公众号:“菜鸟小柒”,欢迎通过上述方式与我交流。

2020/5/2
articleCard.readMore

Python 兴趣小组第十周作业

学习目标 第十周的部分目标在第九周提前完成了,第十周实战章节的教程与我目前的能力不太符合。索性调整了计划,自己设计实现了一个简单地新闻爬取程序。 利用了第三方现有的接口,综合使用了之前学到的许多知识,勉强能让自己满意吧。 学习代码 学习代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 # -*- coding: utf-8 -*- import json import requests import logging import mysql.connector from datetime import datetime from apscheduler.schedulers.blocking import BlockingScheduler # 配置 log 等级 logging.basicConfig(level='INFO') # 新闻 api 地址 REQUEST_URL = 'https://news.topurl.cn/api' # api 抓取间隔,单位为秒 REQUEST_INTERVAL = 60 # 单个分类新闻最大数量 CATEGORY_ITEM_MAX_SIZE = 10 # 新闻抓取最大数量 NEWS_MAX_SIZE = 10 # 新闻时效,只查询过去 n 小时的新闻 PAST_HOURS_OF_NEWS = 23 class New(object): def __init__(self, title, url, category): self.title = title self.url = url self.id = int(url[-12:-5]) self.category = category # @staticmethod # def hash_str(string): # """ # 计算字符串的 hash 值 # :param string: 待计算 hash 值的字符串 # :return: 整型 hash 值 # """ # if not isinstance(string, str): # string = str(string) # # sha = sha1() # sha.update(string.encode('utf-8')) # return int(sha.hexdigest(), 16) % (10 ** 8) def to_markdown_string(news_category): """ 把抓取到的新闻组织成 markdown 文本 :param news_category: 新闻分类字典 :return: markdown 文本 """ if not isinstance(news_category, dict): return keys = news_category.keys() markdown = '' for key in keys: h1 = '# ' + key + ' \n\n' markdown = markdown + h1 news = news_category.get(key) for new in news: list_item = '- [%s](%s) \n\n' % (new.title, new.url) markdown = markdown + list_item return markdown def get_news_from_api(): """ 从 api 抓取新闻列表 :return: 新闻列表 """ api = requests.get(REQUEST_URL) logging.info('抓取内容为:' + api.text) # 判断接口是否异常 if not api.ok: logging.warning("访问频率过高被限制") return [] # 判断是否被限制调用频率 response = json.loads(api.text) if 403 == response.get('code'): logging.warning("访问频率过高被限制") return [] # 返回新闻列表 news_list = response['data']['newsList'] return [New(i['title'], i['url'], i['category']) for i in news_list] def init_database(): """ 初始化数据库,建表等 :return: None """ conn = mysql.connector.connect(user='root', password='123456', host='127.0.0.1', port=3306) cursor = conn.cursor() # cursor.execute('DROP DATABASE IF EXISTS `morning_new`') cursor.execute('CREATE DATABASE if not exists `morning_new`') cursor.execute('USE `morning_new`') # cursor.execute('DROP TABLE IF EXISTS `news`') news_table_sql = r""" create table if not exists `news` ( `id` bigint(0) not null auto_increment comment 'id', `category` varchar(20) not null comment 'category', `title` varchar(500) not null default '' comment 'title of the new', `url` varchar(1500) not null default '' comment 'url of the new', `create_time` timestamp not null default CURRENT_TIMESTAMP comment 'create_time', primary key (`id`) ) charset=utf8mb4 comment 'news item'; """ cursor.execute(news_table_sql) cursor.close() conn.close() def get_mysql_connection(): """ 获取 mysql 连接 :return: mysql 连接 """ return mysql.connector.connect(user='root', password='123456', host='127.0.0.1', port=3306, database='morning_new') def remove_duplicates(news): """ 去除重复新闻 :param news: 待去重新闻列表 :return: 去重后的新闻列表 """ if not isinstance(news, list) or len(news) == 0: return [] id_list = [i.id for i in news if isinstance(i, New)] sql = r"select id from news where id in (%s)" % ','.join(['%s'] * len(id_list)) conn = get_mysql_connection() cursor = conn.cursor() cursor.execute(sql, id_list) duplicates_new_id_list = [i[0] for i in cursor.fetchall()] cursor.close() conn.close() non_duplicates_new_list = [i for i in news if not duplicates_new_id_list.__contains__(i.id)] return non_duplicates_new_list def save_news(news): """ 把新闻写入数据库 :param news: 新闻列表 :return: None """ if not isinstance(news, list): return None # 新闻去重 news = remove_duplicates(news) if len(news) <= 0: return None sql = r'insert into news (id, category, title, url) values ' param_list = [] for new in news: if not isinstance(new, New): continue sql = sql + ' (%s, %s, %s, %s), ' param_list.append(new.id) param_list.append(new.category) param_list.append(new.title) param_list.append(new.url) sql = sql[0:-2] conn = get_mysql_connection() cursor = conn.cursor() cursor.execute(sql, param_list) conn.commit() cursor.close() conn.close() logging.info('存入 %s 条新闻' % len(news)) def query_categories(): """ 获取当日新闻分类列表 :return: 当日新闻分类列表 """ sql = r'select distinct category from news where create_time > (current_timestamp - interval %s hour)' conn = get_mysql_connection() cursor = conn.cursor() cursor.execute(sql, (PAST_HOURS_OF_NEWS, )) category_list = [i[0] for i in cursor.fetchall()] cursor.close() conn.close() return category_list def query_news(): """ 查询过去 23 小时内的最新几条新闻并分类返回 :return: 分类完成的过去 23 个小时内的最新几条新闻 """ category_list = query_categories() paper = {} sql = r""" select title, url from news where category = %s and create_time > (current_timestamp - interval %s hour) order by create_time desc limit %s """ conn = get_mysql_connection() cursor = conn.cursor() for category in category_list: cursor.execute(sql, (category, PAST_HOURS_OF_NEWS, CATEGORY_ITEM_MAX_SIZE)) fetch = cursor.fetchall() news = [New(i[0], i[1], category) for i in fetch] paper[category] = news cursor.close() conn.close() return paper def get_and_save_news(): """ 从 api 获取新闻并存储到数据库 :return: None """ news = get_news_from_api() save_news(news) def query_and_publish_news(): """ 查询并发布新闻 :return: None """ paper = query_news() dt = datetime.now() with open(dt.strftime('%Y%m%d%H%M%S') + '.md', 'w') as f: f.write(to_markdown_string(paper)) def main(): init_database() scheduler = BlockingScheduler() # 新闻抓取 scheduler.add_job(get_and_save_news, 'interval', seconds=REQUEST_INTERVAL) # 新闻读取 scheduler.add_job(query_and_publish_news, 'cron', minute="0") try: scheduler.start() except (KeyboardInterrupt, SystemExit): print('Exit') pass if __name__ == '__main__': # todo 提取 SQL 执行部分代码为方法 main()

2020/4/25
articleCard.readMore

愿世间美好与你环环相扣

前言 我是一个有着轻微强迫症的人,万事万物都想追求尽善尽美。 对于工具,亦是如此。欲善其事,先利其器。我一直在寻找更加得心应手的工具。 所幸、这样的工具并不少见;可惜,很多工具鲜为人知。 所以,我做了一个网站,罗列了一些我个人认为很好的值得与大家分享的东西。 愿世间美好,皆与你环环相扣。 简介 网站的地址是:bookmark.diqigan.cn,点击链接即可到达。 这里简单罗列一些资源,希望你能够喜欢。 学习资源 精品网课 极客时间 轻松学习,高效学习 慕课网 程序员的梦工厂 网易公开课 国内外名校公开课 下厨房 实用菜谱做法与饮食知识 科普视频 Crash Course 中文字幕组 涵盖了各个学科的科普视频,视频内容小而精,强烈推荐 李永乐老师 与我们的生活密切相关的各种科普知识,超赞! 译学馆 特别有意思、涨知识的英语学习平台 3Blue1Brown 深入浅出、直观明了地分享数学之美 效率办公 在线工具 滴答清单 达成更多,用心生活 幕布 极简大纲笔记 | 一键生成思维导图 ProcessOn 免费在线作图、实时协作 Firefox Send 简单、私密的文件分享服务 小码短连接 短链接生成器,拥有统计功能 PDFCandy 免费在线的 PDF 编辑器 cloudconvert convert anything to anything Convertio 将您的文件转换成任意格式 电脑软件 Chrome 最好用的浏览器,没有之一 Typora 所见即所得的 Markdown 编辑器 Xmind Zen 一个全功能的思维导图和头脑风暴软件 VLC 一款自由、开源的跨平台多媒体播放器 Sublime Text 能够大幅度提高效率的文本编辑工具 Listary Windows 下的效率搜索与快捷启动工具 everything Windows 下的高效文件搜索工具 Snipaste 超级好用的截图 + 贴图工具 Awesome Windows Windows 精品软件清单 Geek Uninstaller Windows 下的软件卸载小工具 Captura 录制屏幕、声音、光标和按键 火绒 如果一定要安装杀软,那就选择火绒吧 在线文档 石墨文档 多人实时协作的「云端 Office」 腾讯文档 一款可多人协作的在线文档 语雀 十万阿里人都在用的笔记与文档知识库 花火 免费的在线图表制作工具,可以制作动态排名条形统计图 Dagoo 一站式数据可视化分析平台 文图 在线图表制作工具 吾道 专业演示文档制作工具,PPT模板,协同办公 PPT BOSS PPT 免费在线制作工具 PPT 模板 稻壳儿 WPS 稻壳儿官网 演界网 收费但高质量的PPT模板,PPT图表,PPT作品,PPT动画网站 优品PPT 免费PPT模板下载 HiPPTer PPT资源导航 后记 如果你也收藏了一些精致的小工具,欢迎留言与我交流,期待你的分享。

2020/4/18
articleCard.readMore

Python 兴趣小组第九周作业

学习目标 完成 Python 教程 - 廖雪峰的官方网站 以下章节的学习并输出学习笔记: 访问数据库(MySQL 部分) Web 开发 异步 IO 学习笔记 测试代码 测试代码

2020/4/11
articleCard.readMore

Python 兴趣小组第八周作业

学习目标 完成 Python 教程 - 廖雪峰的官方网站 以下章节的学习并输出学习笔记: 图形界面 网络编程 电子邮件 学习笔记 这周笔记内容很少 测试代码 测试代码

2020/4/9
articleCard.readMore

Python 兴趣小组第七周作业

学习目标 完成 Python 教程 - 廖雪峰的官方网站 以下章节的学习并输出学习笔记: 常用内建模块 常用第三方模块 virtualenv 学习笔记 测试代码 测试代码

2020/4/4
articleCard.readMore

围墙

围墙 温饱有余,奢侈不足。从小到大,我的家境一直是这样的一种境况。 凭借勤俭节约,我们能过上“还不错”的生活。所以,我一直不太理解“有钱人”的生活。 千元左右的安卓手机也能打电话玩游戏,为什么要花四五倍的价格去买 iPhone? 淘宝上遍地都是十几块钱的线控耳机,为什么要花几千块钱去买 AirPods、Sony? 都是打字,薄膜键盘凭啥就不如机械键盘? …… 大学之后,这些问题便常伴随我左右。 因为在自己的小天地里过得“还不错”,所以简简单单地把答案归结为“钱多没地方花”或者“打肿脸充胖子”。 从未去深究,从未去探寻围墙外的世界。 裂痕 观念逐渐发生变化,是在我上班之后。 住得离公司比较远,我每天需要在通勤上花费两个多小时。这可是两个小时啊,浪费了多不好。于是乎,我开始尝试听英语、听极客时间,用的是我在淘宝几块钱买来的线控耳机。即使戴了耳机,地铁上嘈杂的呼啸声依旧如雷贯耳,大段的音频内容被噪音盖过,我需要不断地调整播放进度,才能详细地听完整段音频。 不堪其扰,我不得不花 150 块钱换了一个入耳式蓝牙耳机,并期望上述状况可以被改善。最终的结果是,我放弃了在通勤中听音频。 面前这堵墙,再难提供“还不错”的感觉。 破 同事中,有一位有趣的数码控。断断续续,会听他提起主动降噪耳机。 “嗯?主动降噪是个什么东西?”上网查阅资料,看了大量测评视频,犹如突然发现了新大陆。“嗯,这就是我想要的东西”。犹豫了两个月之后,终于决定上车。在 pdd 以 1700 元的价格拿下了 AirPods Pro。很贵,但很值。 戴上 AirPods Pro 的那一刻,整个世界都安静了。我的世界,只剩下我。随心所欲、不被侵扰的我。 执念的围墙,轰然倒塌。 从未想到,我的个人习惯和价值观,会被一个小小的耳机打破。 以前,我不喜欢听音乐,我觉得音乐会干扰我的思绪。现在,我很大的一部分时间都在听音乐,它能帮助我更加专注; 以前,我不喜欢音频课,我觉得即便花精力学习,也是收效甚微。现在,上下班的路上,我已经离不开这类内容; 以前,我总觉得很多东西是奢侈品。“够用就好了,活得那么奢侈干什么?”现在,我觉得在经济状况支持的情况下,你值得拥有最好的。 立 我不是一个意志力坚定的人,任何一点微小的因素都可能让我心猿意马。所以我对学习环境的要求及其苛刻,好在遇到了 AirPods Pro,它给了我这样的环境。 如果说我之前的专注评分为 1 的话,现在我可以给自己打 5 分。 以前经常听到一句话:“便宜的东西,除了便宜,没有优点;昂贵的东西,除了昂贵,全是优点。” 虽是笑谈,不无道理。 现在,我的观念中立起了一道新的围墙。墙上刻着几个大字:“你值得拥有最好的”。 希望、生活中能有更多的新事物,能帮我扩建这堵墙。

2020/4/2
articleCard.readMore

月刊-202004

优秀文章 聊聊人怎么更值钱这个事情 No.184 最好的投资就是投资你自己。 高效程序员的6个习惯 找一个你崇拜或者最想成为的偶像,从他身上找出你最欣赏的特质,然后开始学习和模仿他,把这些特质变成你自己的习惯。 创业者的孤独感 自己选择走哪条路之前,仔细想好,一旦想好,就不要后悔。患得患失才会让人更痛苦。 巴菲特老爷子说,你们这样不好… 文中有三个归纳很不错: 长期坚持,保持耐心。 不断自我进化,拥抱变化。 知道自己能力的边界,认识自己的“愚蠢”。 IT 相关 单元测试 我是怎么进行单元测试的 如何测试 private 方法 作者介绍了自己单元测试的一些经验方法,可供参考。 亲,建议你使用LocalDateTime而不是Date哦 文章介绍了 Java8 中全新的日期和时间 API LocalData、LocalTime、LocalDateTime 等。 看世界 关于新媒体赚钱,你必须知道的30个思维 文章分享了关于新媒体赚钱的一些思路,有醍醐灌顶之效。 聊聊「这个事情天花板有点低,不想碰」 眼高手低。不行动,永远等于 0。 AirPods Pro 评测消费者报告 相当专业且全面的一个 AirPods Pro 测评视频。 谈谈认知宽度 文章谈论了“认知宽度”对于一个人的种种益处。 拙见/ 什么是网课? 所谓言,以证果,是为课。 关于课堂的很有深度的一篇文章,共勉。 拙见/ 什么是蟒营™? 别人组织的一个编程训练营,教育理念很好,可以参考文章规划自己的学习。 谈谈朋友圈的维护 朋友圈也算是我们的一个门面。 我看罗辑思维&得到简史 极客时间创始人池建强老师眼中的逻辑思维。 资源推荐 不用一行代码,就写了个爬虫!这款谷歌插件已经打包好了! 文章推荐了一款 Chrome 插件,可以 0 代码编写简单爬虫。 rrweb Record and replay the web. 这款工具可以将用户在 Web 页面上的操作过程记录下来,并转成可序列化的日志数据,也可进行回放。原理: rrweb:打开 web 页面录制与回放的黑盒子 [crawlab-team/artipub] 一个开源的一文多发项目。 快速入门新媒体,我想推荐这30个热门网站给你 大幅提高开发效率的 9 款工具 | 英文原文 文中罗列了对程序员比较有用的 9 个在线小工具。 这23款“轻量级”办公神器,一年能帮你省下几千块买正版软件的费用! 文章推荐了 23 款常用的在线工具。 106款不用下载的网页“神器”,毫无保留送给你。(强烈建议收藏!) 文章介绍了很多实用的网络资源。 Captura Windows 下一款开源免费的屏幕录制工具。 给视频加字幕?这8个工具助力微信视频号创作! 文章推荐了几种给视频添加字幕的工具。 『发个教程』教你在 3 分钟找到你想要的电子书 文章推荐了一些电子书下载网站。 小玩具 http://fakeupdate.net/ 假装系统升级的一个小工具。

2020/4/1
articleCard.readMore

Python 兴趣小组第六周作业

学习目标 完成 Python 教程 - 廖雪峰的官方网站 以下章节的学习并输出学习笔记: 进程和线程 正则表达式 学习笔记 测试代码 测试代码

2020/3/29
articleCard.readMore

Python 兴趣小组第五周作业

学习目标 完成 Python 教程 - 廖雪峰的官方网站 以下章节的学习并输出学习笔记: 面向对象高级编程 错误、调试和测试 IO 编程 学习笔记 测试代码 测试代码

2020/3/27
articleCard.readMore

反思

堕 我是一个很纠结的人。 不安于现状,不付诸改变。 退一步安于现状,可得温饱安逸;进一步付诸改变,可得蜕变升华。但我恰恰就身处这夹缝之间,不愿退,不易进。循环往复,无法自拔。庸人自扰,说的大概就是我现在的状态罢。 惑 我大多数时候的生活状态是这样的:忙时碌碌无为,闲时无所事事。 忙到尽头,便是自怨自艾;闲到极致,皆是胡思乱想。 为什么?我会成为这样的自己? 思 想起之前接触到的 五个为什么 理论,想着或许可以作用于此,找到导致现状的原因。花了一丢丢时间,过程记录如下: 为什么?我会陷入“忙时碌碌无为、闲时无所事事”的状态? 因为没有一个好的计划,导致“碌”时未必在做对的事,“闲”时不知道该做什么。 为什么我没有一个好的计划? 目标不清晰。 为什么目标不清晰? 没有刻意去制定、拆解目标。 为什么没有刻意制定和拆解目标? 因为执行力不强。 为什么执行力不强? 因为注意力涣散。不能很好地控制自己的行动,总是贪图更多的东西,做了很多与当前目标不相关的事情。 所以,当下我最根本的问题应该就是注意力涣散了。 嘴上说着自己要先纵向提升技术,再横向扩展能力。却总是在技术无关的事情上花费很多时间; 嘴上说着同一时间只做一件事情。却总是会不自觉地花费大量时间精力去做当下并不是那么重要的事情。 男人的嘴、在骗自己这件事情上,比骗别人厉害多了。 定 明白了原因,接下来便是行动了。抛却繁杂、回归本源。本源之能足够大,磅礴之势自然来。

2020/3/22
articleCard.readMore

Python 兴趣小组第四周作业

学习目标 完成 Python 教程 - 廖雪峰的官方网站 以下章节的学习并输出学习笔记: 函数式编程 模块 面向对象编程 学习笔记 测试代码 测试代码

2020/3/20
articleCard.readMore

Python 兴趣小组第三周作业

学习目标 完成 Python 教程 - 廖雪峰的官方网站 以下章节的学习并输出学习笔记: 安装 Python 第一个 Python 程序 Python 基础 函数 高级特性 学习笔记 测试代码 测试代码

2020/3/13
articleCard.readMore

《寄生虫》观后感

电影信息 寄生虫 기생충 (2019) 电影简介 电影《寄生虫》讲述了一个韩国贫苦人家凭借机遇、能力和心机把自己一家四口安排到一个富贵人家工作,最后因被前帮佣一家发现而引起冲突并导致真相败露的故事。 观影感悟 寄生虫 为什么电影的名字会被命名为《寄生虫》呢? 其实,在我看来,人人都是寄生虫。 员工寄生于公司,家人寄生于家庭,人民寄生于国家…… 这本无可厚非,社会本身就是互相依赖的。员工、家人、人民会为自己的公司、家庭、国家作出属于自己的贡献,公司、家庭、人民也常常因为员工、家人、人民的贡献而变得更好。这种寄生是良性、互利、可持续的。 那么电影中的“寄生虫”有什么特别呢? 他们越过了自己本应坚守的那条“线”,那条道德之线、善恶之线、职操之线。电影中朴社长也多次提到了逾越界线的问题。 设下计谋抢走了前司机和帮佣的工作,此为道德之线;为了隐瞒实情,禁锢甚至误杀了前帮佣,此为善恶之线;身为家教,却和自己的学生恋爱、身为司机,却屡屡谈及雇主家事、身为帮佣,却在雇主家中举办家庭聚会,此为职操之线。 基宇一家人本都是有能力之人,他们能很好地担任老师、司机、帮佣的工作。只是这些越线,使他们变得略加丑陋和可怜。 身上的味道 电影中也多次提及基宇一家人“身上的味道”,不同的人也有不同的猜测:香皂的味道?洗衣粉的味道?地下室的味道?挤地铁的人身上特有的味道? 在我看来,这是贫穷和阶级的味道。 不止是身体,我们的品行也是有“味道”的。 想要祛除这种味道,要提升生活品质,要提升自身能力,要扩展认知边界,要升华自身境界…… 这很难,但好在不是不可能。 救赎 最后男主说:“我要努力赚钱。”并想像自己买下这栋房子之后的情景。 我想,对于他来说,这就是最大的救赎吧。 寄生在他人的屋檐之下,终究不如筑起属于自己的高楼大厦。 后记 为什么想写这篇观后感? 我出生在贫穷的小山村,对基宇一家的种种无奈颇有感触。我能嗅到有些人“身上的味道”,我也在找寻属于自己的救赎。 如果可以,我也希望所有人都能找到属于自己的救赎。 安得广厦千万间,大庇天下寒士俱欢颜。

2020/3/3
articleCard.readMore

月刊-202003

优秀文章 沟通中几个常见的盲区 文章介绍了关于沟通的几个建议,很是受用。 不要跟着平庸的老板 凡是按时间或件数来计酬的工作,都别做太久; 职场政治是这样一个东西,你可以不去碰,但不能不去懂; 分清楚“我不适合”和“我不行”; 不要跟着平庸的老板; 平台能级永远大于个人奋斗。 如何避免自己成为一个傻叉 思维模式差距太大其实是无法交流的。如果你觉得一个人和谁交流都让人如沐春风还有收获,那说明那人要么是随意附和,要么情商智商双高,能降维和你交流。 当傻叉效应出现的时候,不要忘了,你在对方眼里,也可能是同样的形象。 从手机定位谈谈科技 有些我以为是常识的东西,后来发现并不是常识。 考研,还是找工作? 大学毕业后,是该考研?还是该找工作?这篇文章可以给你一点参考。 年化收益10%,您看的上吗 一篇不错的股票科普文。 月入1万和月入10万背后的逻辑思考 文章介绍了关于背后的逻辑和关于学习的思考,值得阅读。 职场选择,还真就不是一个人的事 文章分享了关于职场选择的一些注意点。 资源推荐 Github Pages访问太慢?通过Netlify免费加速 文章推荐了一种免费的静态资源托管服务器,个人站长可以考虑酌情使用。 DesertsP/Valine-Admin Valine Admin 是 Valine 评论系统的扩展和增强,主要实现评论邮件通知、评论管理、垃圾评论过滤等功能。 另外,作者还很贴心地提供了一篇使用教程。 BlogHub 一个开源的,中文独立博客导航站。 这款相见恨晚的效率神器,让我卸载了80%的软件。 文章介绍了一款 Windows 上的工具箱软件,一定程度上可以提高工作效率。 自动为视频添加字幕!我又多了两局开黑的时间。 文章介绍了几款自动为视频添加字幕的工具。 Privace 一款生成虚拟信用卡的工具。 翁天信 · Dandy Weng 的个人网站主页 你好, 我是翁天信, 一个自学者。 爱好旅行、摄影、设计和编程。 Neumorphism/Soft UI CSS shadow generator 一个 CSS 工具,可以简单轻松地实现新拟态效果。

2020/3/1
articleCard.readMore

Python 兴趣小组第二周作业

目标 系统学习 Python 知识,简单入门 Python 编程。 验收标准 学完 Python 教程 - 廖雪峰的官方网站 系列教程。 Python 入门学习计划 本计划依据 Python 教程 - 廖雪峰的官方网站 制定。 第一周 创建 git 仓库,同步学习代码; 完成以下章节的学习并输出学习笔记: 安装 Python 第一个 Python 程序 Python 基础 函数 高级特性 第二周 完成以下章节的学习并输出学习笔记: 函数式编程 模块 面向对象编程 第三周 完成以下章节的学习并输出学习笔记: 面向对象高级编程 错误、调试和测试 IO 编程 第四周 完成以下章节的学习并输出学习笔记: 进程和线程 正则表达式 第五周 完成以下章节的了解,不需要输出学习笔记: 常用内建模块 常用第三方模块 virtualenv 第六周 完成以下章节的学习并输出学习笔记: 图形界面 网络编程 电子邮件 第七周 完成以下章节的学习并输出学习笔记: 访问数据库 只学习 MySQL 部分; Web 开发 第八周 完成以下章节的学习并输出学习笔记: 异步 IO 实战 FAQ 期末总结

2020/2/29
articleCard.readMore

Python 兴趣小组第一周作业

Python 是什么? Python 是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。由 Guido van Rossum 于 1989 年底发明。Python 的第一个公开发行版发行与 1991 年,源代码遵循 GPL(GNU General Public License)协议。Python 语法简洁而清晰,具有丰富和强大的类库。它常被称为胶水语言,能够把用其他语言制作的各种模块(尤其是 C/C++)很轻松地联结在一起。 为什么会出现 Python 这种语言? Python 由 Guido van Rossum 于 1989 年年底出于某种娱乐目的而开发,Python 语言是基于 ABC 教学语言的,而 ABC 这种语言非常强大,是专门为非专业程序员设计的。但 ABC 语言并没有获得广泛的应用,Guido 认为这是非开放造成的。 Gudio 在 Python 中避免了 ABC 不够开放的劣势,加强了 Python 和其他语言如 C、C++ 和 Java 的结合性。此外,Python 还实现了许多 ABC 中未曾实现的东西,这些因素大大提高了 Python 的流行程度。 Python 能解决哪些问题? Web 应用开发 Python 拥有 Django,Flask,Tornado 等 Web 开发框架,在 Web 开发领域已经被广泛应用。 自动化运维 在 Web 开发领域,Python 只是众多语言选择之一。但在自动化运维领域,Python 则是必备技能。灵活的功能和丰富的类库使其成为运维工程师的首选语言。 大量自动化运维工具和平台或以 Python 开发,或提供 Python 的配置接口。单从 Linux 内置 Python 这一点来看也足见其在服务器和运维领域的地位。 网络爬虫 也叫网络蜘蛛,是指从互联网采集数据的程序脚本。对于很多数据相关公司来说,爬虫和反爬虫技术都是其赖以生存的重要保障。尽管很多语言都可以编写爬虫,但灵活的 Python 无疑也是当前的首选。基于 Python 的爬虫框架 Scrapy 也很受欢迎。 数据分析 当通过爬虫获取了海量数据之后,需要对数据进行清洗、去重、存储、展示、分析,在这方面 Python 有许多优秀的类库:NumPy、Pandas、Matplotlib 可以让你的数据分析工作事半功倍。 科学计算 虽然 Matlab 在科学计算领域有着不可取代的地位,但 Python 作为一门通用的编程语言,可以带来更广泛的应用和更丰富的类库。NumPy、SciPy、BioPython、SunPy 等类库在生物信息、地理信息、数学、物理、化学、建筑等领域发挥着重要作用。 人工智能 Python 在人工智能大范畴领域内的数据挖掘、机器学习、神经网络、深度学习等方面都是主流的编程语言,得到广泛的支持和应用。 机器学习:Scikit-learn 自然语言处理:NLTK 深度学习:Keras、Google 的 TensorFlow、Facebook 的 PyTorch、Amazon 的 MxNet 这些已经占据业内主流的工具要么是用 Python 开发,要么也提供了 Python 版本。Python 无疑已成为 AI 领域的必修语言。 胶水语言 Python 简洁、灵活、通用,几乎可以在各种场景与各种平台、设备、语言进行连接,因此被称为胶水语言。有人把它比作小巧而又多功能的瑞士军刀。 Python 和其他语言相比有什么优劣? 优点 简单 相比其他编程语言,Python 代码非常简单,上手非常容易。 比如我们要完成某个功能,使用 C 语言可能需要写 1000 行代码,Java 可能是 100 行,而 Python 可能只需要 20 行。这是 Python 具有巨大吸引力的一大特点。 Python有相对较少的关键字,结构简单,和一个明确定义的语法,学习起来更加简单。 代码规范 Python 采用强制缩进的方式使得代码具有较好的可读性,而且 Python 语言写的程序不需要编译成二进制代码。 可移植 Python 程序可以跨平台运行。 丰富的标准库 Python 标准库非常庞大,可以帮助处理各种工作。 可扩展 如果你需要一段运行很快的关键代码,或者是想要编写一些不愿开放的算法,你可以使用 C 或 C++ 完成那部分程序,然后从你的 Python 程序中调用。 GUI编程 Python 支持 GUI 可以创建和移植到许多系统。 可嵌入 你可以将 Python 嵌入到 C/C++ 程序,让你的程序的用户获得”脚本化”的能力。 缺点 运行速度较慢 这是解释性语言的通病。 Python 程序比 Java、C、C++ 等程序的运行效率都要慢。 源代码加密困难 不像编译型语言的源程序会被编译成目标程序,Python 直接运行源程序,因此对源代码加密比较困难。 现阶段主要在哪些方面应用比较广泛? Python 现阶段主要用于 Web 开发,网络爬虫,人工智能,数据分析,机器学习,自动化运维、科学计算等领域。 能够帮你解决什么问题? 更简单地入门人工智能; 自动化运维; 倘若能够深入人工智能方向,可以为我所在的公司创造更多价值。 你能 / 你想用 Python 做些什么? 把一些重复性的工作脚本化; 尝试学习人脸识别相关知识; 参考资料 学了 Python 能用来做什么? — Crossin 可以用 Python 编程语言做哪些神奇好玩的事情?

2020/2/20
articleCard.readMore

实验 - Python 兴趣小组

背景 受 一名叫大蕉的程序员 的影响,我在过去一年里获得了比较大的成长。我也一直在想,自己能不能像他一样,帮助周围的人一起成长。 两个月前组建了一个写作群,虽发展不及预期,但效果还不错,群员都有自己的成长。亲眼看着有的人从一直有兴趣写作但几乎从未动笔到几乎周周更文并且还能获得班主任和辅导员的点赞转发;亲眼看着有人写出了比我更深刻的对于乡情的认识;亲眼看着有人可以用“我不想长高”切入,讲述让我眼眶发热的关于父爱的故事…… 到最后我只能惊叹道:“你们真是一群宝藏男 / 女孩”。他们的文字,让我收获良多。 一开始只想着看看自己能不能帮到别人什么。没曾想,在收获面前,我的付出根本不值一提。 在学校同乡会的群聊中,我曾不止一次看到不止一人说想学 Python,但之后貌似都没了下文。恰巧前几日群里又有人说起了这个话题,心想着我或许可以给他们一个开始的理由和坚持下去的动力及约束。 于是,“Python 兴趣小组”群应运而生。谨以此文,记录建群历程,供以后借鉴。 要求 基于以往的经验,对于这个小组,我提出了三个基本要求:有目标、有任务、有惩罚。其他的各种细节,均在讨论中商定。 有目标 请记住:“学习 Python” 不是我们的目标!!! 我们的目标必须是清晰、明确、可衡量的。我们需要把目标精确化,想通过什么途径学到什么水平达成什么目的。明确了这一点,才算是真正的有目标。 所以,在明确目标之前,我们需要对 Python 有一个大致的了解。Python 是什么?为什么会出现 Python 这种语言?Python 能解决哪些问题?和其他语言相比有什么优劣?现阶段主要在哪些方面应用比较广泛?能够帮你解决什么问题?你能 / 你想用 Python 做些什么? 解决上面这些问题,会是我们的第一个作业。 有任务 目标明确了之后,就要考虑怎么去实现这个目标,有一个专业的名词叫做“目标拆解”。 就是把一个比较大的目标拆解成一系列切实可行的颗粒度更小的执行项。和大目标一样,这些小的执行项也需要是清晰、明确、可衡量的。另外需要再加上一个时间约束,大致规划一下要在哪个时间点完成哪一个执行项并要求自己完成。 有惩罚 学习是反人类的,但它也是让我们变得比他人更优秀的唯一途径。三分钟热度的事情屡见不鲜,我们需要一些约束来强迫自己完成计划,这就是惩罚。 讨论 2020-02-16,在上述三个基本要求的前提下,我们发起了第一次讨论。参会人数不多,好在基本的约束都确定了下来。 目标 没错,这次玩得有点大。 我们的组员来自各个专业,水平参差不齐,制定相同的目标并不现实。 基于此,我们讨论决定学习目标采用“千人千面”的形式。 每个人制定自己的学习目标和学习计划,并且按照自己的节奏学习; 学习计划做好之后,需要公布出来给所有人看到,作监督之用; 目标需要简单可实现,不可好高骛远,执行困难。 周期 大目标周期确定为两个月。也就是说,目标需要是在两个月内可以实现的。时间太长容易拖沓夭折不可控。 目标拆解以周为周期,把大的目标拆解为多个子目标,每周完成一个子目标即可。 验收 因为每个人的学习计划不同,所以学习成果的验收并不简单。 需要成员提前制定并公布自己的学习计划,然后每周完成自己的周计划并留下可供检查的学习记录。 所有组员轮流检查作业。 记录 学习计划以及学习记录暂定以在线文档 语雀 的形式记录。 惩罚 最简单可行的方案就是发红包了。 红包不可小,小了没有约束力;红包不可大,大了会影响个人生活。 最后暂定以 10 元红包作为完不成周计划的惩罚。 资源 个人认为想要更好成长,每个人都需要有一定的资源检索能力,所以学习资源需要自行查找。 在学习开始之前,小组会组织讨论,分享好的学习资源。 最后 预祝大家都能遇见更好的自己。

2020/2/16
articleCard.readMore

月刊-202002

优秀文章 10 Signs You Will Suck at Programming 成为优秀程序员需要避免的 10 个误区。 给现在那些低学历的年轻人一点建议 持续学习,终身学习。 2020年,过来人给职场新人的建议 修炼硬核业务能力,在此基础上用软技能加分 挖掘公司给你带来除工资外的附加价值,利用公司资源优势找到和壮大自己 职场新人打造个人品牌是稳赚不赔的生意,从现在起重视个人品牌势能的积累 最好在25岁前明白的,30条职场潜规则 前辈们关于职场的一些建议,各取所需即可。 曾经优秀的人,怎么就突然不优秀了。 长江后浪推前浪,一代更比一代强。这篇文章,可以指导我们如何才不至于“死在沙滩上”。 现代人为什么爱生气? 现在的网络环境,愤怒的成本还是太低了。 有时间不学习,没时间的时候是没时间 文章分享了几个不错的编程原则。 踏实些不香么? 人这一生,就是复利效应的最好体现。 投资自己,永远都是稳赚不赔的生意。 自信,还是自卑,别傻傻的分不清 什么是自信?什么是自卑?这篇文章是一面镜子,帮助我们认清自我。 工作被拥抱变化了该怎么办 No.183 干不干得开心是兴趣问题,干不干得好是能力问题。 职场017: 承担更多任务 认知第一,升维第二,读书第三。 字节跳动的增长密码,看了你也学不会 流程太多的公司不会乱,只是会变得很慢、很僵化。 想要简化流程、将规则定得很简单、简化成少数原则,就需要一群高素质的人才。 那么什么是高素质的人才?大家照着原则而不是手把手的规则来配合,甚至只要知道目标,结合常识就可以行动。 网络教程 还在使用 SVN 的企业,如何快速迁移到 Gitee 文章介绍了几种把 svn 仓库转换成 git 仓库的方法。 远程协作 MEGAEASE的远程工作文化 字字珠玑,值得反复品读。 不得不在家办公后,技术人如何在线研发? 阿里的在线协同分享。 资源推荐 Windows桌面美化合辑,定制你的专属桌面! 腾讯桌面整理 应该适合大多数人,其他的可以折腾也可以忽略。 全!GitHub 总星 5.7w+,最赞的操作系统软件都在这里啦! Windows、Linux、MacOS 的优秀软件列表。 LyonWong / Yike 易灵微课的开源代码。 Asabeneh/30-Days-Of-Python 30 天学 Python 的一个简明英文教程。 Asabeneh/30DaysOfJavaScript 30 天学 JavaScript 的一个简明英文教程。 看世界 今天只有这30张图 世界之大,无奇不有。

2020/2/2
articleCard.readMore

2020,果真是多事之秋,开年便有诸多不顺。 最近经常听到别人说庚子年是灾年。但是我觉得吧,好事和坏事每天都在发生,只是有时候坏事恰巧发生在了庚子年,特别惹人注意,仅此而已。 作为一个 30% 的唯心主义者,我认为逆天改命最大的因素在于自身。管它大环境如何,做好自己,便是成功的开始。 虽然说环境的势能也不可忽略,但那都是后话。成就更好的自己,才能更好地利用势能。 归 几经波折。这个年,终究还是在老家过了。这个名不见经传的十八线小山村,也发生了巨大的变化。 政府投资或者是村民集资,村子里的主要街道全都变成了水泥路面,并且配备上了路灯。家家户户联网,三三两两有车。得益于国家的领导与庇佑,人民的生活确实是越来越好。 几乎每位村民的手机上,都能找到抖音快手火山 APP。短视频的崛起,势如破竹。 发小小聚,每个人都逃脱不了被催婚的命运。对于婚姻,我们和父辈的观点早已分道扬镳。父辈都想我们早早结婚生子稳定生活,而年轻一辈却更倾向于晚婚。年轻,使我们更喜欢未知和挑战。 在父辈眼中我们还是孩子,在孩子眼中我们已经变成了大人。我们和下一批年轻人之间,已然产生了新的代沟。 伤 因为过度放飞自我,受了点小伤,进了医院。 刚从医院归来,邻里乡亲便接踵而至前来探望。浓浓的乡情,使我热泪盈眶。 侃 和父辈侃聊,谈到一些观念上的差异,听他们诉说,也多了几分理解。 我经常说父辈的消费观念太保守,他们很排斥信用卡和花呗,对贷款更是敬而远之。如果能够善加利用,这些东西确实能够提高我们的生活水平。 我经常说父辈的理财观念太薄弱,把钱存在银行是不大划算的。想办法让资金流动起来,才能撬动更多的资源。 但是,他们的处境终究与我们不同。农民工,大概是当今社会最弱势的群体了吧,工作的不稳定性导致他们根本不敢超前消费。今天吃顿饱饭,明天可能就要挨饿。贷款的话,他们是很可能还不上的。 身上担着整个家庭,手上做着不稳定的工作,导致他们根本不敢以钱博钱。赌输了,赔上的是整个家庭的幸福。他们的每一笔投资都是谨小慎微,战战兢兢的。 我们的岁月静好,真的是因为有人在负重前行。 乡 借用一位师弟的话说:“我可能不认识你,但我一定可以通过我的关系网认识你。”这就是十八线小乡镇的特色。 一方有难,八方驰援。这种独特的乡情,是我们始终难以割舍的惦念。

2020/1/29
articleCard.readMore

我的 2019 总结和 2020 计划

前言 碌碌无为。 人生中的前 20 多年,似乎都是这同样的一种状态。 每一天都在学习,每一刻都在做事。但是一天下来,却想不起当天做了什么;一年过去,依旧停留在原地踏步。 可能,这是因为没有计划吧。 朝菌不知晦朔,蟪蛄不知春秋。漫无目的,得过且过,大局难定,大器难成。 上古大椿,八千春秋。知途知径,浑然天成。 姑且尝试着对自己的人生进行简单的总结与规划,若有裨益,幸甚至哉。 2019 总结 走出樊笼 18 年下,19 年上,大概是我前半生中最低落的时光罢。 幸而,遇到一些有趣的人,做了一些正确的事。终于走出樊笼,重拾自信。 感谢,那些从未放弃过我的人。 持续学习 有幸遇到大蕉,有幸遇到极客时间,有幸遇到知识星球,有幸关注了一大堆高质量公众号,有幸参加了“大蕉 ARTS 打卡小组”。 过去一年,我学到了比往年更多的东西。最可贵的,是认知的升级和自信心的重建。 尝试理财 因为好奇,花 18.8 块钱买了两个理财入门课程。也恰好稳定了工作和生活,能空出几千块闲钱尝试理财。 我选择了较高风险较高收益但是相对省心的基金,到目前为止尝试了 3 个月定投,理财的收益是买课花费资金的十倍有余。 所以,最好的理财就是投资自己,学习知识。敢于探索未知的领域,会发现更多美好的事物。 开始写作 即使是流水账般的 ARTS,写了三个月之后,认知的升级、自信的重建、交际的扩展,视野的开拓,专业的深入等等等等,这些收益是远超我一开始的预期的。 终于捡起了 2015 年注册至今一直想试却从未发文的微信公众号,写了两篇推文,自我感觉良好。 2020 计划 说起计划,我这一年的感悟是:不可空谈,不可漫谈。一定要从实际出发,从小事做起。步步为营,设置好阶段性目标和验收标准,好让计划不再落空。 Java 之前听说过这么一句话:“人生有三个半径:认知半径、能力半径、行动半径。我们要努力扩大认知半径、明确能力半径、缩小行动半径。”听起来很简单,但是尝试实践的时候,却发现自己很难去缩小行动半径。 人性本贪,看到什么都想要,碰到什么都想学。东一榔头西一棒子,到头来发现自己只是在原地兜圈。 最理想且力所能及的情况下,我们都应该成为一专多能的“ T 型人”。而这“一专”,我认为应该是自己赖以生存的技能。 所以,2020 年,我应该回归本业。少折腾一些华而不实的东西,多学习一下 Java 知识。 至少,在五个月以内,看完 《Java 编程思想》这本书吧。 读书 真的是进入社会,才会真正明白读书的好处。读书,可以让我们更多地看到别人眼中的世界,学到别人多年的结晶。巨人的肩膀就在那里,我们为什么不站在上面前行呢? 新的一年,暂且要求自己能够读完 4 本书吧。 写作 写作,是一个持续的发现自我,发现世界,归纳总结,输出表达的过程。它带给我们的收益不只是写作本身,还有视野的开拓、资料的收集、逻辑的论证、表达的精炼,甚至还能收获更多好友,何乐而不为? 职场之中,需要的除了专业素质过硬之外,还有可迁移能力的提升。而写作,恰巧就能很好地提升我们的可迁移能力。 新的一年,我会持续在微信公众号创作。内容可能是 Java 知识,也可能是成长鸡汤。总之,佛系创作,在路上就好。 要求不高,12 篇推文就好。 理财 保险 得益于收入的稳定,2019 年置办了一部分保险,包括意外险和医疗险。2020,希望能补齐重疾险和寿险。 基金 前辈有言:不要赚认知以外的钱。 对于现阶段的我来说,基金还是属于认知以外的事物。2020,我会尝试补充基金相关的理论知识,知其然,更要知其所以然。 验收标准,就以写出一篇基金相关的推文为准吧。 朋友 性格内向,加上外地求学,这几年疏远了一大批好朋友。2020,希望能跟他们继续联系,哪怕一个电话也好。 书法 其实就是想练一下字,19 年买了字帖却一直没能行动。2020,希望能够在这方面有所提升。 后记 嗯,Flag 都在这里了。写得很保守,认真对待的话都是能够完成的。 新的一年,祝大家心想事成,万事如意!也祝自己的 Flag 大旗屹立不倒!

2020/1/24
articleCard.readMore

月刊-202001

资源推荐 两个优秀的后台管理系统模板 vue-element-admin Ant Design Pro addog 广告创意 / 设计 / 文案 / PPT 模板 / 营销策略大全网址导航。 优秀文章 收获,静静等待 人生就像耕种,今天埋下一颗种子,不能奢求明天就可以收获。 选一粒好的种子,坚持耕耘,在下一个季节到来之前,静静等待即可。 活着是为了说服自己,不是为了说服别人 不同的认知体系,没必要事事说服别人。 企业要不要修善缘 科技向善,是一种产品能力。 我的2019年总结和2020年计划 人无法理解认知以外的事情,无法赚认知以外的钱。“这厮是如何做到吃喝玩乐金银满屋的呢?” 羡慕之外,还是多尝试一些新的东西,接触一些新的领域。别人可以,你也可以。 7 Skills of Highly Effective Programmers 高效编程的 7 个技巧。 很抱歉,我的立场不坚定 似乎看懂了,又似乎没看懂。 总之,关注事实本身,不要被立场蒙蔽视野这句话还是对的。 出不了门?节后可以考虑远程办公 池建强老师关于远程办公的一些经验, 从顶级黑客到上市公司老板 文章阐述了两个多行业通用的核心技术: 掌握系统运行的底层逻辑; 拥有反常规的分析视角。 开眼看世界 9千元大疆激光雷达发布!放话超过所有竞品,方案成本仅同行5%;无人车工程师激动了:我要买10个 科技带来的降维打击。 张小龙微信公开课PRO演讲:信息互联的7个思考 嗯,看一看别人眼中的世界。 高度不同,看到的东西自然不同。 复习张小龙微信公开课的主题演讲 fenng 大关于微信公开课的解读。 职场 职场005:技术不是唯一 技术不是唯一,记得多看书,多思考,这样子的技术宅,才能口吐莲花,妙语连珠,有趣得很。 职场006:怎样拆解需求 1 确认需求。 2 拆解成几个模块。 3 每个模块拆解成几个功能。 4 从经验推出哪些确定可以搞定,哪些没有把握。 5 找资料,测试那些没有把握的。 6 验证完成后,各个模块开始编写,记录,测验。 7 每个模块完成,进行跑流程。 8 验证,收尾。 9 出实现原理,以及设计逻辑,有时间画时序图。 10 反馈, 迭代,调整。 职场012: 什么是换位思考 多换位思考,你会有同情心,共情能力,你会懂的对方的想法,也就能够找到合适的解决方案。 职场011:个人成长曲线 所谓积累,一定程度上来讲,可以说是做好生活中的每一件小事,搞清楚这些小事背后的逻辑。 目标要远大,但是执行项要细小。知表知里,厚积薄发。 职场010:怎么看待年终奖 以年终奖判断公司好坏。 职场013: 破除不自信法则 设定目标,拆解攻克。 工作后,你悟出什么职场道理?

2020/1/1
articleCard.readMore

ARTS-16

Algorithm 1299. 将每个元素替换为右侧最大元素 解法一: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Solution { public int[] replaceElements(int[] arr) { for (int i = 0; i < arr.length - 1; i++) { int maxRight = arr[i + 1]; for (int j = i + 1; j < arr.length; j++) { if (arr[j] > maxRight) { maxRight = arr[j]; } } arr[i] = maxRight; } arr[arr.length - 1] = -1; return arr; } } 执行用时:137ms,内存消耗:39.6MB。 解法二: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Solution { public int[] replaceElements(int[] arr) { int length = arr.length; if (length < 1) { return arr; } int[] result = new int[length]; int maxRight = -1; for (int i = length - 1; i >= 0; i--) { result[i] = maxRight; if (arr[i] > maxRight) { maxRight = arr[i]; } } return result; } } 执行用时:1ms,内存消耗:39.1MB。 Review 5 Exceptional Morning Routines That You Probably Didn’t Consider Yet 你的早上决定了你接下来一天的生活。 作者认为早上的生活习惯对人接下来的一天至关重要,并且介绍了自认为能提高自身状态的 5 个实践: 补水; 散步; 清洁舌头; 我们经常会刷牙,但是很容易忽略舌头清洁。 干刷皮肤(每周一两次); 貌似是为了清理死皮?作者建议在晨浴前进行此项活动。 聆听双耳节拍; 可以帮助冥想,减少焦虑。 Tip 怎么给字符串字段加索引? 直接在字符串字段上面创建索引,可能会比较占用空间; 使用前缀索引,定义字符串的一部分作为作为索引; 要谨慎选择长度,技能节省空间,又不会额外增加太多的查询成本; 使用前缀索引的话就会丧失覆盖索引的优势,需要谨慎选择; 把字符串倒序存储,然后添加前缀索引,用于绕过正序字符串本身前缀区分度不够的问题(比如身份证号); 额外创建 hash 字段并对其添加索引,查询性能稳定,有额外的存储和计算消耗,并且不支持范围查询; Share 20 小时定律

2019/12/31
articleCard.readMore

ARTS-15

Algorithm 283. 移动零 解法一: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Solution { public void moveZeroes(int[] nums) { int loopTimes = nums.length, zeroCount = 0, insertIndex = 0; for (int scanIndex = 0; scanIndex < loopTimes; scanIndex++) { if (nums[scanIndex] != 0) { nums[insertIndex++] = nums[scanIndex]; } else { zeroCount++; } } for (int i = nums.length - 1; zeroCount > 0; zeroCount--, i--) { nums[i] = 0; } } } 执行用时:0ms,内存消耗:37.7MB。 解法二: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Solution { public void moveZeroes(int[] nums) { for (int scanIndex = 0, insertIndex = 0; scanIndex < nums.length; scanIndex++) { if (0 != nums[scanIndex]) { nums[insertIndex] = nums[scanIndex]; if (scanIndex != insertIndex) { nums[scanIndex] = 0; } insertIndex++; } } } } 执行用时:0ms,内存消耗:36.6MB。 Review 10 Signs You Will Suck at Programming 文章论述了 10 个影响编程的品格。想要入门的人可以自查自己适不适合编程,已经入行的人可以看看自己有哪些地方需要改进。 缺乏好奇心 缺乏自己解决问题的能力(或资源检索能力) 面对问题时缺乏耐心 攻克问题时没有成功的喜悦 对学习和理解不耐烦 厌倦思考 无法独立思考 思维僵硬、狭窄或者混乱 需要“正确”答案,而不是从一系列答案中甄别好坏 不注意细节 Tip MySQL 选错索引 MySQL 通过优化器来制定执行方案,优化器会结合扫描行数、是否使用临时表、是否排序等各种因素综合判断使用哪一个索引进行查询。 扫描行数判断:MySQL 在真正开始执行语句之前,并不能精确地知道满足这个条件的记录有多少条,只能通过统计信息来估算记录数,这个统计信息就是索引的“区分度”。一个索引上不同的值越多,这个索引的区分度就越好。而这个索引上不同值的个数,我们称之为“基数”(cardinality)。也就是说,这个基数越大,索引的区分度越好。但是 MySQL 是通过采样统计的方法来估算索引基数的,所以基数的估计很容易是不准的。 对此,可以通过 analyze table 命令来重新统计索引信息。 但是优化器的判断标准不止是扫描行数,是否使用临时表、是否排序等因素也会影响索引的选择。 索引选择异常的处理 采用 force index 强行选择一个索引; 考虑修改语句,引导 MySQL 使用我们期望的索引; 有写场景下,我们可以新建一个更合适的索引,来提供给优化器选择。或者删掉误用的索引。 Share 过年回家,看到的一些变化:年

2019/12/30
articleCard.readMore

ARTS-14

Algorithm 832. 翻转图像 解法一 使用双层循环。 1 2 3 4 5 6 7 8 9 10 11 12 class Solution { public int[][] flipAndInvertImage(int[][] A) { int[][] result = new int[A.length][A[0].length]; for (int i = 0; i < A.length; i++) { int columnNumber = A[i].length; for (int j = 0; j < A[i].length; j++) { result[i][j] = A[i][columnNumber - j - 1] ^ 1; } } return result; } } 执行用时:1ms,内存消耗:36.6MB。 解法二 减少内层循环次数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Solution { public int[][] flipAndInvertImage(int[][] A) { int[][] result = new int[A.length][A[0].length]; int innerLoopTimes = (A[0].length + 1) / 2; for (int i = 0; i < A.length; i++) { int columnNumber = A[i].length; for (int j = 0; j < innerLoopTimes; j++) { result[i][j] = A[i][columnNumber - j - 1] ^ 1; result[i][columnNumber - j - 1] = A[i][j] ^ 1; } } return result; } } 执行用时:1ms,内存消耗:36.3MB。 解法三 尝试在原有数组上操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Solution { public int[][] flipAndInvertImage(int[][] A) { int columnNumber = A[0].length; int innerLoopTimes = (columnNumber + 1) / 2; for (int i = 0; i < A.length; i++) { for (int j = 0; j < innerLoopTimes; j++) { int temp = A[i][j] ^ 1; A[i][j] = A[i][columnNumber - j - 1] ^ 1; A[i][columnNumber - j - 1] = temp; } } return A; } } 执行用时:0ms,内存消耗:36.2MB。 Review The Two Kinds of Moderate 文章阐述了两种截然不同的温和派:一种是自然的温和,他们本来的观点就是温和派;另一种是有意的温和,他们没有自己的观点,故意在极左和极右之间选择一个温和的观点。 这让我想到当今世界中的杠精。他们没有自己的观点,但是不管你的观点如何,他们总能找到你观点的对立面,然后对你进行无情的抨击。 极端点说,最可怕的事情不是你的观点被整个世界反对,而是一个人本身没有观点。很多时候,观点就是我们意识的载体。而意识,就是我们存在的证明。 对一件事没有观点,要么是自身没有灵魂,要么是认知落后。这才是比较可怕的事情。 Tip 普通索引和唯一索引 查询过程 使用普通索引,查找到满足条件的第一个记录后不会停止。系统会查找下一个记录,直到碰到第一个不符合条件的记录才停止; 使用唯一索引,由于索引定义了唯一性,查找到第一个满足条件的记录后,就会停止继续检索。 因为 InnoDB 的数据是按照数据页为单位来读写的。所以“使用普通索引查找下一个记录”这个操作的消耗大概率是发生在内存中的,对性能的影响微乎其微。 所以单从查询性能来讲,普通索引和唯一索引差异不大。 更新过程 change buffer:当需要更新一个数据页时,如果数据页在内存中就直接更新,而如果这个数据页还没有在内存中的话,在不影响数据一致性的前提下,InnoDB 会将这些更新操作缓存在 change buffer 中,这样就不需要从磁盘中读入这个数据页了。在下次查询需要访问这个数据页的时候,将数据页读入内存,然后执行 change buffer 中与这个页有关的操作。通过这种方式保证这个数据逻辑的正确性。 change buffer 实际上是可以持久化的数据,不仅在内存中有拷贝,也会被写入到磁盘上。将 change buffer 中的操作应用到原数据页,得到最新结果的过程称为 merge。除了访问数据页会触发 merge 外,系统有后台线程会定期 merge,在数据库正常关闭的过程中,也会执行 merge 操作。 索引,如果能够将更新操作先记录在 change buffer 中,减少读磁盘,语句的执行速度会得到明显的提升。 唯一索引的每次更新操作都需要先判断这个操作是否违反唯一性约束,那么对应的数据页会被读入到内存,所以没有必要使用 change buffer 了。 如果要更新的目标页在内存中,两者的执行效率相差无几。 如果要更新的目标页不在内存中: 对于唯一索引来说,需要将数据页读入内存,判断唯一索引是否冲突,没有冲突的话插入记录,语句执行结束; 对于普通索引来说,直接将更新记录在 change buffer 中,语句执行就结束了。 场景:对于写多读少的业务来说,页面在写完以后马上被访问到的概率比较小,此时 change buffer 的使用效果最好。 Share 近些年来,少有事物能够做到人尽皆知。遗憾的是,新型冠状病毒做到了。 但是,除去病毒本身,它背后的人生百态更加耐人寻味。 轻视 一部分人的轻视导致疫情没有在最佳的时间得到控制,一部分人的轻视导致自己、家人甚至是陌生人惨受牵连。 未知的事物,我们应该怀抱敬畏之心。 人血馒头 消息一出,各大网店、药店的口罩瞬间脱销。有趣的是、当我从药店空手走出来的时候,有个人从后面追上来:“哥,要口罩吗?”黄牛的嗅觉,真是灵敏。 随后又爆出新闻,有一些不合格的口罩被售出。 还有更劲爆的消息,甚至还有人回收口罩想要二次销售。 良心这种东西,总是会被利益给吃掉。 利益 什么是对?什么是错?对于人类这种群居生物来说,多数人的利益就是对,少数人的利益就是错。 社会、人心、就是如此。 疫区的人想出去,是为了自己的利益;外面的人想隔离,也是为了自己的利益。想要追求自己的利益本没有错,错在自己的利益和大多数人冲突。 谣言 疫情一出,谣言四起。 始终想不明白造谣的人有何意图。三人成虎,很多人又特别喜欢相信这类谣言,进而推波助澜。 最可怕的事情莫过于,我以为我在做的错的事情是对的。 普通人 我们这样的普通人,最好能够做到不传谣、不信谣、不轻视。在一定程度上,不给他人添乱,也是一种善良。 倘有余力,努力提升自己对真实时态的认识,然后尽可能科普给更多人。

2019/12/29
articleCard.readMore

月刊-201912

优秀文章 Kindle 盖泡面,单反爱吃灰 池建强老师经常会给极客时间很多课程写推荐文章。不像其他广告, 池老师的推广文章我是很喜欢阅读的。字里行间,无不透露着他对课程的喜爱与雕琢。用心做内容,并且为自己的产品自豪,达到这种境界,并不简单。 借用池老师的一句话:“如果连我自己都不好意思推荐我自己的产品,那它一定是有问题的”。 搭便车的时候,请别把车砸了。 仁慈和善良的人,实际上是被那些不够仁慈,不够善良的人所保护的。 听说你想过上流生活 关于消费观的一些见解。 微信搜一搜迈出新的一步,好戏来了 人无我有,人有我优,人优我便利。微信的布局着实可怕。 职场心经 职场001:什么时候跳槽 《联盟》这本书讲的观点我很喜欢,公司和员工是相互投资过程,公司要给与员工成长机会,哪怕是要跳槽,也要助力他。员工要想着给公司争取利润,为公司做好分内之事。 职场002:什么是可迁移能力 技术的确有可能会成为技术人的牢笼 。技术之外,刻意练习一下可迁移能力也会有很大的帮助。 职场003:什么是差异化竞争 差异化竞争说到底,就是保持自己的稀缺性,大家拼技术,你就拼下管理,需求分析,大家都在这个技术领域,你可以多看看市场其他可替代技术,来进行对比学习,寻到优缺点,那么当公司用的技术出现瓶颈的时候,你的机会就出现了。 比别人多做一点点。对自己的工作负责,为团队创造更大的价值,尝试未知的领域,拓宽自己的能力边界。 盲目创业是一道送命题 关于盲目创业的一点看法,很有道理。 没必要互相 diss,聊聊面试礼仪 面试,在大部分情况下是一个十分平等和公平的事情。企业招聘,你投简历,信息通过某种方式连接到一起,那就聊聊呗。笔试、面试,技能匹配、价值观匹配、薪资和岗位需求匹配,聊对了眼,那大家就试试一起合作做一件事,看能不能做成。不合适,那就再分开。这个过程不应该存在任何歧视、挑衅和不尊重对方的做法。

2019/12/26
articleCard.readMore

ARTS-13

Algorithm 1313. 解压缩编码列表 解法一: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Solution { public int[] decompressRLElist(int[] nums) { int resultLength = 0; int loop = nums.length / 2; // 计算目标数组的长度 for (int i = 0; i < loop; i++) { resultLength += nums[2 * i]; } // 执行解码操作 int[] result = new int[resultLength]; int resultIndex = 0; for (int i = 0; i < loop; i++) { for (int j = 0; j < nums[2 * i]; j++) { result[resultIndex] = nums[2 * i + 1]; resultIndex++; } } return result; } } 执行用时:1ms,内存消耗:37.8MB。 解法二: 优化了一下循环策略,性能差别不大。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Solution { public int[] decompressRLElist(int[] nums) { int resultLength = 0; for (int i = 0; i < nums.length; i += 2) { resultLength += nums[i]; } int[] result = new int[resultLength]; int resultIndex = 0; for (int i = 0; i < nums.length; i += 2) { for (int j = 0; j < nums[i]; j++) { result[resultIndex] = nums[i + 1]; resultIndex++; } } return result; } } 执行用时:1ms,内存消耗:37.3MB。 Review 7 Skills of Highly Effective Programmers 学习如何阅读他人的代码 对坏项目的意识 避免无效开会 学透你的版本控制系统 编写简单可维护的代码 学会排列优先级和说“不” 学习设计思想 Tip MySQL 中的锁 根据加锁的范围, MySQL 里面的锁大致可以分成全局锁, 表级锁和行锁三类. 全局锁 顾名思义, 全局锁就是对整个数据库实例加锁. MySQL 提供了一个加全局读锁的方法, 命令是 Flush tables with read lock(FLWRL). 当你需要让整个库处于只读状态的时候, 可以使用这个命令, 之后其他线程的以下语句会被阻塞: 数据更新语句 (数据的增删查改), 数据定义语句 (包括建表, 修改表结构) 和更新类事务的提交语句. 表级锁 MySQL 里面表级别的锁有两种: 一种是表锁, 一种是元数据锁 (meta data lock, MDL). 表锁 表锁的语法是 lock tables ... read / write. 与 FTWRL, 可以用 unlock tables 主动释放锁, 也可以在客户端断开的时候自动释放. metadata lock (MDL) MDL 不需要显式使用, 在访问一个表的时候会被自动加上. MDL 的作用是保证读写的正确性. 行锁 行锁就是针对数据表中 “行记录” 的锁. 也就是说, 两个针对同一行的更新操作不能同时执行, 后提交的事务必须在先提交的事务执行完毕之后才能执行. 两阶段锁 在 InnoDB 事务中, 行锁是在需要的时候加上的, 但并不是不需要了就立刻释放, 而是要等到事务结束时才释放. 这个就是两阶段锁协议. 所以, 如果你的食物中需要锁多个行, 要把最可能造成锁冲突, 最可能影响并发度的锁尽量往后放. 死锁和死锁检测 当并发系统中不同线程出现循环资源依赖, 涉及的线程都在等别的线程释放资源是, 就会导致这几个线程都进入无期限等待的状态, 称为死锁. 解决方案: 直接进入等待, 直到超时. 这个超时时间可以通过参数 innodb_lock_wait_timeout 来设置. 但是这种方法可能会出现误伤, 比如杀掉正处于锁等待阶段的线程. 发起死锁检测, 发现死锁后, 主动回滚死锁链条中的某一个事务, 让其他事物得以继续执行. 但是这种方法有额外的负担. 对于热点行更新导致的性能问题: 一种头痛医头的解决方案, 就是如果你能确保这个业务一定不会出现死锁, 可以临时把死锁检测关掉. 但是这种方法可能有损业务. 另一个思路是控制并发度, 并发控制最好做在服务端. Share 回首 2019,似乎一直在忙碌,但始终感觉寸步未进。静心回想,大概是因为贪吧。 想学 Java,想学 MySQL,想学 Docker,想学运维,想学产品,想学设计,想学 Vue,想学 React,想学写作,想学书法,想学理财…… 想学的东西太多,做得事情太杂,始终没有主线。多线程切换,全部都是蜻蜓点水,未曾深入。什么都想要,到最后什么都得不到。 老早就知道我的目标应该是成为一个 “T 型人”,一专多能。老早就知道我应该缩小行动半径,先纵向深入研究自己的专业领域。但是走到行动阶段,又总是被路途中的风景所吸引,从而迷失方向。 之前总是安慰自己说日进一步,只要自己始终在前进,终有一天能够达成目标。可是如果没有方向指引,连目标是什么都摇摆不定,所谓的日进一步也不过是在小小的庭院里转圈圈罢了。 2020,终究还是要作一下规划的。确保自己先精通本行,然后再逐步向周边延伸。一次只做一件事,一次做好一件事。 嗯,加油!

2019/12/25
articleCard.readMore

ARTS-12

Algorithm 1295. 统计位数为偶数的数字 题解: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Solution { public int findNumbers(int[] nums) { int count = 0; for (int num : nums) { int bits = 0; do { num = num / 10; bits++; } while (num != 0); if (bits % 2 == 0) { count++; } } return count; } } 执行用时:1ms,内存消耗:38.7MB。 Review Fashionable Problems 作者在许多不同的领域见到了相同的模式:尽管很多人在对应的领域内努力工作,但是仅仅只能探索了很小的一部分可能性,因为他们大都工作在相似的事情上。 即使是最聪明、最优想象力的人们,在决定要做什么时,他们也会出奇地保守。那些从未想象过以任何方式成为主流的人会陷入对主流问题的研究中。 如果你想尝试研究非主流问题,最好的方向之一是去看看哪些人们认为自己已经完全摸透了的领域:文章、演讲、风险投资等。你会在这儿发现一种模式:如果你可以在一个庞大但显然已经淘汰的领域中找到新方法,你发现的事物的价值都会乘以该领域已经覆盖的面积。 避免陷入和其他人做一样的事情的最好的方法是真诚地热爱你所做的事情。这样,即便犯了和其他人相同的错误,你也会认为这很重要并且能够坚持下去。 Tip 数据库索引 作用 索引的出现其实就是为了提高数据查询的效率, 就像书的目录一样. 常见的索引模型 哈希表 特点: 插入快, 等值查询快, 区间查询慢. 适用场景: 只有等值查询的场景. 有序数组 特点: 有序数组在等值查询和范围查询场景中的性能都非常优秀, 但是插入慢. 适用场景: 静态存储引擎. 搜索树 特点: 均衡了读写速度, 相对来说都比较快. 适用场景: 已被广泛应用. 索引类型 主键索: 主键索引的叶子节点存的是整行数据. 在 InnoDB 里, 主键索引也被称为聚簇索引 (clustered index); 非主键索引: 非主键索引的叶子节点存的是主键的值. 在 InnoDB 里, 非主键索引也被称为二级索引 (secondary index); 非主键索引查询到的是索引值, 主键索引查询到的是整行数据, 所以非主键索引的查询需要多扫描一颗索引树. 因此, 我们在应用中应该尽量使用主键查询. 建议 一般情况下, 建议使用自增主键. 原因有二: 自增主键的每次插入都是一条新纪录, 执行追加操作, 不涉及挪动其他记录; 主键长度越小, 普通索引的叶子节点就越小, 普通索引占据的空间也就越小. KV 场景下, 可以使用业务字段直接做主键, 需要满足两个条件: 只有一个索引; 该索引必须是唯一索引. 优化 根据非主键索引查询到的主键回到主键索引搜索的过程称为 回表, 回表会增加搜索次数, 导致查询效率降低. 覆盖索引 索引上的字段已经满足了查询需求, 这种索引称之为 覆盖索引. 由于覆盖索引可以减少树的搜索次数, 显著提升查询性能, 所以使用覆盖索引是一个常用的优化手段. 最左前缀原则 B+ 树这种索引结构, 可以利用索引的 “最左前缀” 来定位记录. 这个最左前缀可以是联合索引的最左 N 个字段, 也可以是字符串索引的最左 M 个字符. 所以, 如果通过调整顺序可以少维护一个索引, 那么这个顺序往往就是优先考虑采用的. 索引下推 MySQL 5.6 引入的索引下推优化 (index condition pushdown), 可以在索引遍历过程中, 对索引中包含的字段先做判断, 直接过滤掉不满足条件的记录, 减少回表次数. Share 之前听了极客时间谭超老师的直播,老师提到一个刷算法题的思路,感觉很是有道理。 做算法题最大的误区:只做一遍。 第一遍 5 分钟读题 + 思考,不会做不要紧,直接看解法,比较各种解法的优劣。然后背诵、默写好的解法。 第二遍 自己解题、优化、比较各种解法利弊、比较时间复杂度和空间复杂度。 第三遍 一天之后 重复做题、专项练习。 第四遍 一周之后 反复练习相同的题目。 第五遍 面试前一周恢复性训练。 我自己做题会陷入一种“自己做出来才算是会了”的误区。但谭超老师有句话很有道理:“我们是在学算法,不是在发明算法。”如此说来,自己一直死磕就有点浪费时间了。

2019/12/22
articleCard.readMore

ARTS-11

Algorithm 1304. 和为零的N个唯一整数 解法一: 钻了个漏洞。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Solution { public int[] sumZero(int n) { if (n <= 1) { return new int[]{0}; } int[] result = new int[n]; int initNumber = 1, sum = 0; for (int i = 0; i < n - 1; i++) { result[i] = initNumber; sum += initNumber; initNumber++; } result[n - 1] = -sum; return result; } } 执行用时:0ms,内存消耗:34.9MB。 Review What makes a programming exercise good? 什么促成了良好的编程实践? 很有趣 能够教会一些你在意的东西 比如它可以辅助你更好地工作,或者帮助你更好地完成目标等。 有一定的挑战性 不然就很容易变得无聊。 可量化的是否成功的标准 比如你要写一个 TCP 堆栈,怎样才算成功了?成功的标准必须是可量化的,比如你需要通过哪些测试用例才算是成功。 可以快速完成 一项编程工作一般要在 2-3 小时内完成。除非这项工作非常有趣,不然我们很少能够挤出 8 个小时的完整时间。 作者相信你可以做到 这一条相对比较模糊。一个前提条件是,你所学习的教程的作者相信你可以通过教程学会对应的东西。 你所学习的东西必须已经被测试过并且被证明是可行的的 作者举例一本食谱的作者测试了自己的所有食谱,以保证自己的食谱确实是可以操作的。 从另一个方面说,教程的提供者也必须保证你的教程是被测试过且证明可行的。 你所学习的东西不会遇到阻碍 很容易陷入与我们尝试学习的技能完全无关的编程练习中的一些随机问题,这会花费我们大量时间解决,但是对我们真正要学的东西并没有太大助力。 很容易获取帮助 如果学习过程中真的遇到了一些问题,你能够很容易获取帮助。 无需耗时的设置 我们学习教程时经常会遇到一些环境配置或者依赖引入的坑,这会耗费我们大量精力。 作者也对教程提供者提供了一些建议: 告诉人们你的教程适用于哪些环境(Windows / Linux / Mac .etc); 声明你的教程需要哪些依赖或者软件; 使用 Docker; 在浏览器中运行代码; 使用云操作系统; 很容易被扩展延伸 / 有较大的成长空间 你可以很容易地举一反三。 Tip MySQL 事务 作用 事务就是要保证一组数据库操作, 要么全部成功, 要么全部失败. 特性 ACID(Atomicity, Consistency, Isolation, Durability), 即原子性, 一致性, 隔离性, 持久性. 问题 多事务同时执行的时候, 可能会出现脏读(dirty read), 不可重复读(non-repeatable read), 幻读(phantom read) 的问题. 为了解决这个问题, 就有了事务隔离的概念. 事务隔离 隔离越严, 效率越低. 分类 SQL 标准的事务隔离级别包括: 读未提交(read uncommitted), 读提交(read committed), 可重复读(repeatable read) 和串行化(serializable). 实现 每条记录在更新的时候都会同时记录一条回滚操作. 同一条记录在系统中可以存在多个版本, 这就是数据库的多版本并发控制(MVCC). 建议 set autocommit = 1, 显式启动事务语句, begin 或者 start transaction. 配套的提交语句是 commit, 回滚语句是 roolback; 如果介意显式启动事务会多一次交互的问题, 可以使用 commit work and chain 语法. 在 autocommit 为 1 的情况下, 用 begin 显式启动的事务, 如果执行 commit 则提交事务; 如果执行 commit work and chain 则提交事务并自动启动下一个实物, 这样也省去了再次执行 begin 语句的开销. Share 累了的话,就休息一下吧 平时下班回家我是一定会学习的。这周四下班突然就觉得很丧,啥都不想干。干脆躺床上刷了一个钟的淘宝,顿时觉得心情爽朗不少,第二天的精神状态也特别好。顿觉之前我给自己的束缚可能还是太多了。 近一年以来,生活中陪伴我最多的就是电脑。 除了上班时间要和电脑培养感情,几乎是所有的业余时间,我也会逼迫自己坐在电脑前。美其名曰:学习。 但是,凭心而论,工作效率并不高,学习质量并不好。 这本来也不算什么。问题就在于,明明工作效率不高,我却会强制自己持续工作;明明学习质量不好,我却会砍掉自己所有娱乐。一天下来,除了碌碌无为,只剩下空虚寂寥。 世上本没有太多枷锁,我的现状更像是左手抓着右手,然后告诉自己:“你看,我的手被束缚了,啥都做不了。只能这样。”想想,也是可笑。 觉得累了的话,大可休息一下,调整好心情,以更好的姿态重新出发。嗯,还有,不要自己给自己上枷锁。

2019/12/21
articleCard.readMore

不尝试一下, 你怎么知道自己不行?

打卡这么简单的事, 真的能影响到我? 背景 ARTS 起源于 陈皓 老师的 左耳听风 课程, 我们在其基础上做了一丢丢改造, 感谢 陈皓 老师. 两个月前, 我的朋友 大蕉 发起了一个为期 8 周的 ARTS 打卡活动: 每周打卡,自行创建一个博客地址。 打卡要求: 8人8周为一个小组。入群门槛188,8周完成全额退款,若全员完成,大蕉发188红包。 一周没打卡,请发10*当前人数红包一个。 两周没打卡,请退群。 打卡条件: 大家自发每周完成一个ARTS: (也就是 Algorithm、Review、Tip、Share 简称ARTS) (高级打卡组) 1.每周至少做一个 leetcode 的算法题 2.阅读并点评至少一篇英文技术文章 3.学习至少一个技术技巧(编程或其他) 4.编写一篇有观点和思考的技术文章(500字以内) (初级打卡组) 1.每周至少做一个 leetcode 的算法题或者小玩具代码 2.阅读至少一篇英文技术文章 4.编写一篇文章(500字以内) 入群流程: 转账即报名,请描述自己想进入初级还是高级组 承诺: 我不承诺你一定能坚持下去,但我承诺你坚持下去肯定会看见不一样的自己 乍一看, 似乎很简单, 也似乎很难. 一咬牙, 我加入了这个计划. 执行 真正动手做起来, 还是有着一定的难度. 英语 没错, 我们都学习了将近 20 年英语, 但是很少有人能够真正把英语用起来. 这或许也是导致我英语水平很烂的原因吧 (当然, 不努力学习也是一部分). 刚决定加入打卡小组的时候最担心的就是 Review 了, 毕竟我大学连英语六级都没考过, 甚至说高中之后就没再好好学过英语, 我真的可以阅读英语原文吗? 主题选择 没错, 打卡不要求主题, Review 和 Share 内容自己选择. 但这又引发了其他问题, 高质量主题不好获取, 低质量主题懒得分享. 刚开始打卡的时候, 主题的选择也曾是一大难题. 时间安排 工作难免加班, 学习逃不过作业. 时间安排, 如何权衡? 结果 但是, 所有队友都完成了第一期打卡, 7/8 的队友选择了继续参加第二期打卡. 我们发现, 很多难题, 都只是你以为它很难而已. 真正动手做了, 会发现它也不过如此. 曾经担心自己英语水平不够, 但是真静下心来阅读英文原文的时候, 才会发现阅读并不需要很高的英文水准; 曾经担心自己找不到值得分享的主题, 但是只要保持开放求索的心态, 生活中值得分享的东西太多了; 曾经担心自己时间上面会冲突, 那大概是你没有安排过自己的时间. 花一点点时间规划, 按照规划严格执行, 这就不再是一个问题. 影响 Algorithm: 算法一直是计算机方向极其重要的一门学科, 但是不精通似乎也能完成工作. 于是被无期限拖延, 只是存活在我的心中, 除了偶尔产生一点愧疚感, 别无他用. 打卡小组给了我一种强制的约束, 而且约束的方向刚好是我自己需要且愿意去做的东西. Perfect! 虽然到现在写的算法题也还是很少, 但至少我在行动了, 而且出于惯性, 这个行动还会持续很久. Review: 在以前, 看到大篇英语的时候我的内心是拒绝的. 六级都没过, 我怎么可能看得懂? 果然还是右上角小红叉比较懂我. 但是真正静下心去做的时候, 我突然惊喜地发现原来自己可以! 这种成就感对我来说着实意义非凡, 再次看到英语原文, 我心中的恐惧消失了, 甚至于现在我开始试着去看一些国外技术框架的官方文档. 对于一个程序员来说, 能够与世界交流, 哪怕只是接收, 也绝对是一项极其重要的成长. 不尝试一下, 你怎么知道自己不行? Tips: 日常生活或者工作中, 经常会有一些令人惊喜的小技巧. 虽然不能左右最终的结果, 但是往往能让你在做的事情更加得心应手. 如果说, 我们的主业是反应剂, 那么 Tips 就是催化剂. 心有猛虎, 细嗅蔷薇. 用心观察, 思考, 发现, 生活会增加许多乐趣. 比如我发现以前只是知道名字的 Java Lambda 原来这么好用, 原来只是听说过的番茄钟真的能提高专注力, Chrome 中有着许多能够提高生产力的插件, IDEA 中有着大量帮助你更好地编程的插件, 甚至说我们常用的手机微信都隐藏了很多小小的惊喜. 世界是多彩的, 你想要的, 他全都有. Share: 人应该是一专多能的. 在自己的领域保持足够专业, 在认知层面保持足够宽广. 世界是多元的, 以开放的心态面对, 它会给你多元的碰撞和无限的可能. 保持开放的心态, 接受世界的馈赠, 迎接你的会是全新的成长. (当然, 这要约束在道德和法律范围内.) 总结 免费的有时候也是最贵的 很多时候我们都喜欢免费的东西, 免费的学习资料, 免费的打卡小组等等. 但是很多时候免费的东西恰巧是最贵的. 我们经常会看到各种公众号分享多达几个 T 的学习资料, 我也曾热衷于领取这类资料. 好像拿到手了就等于我学会了, 但是数量多意味着品质参差不齐, 拿来筛选资料的时间足够你学会一门新技术了. 这种损失, 很贵! 如果不是自制力足够, 免费的打卡小组有更大概率让你半途而废. 而这一次半途而废, 也很有可能在你心中留下 “我坚持不下来” 的烙印. 这种损失, 也很贵! 不尝试一下, 你怎么知道自己不行? 很多困难只是我们看起来很难而已, 尝试一下, 你会发现它不过如此. 科学的方法, 成功的捷径 诚然, 靠着良好的自制力和执行力, 我们终将走向成功. 但是如果有科学的方法支撑, 我们可以走得更快更稳. 比如番茄钟, 比如任务拆解, 比如时间梳理, 比如 5 Whys, 比如我还尚未学习的很多理论. 生命中的贵人 生命中, 我们会遇到很多贵人, 且行且珍惜. 先模仿, 再超越 如果你暂时没有目标, 不妨先模仿一个你仰慕的人. 走着走着, 前路自会清晰. 小 Tip Review 的主题, 大可选择一些自己喜欢的技术的官方文档. 跟着文档走一遍, 既练习了英语, 又了解了新技术, 何乐而不为? Share 的主题选择大部分取决于自己了解到的新的观点和思考, 中文独立博客列表 收集了国内大部分中文博客, 可以挑选自己喜欢的持续关注. 当然, 我希望你关注的范围不止是中文. 后记 如果这篇文章对你有所启发, 那么请树立一个目标并立刻行动起来. 立刻!!! 有些东西, 认知理解到了, 但是行动没追上, 也是等于 0. 附录-成员总结 小柒 个人希望继续 打卡对我的影响不止是打卡本身, 更多的是自律, 持续学习, 以及正向反馈, 甚至还顺带改变了我的拖延症, 总之对我个人来讲好处很多. 感谢! 至于过程, 一开始觉得还真挺难. 从大学开始就知道算法的重要性, 但是直到大学毕业都没有真正开始着手学习算法, ARTS 中我选的都是简单题, 但至少有了一个开端, 以及坚持下去的信念. 高中以后真没好好学过英语了, 做 Review 之前我以为我看不懂英文文档, 但是真正做了之后发现其实我可以. 做 和 不做的差别就在于此, 不试一下怎么知道自己不行呢? 人嘛, 最理想的境界是一专多能. 如果说 Share 做得是多能, Tip 可能就是对应一专了. 虽然是一些小的技术点, 但是它们能造成的影响远不止一个点. 人无法理解认知以外的事情, 所以我也一直在努力提升认知. Share 刚好与这件事情契合, 开拓视野, 提升认知, 分享观点, 交流讨论. 这是一个正向循环, 会促使我们持续进步. 不足, 现阶段多数人的 ARTS 主题是互不相关的, 所以 Tip 和 Share 的东西暂时不能很好地对小组中其他人提供帮助, 所以我们现阶段的交流是缺失的. 我的一个想法是如果有后续, 要不要考虑按照方向分组, 这样大家能交流的话题也会更多一点. 之前我的学习是没有规划的, 觉得自己需要学某个知识点, 然后可能拖了几个月都没有行动. ARTS 改变了这种状况, 我现在会强制自己在周一完成A, 周三完成R, 周五完成S, 偶尔可能会超时, 但是自我感觉还是比以前好很多的. 过去一年我都是处于一种认知大于行动的状态, 然后无论是生活还是技能还是心态都没有任何好转. 最近两个月是行动追赶认知的过程, 好多 0 突然变成了 1, 感觉整个世界都好起来了. 其实我也动摇过是不是要继续 有那么几天, 我反思过 ARTS 这件事, 当时我感觉 ARTS 对我的改变已经完成了, 我是不是要把更多精力转向当下的工作, 也就是拔高 “一专”. 但是后来又想了一下, 对于现在的我来说, “一专” 和 “多能” 并不冲突. 毕竟, 每天还是会有大把的时间被我浪费掉了. 想做的事情, 就从这里找时间吧. 拆解! 把 ARTS 拆开, 每个部分定一个 DDL, 其实还是很容易的. 微光 对我来说,就是可以看到自己一段时间内都做了什么学习;之前也有记笔记,但比较零散。ARTS就感觉比较好,可以在坚持一段时间之后回顾看看在A干了什么、R干了什么… 更好的是对ARTS进行阶段性的总结; 荻 说一个找工作秋招的我文章中没提到的: 我其实找工作期间每天几乎就是按照ARTS来学习的, Argorithm 算法题必须每天刷一道, Review 换成了专业技能知识点背题复习 Tips 就是自己学校的项目,实习公司做的项目的深挖,总结复习 Share 就是能写博客输出就输出,要不就记录公司的面试历程,题目 总的下来,有沉淀,有积累,有收获,有目标推动 我觉得工作了是一样的,A是你代码的逻辑能力,也可以靠刷题来提高;R是可以看英文论文,英文文章博客来提高自己解决问题能力和搜索能力,英文能力;T是你做业务的能力的提升,关键要总结;SHare就是人生的感悟,人最重要的就是为了生活,而且要生活的幸福 感谢! 如此,ARTS就不仅仅是ARTS了 而是生活工作的艺术 小傻孩 想继续 强化主动学习意识,有针对性学习 只是彼此之间领域不同交集很少,无法互相交流 forip 继续加一 毕竟就个人而言,我习惯性缺乏动力,而一件半强迫性的事对自身而言是非常有益的。 而且写ARTS的过程也是自己进步的过程,无论是否偷懒,还是认真达标甚至超标,我下笔了就代表我整理了甚至学习了,而一个小组的小范围不会像百人大群那样无所谓,看着他人的完成也会对自己有一部分的激励效果。 当然有时这件事也会对自己造成一定的困扰,毕竟懒癌晚期,有时工作日的整理不到位就导致周日上午的压力剧增(ps由于周日的早起导致了提前习惯了搬公司的早起。。。某种意义上这是件好事) 不过就交流而言的群里交流是比较少,可能连有人连对方的arts都没有读完(我自己= =)。我是希望能定期能从别人的文章中拿到一个review、share或者tip进行集中的讨论。 大蕉 很多人都是眼睛学会了 脑子和手没学会 等于 0 健行 其实感慨颇多,我最主要的是在与拖延症作斗争,然后最大的收获就是好记性不如烂笔头。 好多以为自己看了就会,真正写起来,脑子就短路了

2019/12/21
articleCard.readMore

ARTS-10

Algorithm 1252. 奇数值单元格的数目 解法一 思路: 因为题目中行列式每次数据变换都是整行或者整列同时进行的, 所以只需要统计每一行和每一列会被自增多少次即可. 最后对应行和列的统计结果相加即为对应坐标的数值. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Solution { public int oddCells(int n, int m, int[][] indices) { int[] rows = new int[n]; int[] columns = new int[m]; for (int[] axis : indices) { rows[axis[0]]++; columns[axis[1]]++; } int oddNumber = 0; for (int row : rows) { for (int column : columns) { if ((row + column) % 2 == 1) { oddNumber++; } } } return oddNumber; } } 执行用时: 1ms, 内存消耗: 38.7MB. 解法二 思路: 统计行列累加次数的思路同解法一. 但是最后的记数可以优化. 解法一遍历了每一个坐标的数值并对其进行奇偶判断, 最后累加出奇数数量; 但是实际上我们没有必要对其进行遍历. 我们可以把行和列拆开, 分别统计最后有多少个奇数行, 有多少个奇数列, 然后使用算数求解最后的奇数数量. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Solution { public int oddCells(int n, int m, int[][] indices) { int[] rows = new int[n]; int[] columns = new int[m]; for (int[] axis : indices) { rows[axis[0]]++; columns[axis[1]]++; } int r = 0, c = 0; for (int row : rows) { r += row % 2; } for (int column : columns) { c += column % 2; } return r * (m - c) + c * (n - r); } } 执行用时: 0ms, 内存消耗: 35.6MB. Review 声明 水平以及精力有限,下文可能存在很多问题错译,仅做个人学习之用,如果需要学习 Docker, 还请查阅其他资料!!! Orientation and setup Docker 概念 Docker 是一个提供给开发人员和系统管理员拿来在容器中构建、分享、运行应用的平台。使用容器部署应用程序的过程叫作 容器化。容器不是新的发明,但是使用容器简单部署应用却是新出现的。 容器化越来越受欢迎是因为: 适应性强:即便是最复杂的应用也可以被容器化。 轻量级:容器利用并且共享系统内核,使它们在系统资源方面比虚拟机更高效。 便携的:你可以在本地构建,云端部署,然后运行在任何地方。 松耦合:容器是高度自给自足并且封装的,允许你更换或者升级其中一个而不影响其他容器。 可复制的:你可以通过数据中心添加并且自动分发容器的复制品。 安全:容器积极地将限制和隔离应用与流程而无需用户的任何配置。 镜像和容器 从本质上来讲,容积就只是一个运行的程序,只是增加了一些封装的功能,使容器与主机和其他容器隔离。容器隔离最重要的一个方面是每一个容器都只与自己的私有文件系统进行交互,这个文件系统是由 Docker 镜像提供的。一个镜像包含了运行容器所需的所有东西:代码、二进制文件、运行时、依赖或者是需要的任何其他文件系统对象。 容器和虚拟机 容器运行在 Linux 本地并且和其他容器共享系统内核。它运行了一个独立的进程,并不会比其他可执行应用占用更多的内存,这个特性使得容器更加轻量级。 截然不同的是,虚拟机运行了了一个完整的操作系统,并且通过虚拟机管理程序对主机资源进行虚拟访问。通常,虚拟机会产生大量的超出了应用程序逻辑所消耗的开销。 编排 容器化程序的可以执行和可重复性意味着我们有着可以跨云和数据中心来移动或者扩展应用程序的优势;容器有效地保证了这些应用程序在任何地方都会以相同的方式运行,使我们能够简单快速地利用所有环境。此外,随着应用程序的扩展,我们需要一些工具帮助我们自动化地维护这些应用,能够自动替换发生故障的容器,并且能够在应用的生命周期内对其进行管理和重新配置。 用来管理、扩展和维护容器化应用的工具称为编排器,最具代表性的是 Kubernetes 和 Docker Swarm 。 Docker Desktop 提供了这两个工具的开发环境部署,我们将在本指南中使用它来创建我们的第一个编排的、容器化的程序。 安装 Docker 桌面版 因为文档描述的安装流程只有 Windows 和 OSX,与我无关,不再转译。感兴趣的话请自行阅读 原文。 Tip 记录一下 Mysql 中的两种日志: binlog Server 层, 所有引擎都可以使用; binlog 是逻辑日志, 记录的是 SQL 语句的原始逻辑; binlog 可以追加写入, 做到不覆盖以前的日志; redo log InnoDB 特有日志; redo log 是物理日志, 记录的是 “在某个数据页上做了什么修改”; redo log 是循环写入, 空间会耗尽; 两阶段提交 binlog 和 redo log 使用 “两阶段提交” 保证一致性, 一条更新语句由执行器传递给 InnoDB 存储引擎 -> InnDB 执行成功后 -> InnoDB 记录 redo log 并且把当前语句的执行状态标记为 prepare, 然后告知执行器可以提交事务 -> 执行器记录 binlog 并提交事务 -> InnoDB 把刚才写入的 redo log 改成 commit 状态, 更新完成. Share 本周极客时间推出了年终学习总结, 虽然不太准, 但仍有参考意义. 我的总结内容是学习 31 小时, 超过 96% 的用户. 看到有位朋友分享出来的截图, 学习 2 小时, 超越 85% 的用户. 要知道, 极客时间是一款学习产品, 来到这里的人已经是有着极强学习意愿的人. 你看, 即便是在当前的大环境下, 即便是有着极强的学习意愿, 持续学习的人依旧是少数. 所以, 大可不必焦虑. 把焦虑的时间拿来学习, 然后努力成为前 10%, 或者加把劲儿成为前 5%. 生活自会越来越好. 想变得更优秀, 其实很简单. 找准自己的方向, 持续学习就完了. 当然, 此处数据略有偏颇. 毕竟极客时间现在还算是小众, 有着更多的人在使用其他的产品和方法在学习; 毕竟极客时间一直在策划各种活动, 主动或者被动引来了一大批僵尸用户. 虽是管中窥豹, 仍旧可见一斑.

2019/12/15
articleCard.readMore

我一个程序员, 心血来潮建了个写作群

没错, 我一个程序员, 心血来潮建了个写作群. 大学 大学生涯, 我经历的似乎只有失败二字. 大一上学期就开始挂科, 一直持续到毕业. 40 学分劝退, 曾一度挂到了 39 个学分. 学习上, 我无疑是失败的. 同班 29 人, 与我交好的只有 4 人. 同校 4 万人, 毕业后还有联系的, 屈指可数. 大学前, 整个学校都是我的好朋友, 大学后, 却和他们越走越远. 交际上, 我依然是失败的. 大学前, 我可以很自豪地说自己喜欢计算机, 说自己喜欢 DIY, 说自己喜欢轮滑. 大学后, 却一事无成. 就连兴趣爱好, 我都是失败的. 摸爬滚打两个月, 找到了全班最差的工作; 无可奈何签合同, 拿了同岗位最低的薪水. 甚至还被忽悠着贷款购买了两万块的 Python 课程. 找工作, 当然还是失败的. 欠下的债, 终究要自己来还 可惜我足够乐观. 因为乐观, 我曾过分高估了自己的处境, 也一度错过了改变处境的机遇. 庆幸我足够乐观. 因为乐观, 我没有因为过去的失败而气馁, 也可以微笑着向将来奋斗. 生活向来是公平的, 自己欠下的债, 终究要自己来还. 成长 最近一年, 得益于一名叫大蕉的程序员 和 极客时间 的熏陶, 自觉有一点点成长, 时常有颇多感悟. 终于, 有了主动学习的意识和行动, 也因此受益良多. 深受开源精神影响, 始终觉得有益的东西就该分享出去, 让有需要的人共同成长. 对不起, 第一次学着做师兄, 有些急促了 或许是起点真的很低, 初入社会, 看到什么都觉得是人间真理. 于是, 我迫切地想要把自己的所感所悟分享出去. 阴差阳错, 识得一位想要建立同乡会的小师弟, 见证了一个从 0 到 350 的团体的形成. 异乡异客, 虚无飘零, 得遇同乡, 欣喜若狂. 一厢情愿, 我开始在群里分享各种鸡汤和所谓的大道理, 并且渴望别人能有所回应. 但是啊, 所处环境不同, 人生阅历不同, 待人观物, 自是有不同的理解与感悟. 我所奢求的东西, 终究还是太多了点. 自以为白衣圣母, 在别人眼里, 或许只是跳梁小丑罢了. 新的尝试, 新的寄托 处境不同, 经历不同, 目标不同, 路径不同, 感悟不同. 那么我为什么不去找一些志同道合的人一起努力呢? 刚好最近参与的 ARTS 促使我一直在记笔记, 刚好最近计划的学习方法要求我持续写作, 刚好我害怕自己一个人可能会坚持不下去. 那就建一个写作群吧, 独立创作, 互相勉励, 一起成长, 想必是极好的. 以点带线, 以线绘面. 或许会有更多好玩的事情发生. 写作远不止写作那么简单 很多人止步于 “看见”, 以为 “看见” 了就是自己的. 殊不知仅仅 “看见” 还远远不够, 只有能够把 “看见” 的东西表达出来, “看见” 的才有可能内化为自己的. 所观, 所感, 所学, 所悟. 观其形容易, 感其意难, 学其表容易, 悟其道难. 写作, 就是在观, 学, 感, 悟之后, 重新塑造, 重新表达的过程. 写作, 远不止写作那么简单. 私心 时至如今, 我依然是有私心的. 通过文章, 我想看看你们眼中的世界. 通过交流, 我想看到你们的成长. 通过扩散, 我想看到正能量的传递. 加了一份淡然, 却依旧是私心. 解脱 此刻的我, 依然在肆无忌惮水群, 依然在努力开拓视野, 依然在咬牙攀登高峰, 依然在强行传播感悟. 如果暂时没能帮到别人, 不再气馁, 倘若能够助人丝毫, 幸甚至哉. 多一分恬适, 少千斤重担. 感谢 或聚或散, 感谢生命中遇到的每一个人; 或顺或逆, 感谢生命中走过的每一段路; 虽不善言, 但记于心.

2019/12/12
articleCard.readMore

ARTS-No.8

Algorithm 1266. 访问所有点的最小时间 思路: 这其实更像是一个数学问题. 因为需要按顺序访问所有点, 所以可以把问题简化为计算两个点之间的最小距离, 最后把每两个点之间的最小距离累加, 问题得解. 接下来要计算两个点之间的最小距离, 因为走对角线的效率是分别走横竖的两倍, 所以我们要尽可能多地走对角线. 那么两个点之间势必会形成一块而走对角线的正方形区域, 正方形的边长就是以两点为对角形成的长方形的较短的一条边. 剩余的距离一直横着或者竖着走即可. 问题得解. 代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Solution { public int minTimeToVisitAllPoints(int[][] points) { if (points.length <= 1) { return 0; } int total = 0; int[] last = points[0]; for (int i = 1; i < points.length; i++) { int[] current = points[i]; int x = Math.abs(current[0] - last[0]); int y = Math.abs(current[1] - last[1]); int italic = Math.min(x, y); int straight = Math.max(x, y) - italic; int move = italic + straight; total += move; last = current; } return total; } } 执行用时: 1ms, 内存消耗: 41.4MB. Review 声明!!! 水平以及精力有限, 下文可能存在很多问题错译, 仅做个人学习之用, 如果需要学习 Docker, 还请查阅其他资料!!! Get Docker Engine - Community for Ubuntu 卸载旧版命令 1 sudo apt-get remove docker docker-engine docker.io containerd runc 使用 Docker 仓库安装 Docker 升级 APT 安装包索引 1 sudo apt-get update 安装下列软件包以允许 apt 命令使用 https 仓库 1 2 3 4 5 6 sudo apt-get install \ apt-transport-https \ ca-certificates \ curl \ gnupg-agent \ software-properties-common 添加 Docker 官方的 GPG 秘钥 1 curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - 通过指纹验证秘钥已经正确添加: 1 sudo apt-key fingerprint 0EBFCD88 使用如下命令设置 Docker 稳定版仓库 1 2 3 4 sudo add-apt-repository \ "deb [arch=amd64] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) \ stable" 安装最新版本的 Docker 引擎社区版和 containerd 1 2 sudo apt-get update sudo apt-get install docker-ce docker-ce-cli containerd.io 至此, Docker 引擎社区办已经安装完成. 如果有需要, 也可以安装指定版本, 步骤请参考原文. 现在只能通过 sudo 执行 docker 命令, 如果想要通过普通账户运行 docker 命令, 需要把指定的用户添加进 docker 用户组 (这个操作会让指定用户获取 Docker 容器的 root 权限.): 1 sudo usermod -aG docker your-user 需要退出当前账户并重新登录来使上面的命令完全生效. Docker 还可以通过 .deb 文件或者 脚本文件安装, 具体步骤参见原文. 卸载 Docker 引擎社区版 卸载 Docker 引擎社区版 1 sudo apt-get purge docker-ce 删除 Docker 镜像, 容器, 卷, 配置文件等 1 sudo rm -rf /var/lib/docker Tip MySQL 中的 null 和 not null MySQL 中 null 值的检索 之前偶然间写了一个 SQL, 发现执行结果和预期不符, 最终发现是 null 的问题. MySQL 中 null 值是不能用 = 号比较的, 结果始终是 null. (此外, 使用 >, <, +, -, != 等操作符操作 null 结果始终是 null.) 下面语句的执行结果会是 null: 1 SELECT NULL = NULL; 1 SELECT NULL != NULL 那么怎么检索 null 值呢? 我们可以使用 IS 和 IS NOT 关键字或者 <=> 操作符或者 ISNULL() 方法: 1 SELECT NULL IS NULL 1 SELECT NULL IS NOT NULL 1 SELECT NULL <=> NULL 1 SELECT ISNULL(NULL) MySQL 中 null 和 空值 的区别 MySQL 中 null 是会占用空间的, 表示 “a missing unknown value”; MySQL 中 空值 是不占用空间的; eg: 空值 代表什么都没有, 而 null 值代表有一个空杯子, 虽然杯子里面什么都没有, 但空杯子还是会占用空间. 其他 使用排序时, null 会被当作是一个无穷小的值; 聚合函数 count(), min(), sun() 是忽略 null 值的, 但是 count(*) 例外; 在 MyISAM, InnoDB 和 MEMORY 引擎中, 是可以在包含 null 值的列添加索引的, 而在其它引擎中 not null 列才可以添加索引; 如果在一个 timestamp 列插入 null 值, 则当前时间会被自动插入; 如果在一个自增列插入 null 值, 则下一个自增值会被自动插入; 参考文献 MySQL NULL值处理详细说明 — zejin Share 实验 - 时间梳理 这周做了一个小实验, 刻意梳理并充分利用自己的时间, 看看最后会有什么变化. 欢迎围观 / 参考 / 同行.

2019/12/9
articleCard.readMore

ARTS-No.9

Algorithm 66. 加一 思路: 从最低位开始, 依次把当前位数字加一: 如果当前位数字加一后结果小于十, 直接返回当前数组, 程序结束; 如果当前位数字加一后结果等于十, 把当前位数字置为 0. 把当前位左移 1, 跳转到第 1 步; 如果第 1 步执行结束之后程序依然没有终止, 证明数字一直累加到了最高位, 那么当前数组长度已经放不下加一后的数字了. 重新声明一个长度加一的数组, 最高位置为 1, 其他位置按序填充原数组的内容即可. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Solution { public int[] plusOne(int[] digits) { int length = digits.length; for (int i = length - 1; i >= 0; i--) { if (++digits[i] < 10) { return digits; } digits[i] = 0; } int[] temp = new int[length + 1]; temp[0] = 1; for (int i = 1; i < temp.length; i++) { temp[i] = digits[i - 1]; } return temp; } } 执行用时: 0ms, 内存消耗: 35.1MB. Review 声明!!! 水平以及精力有限,下文可能存在很多问题错译,仅做个人学习之用,如果需要学习 Docker, 还请查阅其他资料!!! Post-installation steps for Linux 这个章节主要包含了 Docker 在 Linux 下的可选的优化配置. 使用非 root 用户管理 Docker Docker 守护进程绑定在 Unix 套接字而不是 TCP 端口. 默认情况下 Unix 套接字属于 root 用户, 其他用户只能通过 sudo 命令获得授权. Docker 守护进程经常使用 root 用户身份运行. 如果你不想通过 sudo 命令运行 docker, 可以创建一个 叫做 “docker” 的 Unix 用户组并且把用户添加到这个用户组. Docker 守护进程启动的时候会创建一个 docker 用户组下所有成员都有权限的 Unix 套接字. 警告: Docker 用户组拥有和 root 用户相同的权限. 操作流程: 创建 docker 用户组. 1 sudo groupadd docker 把用户添加进 docker 用户组. 1 sudo usermod -aG docker $USER 退出当前账户并重新登录, 当前账户的组成员信息会被重新加载. 如果你是在虚拟机中测试, 那么你可能需要重启虚拟机. 在 Linux 桌面版中, 你需要完全退出当前会话并重新登录. 在 Linux 中, 你也可以使用下面的命令激活用户组的变更: 1 newgrp docker 使用非 sudo 方法测试 docker 命令. 1 docker run hello-world 设置 Docker 自启动 当前大多数 Linux 发行版使用 systemd 来管理服务自启动. 可以使用这个命令设置自启动: 1 sudo systemctl enable docker 可以使用下面的命令禁用自启动: 1 sudo systemctl disable docker 设置 Docker 守护进程监听连接 默认情况下, Docker 守护进程会通过一个 UNIX 套接字监听来自本地客户端的连接. 可以通过配置使 Docker 监听 一个 IP 和 端口来接收远程主机的连接. 通过 systemd 单元文件配置远程访问 使用命令 sudo systemctl edit docker.service 在文本编辑器中打开一个 docker.service 的替代文件. 添加或者修改这几行, 替换成你自己的值 1 2 3 [Service] ExecStart= ExecStart=/usr/bin/dockerd -H fd:// -H tcp:://127.0.0.1:2375 保存文件. 重新加载 systemctl 配置. 1 sudo systemctl daemon-reload 重启 Docker 1 sudo systemctl restart docker.service 通过观察 netstat 的输出来确认 dockerd 在监听刚才配置的端口. 1 2 sudo netstat -lntp | grep dockerd tcp 0 0 127.0.0.1:2375 0.0.0.0:* LISTEN 3758/dockerd 通过 daemon.json 配置远程访问 设置 /etc/docker/daemon.json 中的 hosts 数组来连接 Unix 套接字和 IP 地址, 如下: 1 2 3 { "hosts": ["unix::///var/run/docker.sock", "tcp://127.0.0.1:2375"] } 重启 Docker. 通过观察 netstat 的输出来确认 dockerd 在监听刚才配置的端口. 1 2 sudo netstat -lntp | grep dockerd tcp 0 0 127.0.0.1:2375 0.0.0.0:* LISTEN 3758/dockerd 常见问题的解决方案 对症下药, 这里不转译了. 为 Docker 指定 DNS 服务 默认的配置文件是 /etc/docker/daemon.json. 你可以使用 --config-file 标记更改配置文件的位置. 下面的文档假设配置文件是 /etc/docker/daemon.json. 创建或者修改 docker 进程配置文件, 这个文件默认是 /etc/docker/daemon.json. 1 sudo nano /etc/docker/daemon.json 添加一个名为 dns 的键, 对应的值可以写一个或者多个 IP 地址. 如果这部分内容已经存在于文件中, 你只需要添加或者修改 dns 对应的那一行. 1 2 3 { "dns": ["8.8.8.8", "8.8.4.4"] } 如果你的内网 DNS 服务器不能解析公网 IP 地址, 请至少配置一个可以解析公网 IP 地址的 DNS 服务器. 以便于你可以连接到 Docker 中心并且你的容器可以解析公网域名. 保存并且关闭文件. 重启 Docker 进程. 1 sudo service docker restart 通过拉取一个镜像验证 Docker 可以解析外部 IP 地址. 1 docker pull hello-world 如果有需要, 可以通过 ping 一个内部 IP 地址来验证 Docker 是否可以解析内部 IP 地址. 1 2 3 4 5 6 7 docker run --rm -it alpine ping -c4 <my_internal_host> PING google.com (192.168.1.2): 56 data bytes 64 bytes from 192.168.1.2: seq=0 ttl=41 time=7.597 ms 64 bytes from 192.168.1.2: seq=1 ttl=41 time=7.635 ms 64 bytes from 192.168.1.2: seq=2 ttl=41 time=7.660 ms 64 bytes from 192.168.1.2: seq=3 ttl=41 time=7.677 ms 允许通过防火墙远程访问 API 如果你在运行 Docker 的同一台主机上运行了防火墙, 在 Docker 中启用了远程访问并且希望从另一台主机访问 Docker 的 API, 你需要配置防火墙来允许外部连接访问 Docker 端口, 默认端口在开启了 TLS 加密传输的情况下是 2376, 其他情况下是 2375. 两个比较常见的防火墙是 UFW 和 firewalld. UFW: 在 UFW 中进行如下配置: DEFAULT_FORWARD_POLICY="ACCEPT". firewalld: 把类似的规则添加到你的策略中(其中一条是入口规则, 另一条是出口规则), 一定要确认接口名字和链路名字是正确的. 1 2 3 4 <direct> [ <rule ipv="ipv6" table="filter" chain="FORWARD_direct" priority="0"> -i zt0 -j ACCEPT </rule> ] [ <rule ipv="ipv6" table="filter" chain="FORWARD_direct" priority="0"> -o zt0 -j ACCEPT </rule> ] </direct> Your kernel does not support cgroup swap limit capabilities 在 Ubuntu 或者 Debian 系统中, 你可能会在运行镜像的时候遇到类似的错误: 1 WARNING: Your kernel does not support swap limit capabilities. Limitation discarded. 这个警告不会出现在基于 RPM 的系统, 它们已经默认启用了这些功能. 如果你不需要这些特性, 你可以忽视这个警告. 或者你也可以通过下面的说明启用这些特性, 但是内存和交换会产生总内存的 1% 并且总体性能会降低 10%, 即使 Docker 没有在运行. 使用拥有 sudo 权限的账号登录系统 (Ubuntu 或者 Debian); 编辑 /etc/default/grub 文件, 添加或者编辑 GRUB_CMDLINE_LINUC 这一行并添加如下两个键值对: 1 GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount=1" 保存并关闭文件. 更新 GRUB. 1 sudo update-grub 如果你的 GRUB 配置文件有错误, 这里一步会发生错误. 在这种情况下, 你需要重复第 2 步和第 3 步. 这个设置会在系统重启后生效. Tip SpringBoot 单元测试事务回滚 SpringBoot 中如果要写单元测试只需要给测试类添加两个注解即可: 1 2 @SpringBootTest @RunWith(SpringRunner.class) 如果需要进行事务回滚, 可以通过添加 @Transitional 注解实现, @Transitional 可以添加到类或者方法上: 如果添加到方法上, 那么只对当前方法进行事务回滚; 如果添加到类上, 那么当前类的所有方法都会进行事务回滚; 在这种情况下, 如果要对个别方法提交事物, 可以在对应方法上添加 @Rollback(false) 注解来提交事务. Share 不尝试一下,你怎么知道自己不行? 记录了 ARTS 第一期的打卡总结, 因为文字较多, 独立写了一篇文章. 参考文献 SpringBoot实现单元测试时回滚事务 — Alphathur

2019/12/9
articleCard.readMore

实验-时间梳理

前言 毕业差不多一年了, 一年之中似乎每天都在寻求着进步, 一年之后又觉得其实自己并没有进步. 碌碌无为, 好像就是我每天不断重复的循环. 意识到这个问题之后便开始尝试破除当下的困境, 恰巧上周看到一篇文章: 如何利用碎片时间,提升自己的职场竞争优势, 感觉应该有用, 不仿就先试试文章中提到的时间梳理吧. 时间梳理-现状 以半小时为时间片段进行分割, 现阶段我的时间使用情况如下: 时间事件处理 7:00 - 7:30起床, 洗漱, 出门 7:30 - 8:00在公交车上发呆无效, 改为听音频 8:00 - 8:30在地铁上发呆无效,改为背单词 8:30 - 9:00工作超低效, 改为任务规划 + 分解 9:00 - 9:30晨会 9:30 - 12:00工作 12:00 - 12:30午饭 12:30 - 13:00玩手机无效 13:00 - 13:30午休低效 13:30 - 14:00午休 14:00 - 17:30工作 17:30 - 18:00工作 18:00 - 18:30逗留无效 18:30 - 19:00在地铁上发呆无效, 改为筛选公众号推送 19:00 - 19:30在公交车上发呆无效, 改为听音频 19:30 - 20:00吃饭, 看视频低效, 改为吃饭, 不看视频 20:00 - 20:30吃饭, 看视频无效 20:30 - 21:00ARTS 21:00 - 21:30玩手机无效 21:30 - 22:00ARTS 22:00 - 22:30散步 22:30 - 23:00洗澡 23:00 - 23:30玩手机无效 23:30 - 7:00睡觉 梳理完毕之后, 发现我每天有 6 个小时的无效 / 低效时间, 包括: 1 个小时的公交通勤, 因为个人不喜欢在公交车上玩手机, 就改为听音频吧; 1 个小时的地铁通勤, 相对公交车更稳一些, 可以用半个小时来背单词, 另外半个小时用来筛选公众号推送信息. 只做简单梳理, 标记出那些值得阅读的文章; 8:30 - 9:00 半个小时的不连续 / 超低效工作时间, 将其更改为新一天的任务规划拆解; 19:30 - 20:00 半个小时的低效用餐时间, 改为专心吃饭, 不看视频; 12:30 - 13:00, 13:00 - 13:30, 18:00 - 18:30, 20:00 - 20:30, 21:00 - 21:30, 23:00 - 23:30 一共 3 个小时的无效时间, 暂时预留一个小时的应变时间, 剩余两个小时就是我现阶段可以更加充分利用的时间. 时间再分配 时间梳理之后发现我每天有 2 个小时的可利用时间, 暂时没有直接在对应的时间段填充任务, 这样显得过于呆板. 万一没能在对应的时间做完对应的事, 很可能会引发负反馈, 不利于计划进行. 所以我给自己的标准是在该完成任务的时间内完成所有的任务, 时间片段自由安排. 现在坑已经挖好了, 一个萝卜一个坑也是确定的, 但是哪个萝卜种在哪个坑是随意的. 我要做的事情就是找出能在 2 个小时内种完的 4 个萝卜: 每天阅读半小时; 按照 “Java 学习路线图” 学习 Java 知识, 将知识点拆分成可以用 30 分钟查阅资料, 30 分钟整理重述的小知识点进行学习; 剩下的半个小时拿来阅读公众号推送, 按照往常的经验来讲, 每天收到的推送信息虽然很多, 值得阅读的文章却并不多, 半个小时时间应该是够的. Tips 时间梳理和任务拆解是分不开的, 每天的任务拆解就不在这里记录了, 希望我能做好; 既然是实验, 就得约定测试周期, 然后统计每个周期内的情况, 适当调整. 周期暂定为一个月吧. 2019 年 1 月 9 号, 希望能够以更好地姿态在这里出现; 时间拆解搭配番茄钟使用效果更佳, 暂时使用的是 Forest 这款工具, 一个番茄钟的时间设置为 25 min, 配合前面 30 min 的时间片段应该是刚好的. End. 追踪反馈 第一周 20191209 - 选定 Forest 作为番茄钟工具, 自我感觉良好. 只是有些时候会忘记开启番茄钟, 暂时要培养一下使用工具的意识; 20191210 - 背单词软件从 “有道背单词” 切换为 “不背单词”, 个人喜好, 不多赘述; 20191215 - 第一周总结, 本周执行最好的部分是背单词和看书, 基本上每天都会有固定的时间去做这两件事. 执行最差的部分是按照 Java 学习路线图规划学习, 并没有开始做这件事, 下周要改一下. 第二周 20191216 - 前面尝试了一周 Forest, 但是经常是 “为了用软件而用软件” 的状态, 只得其形, 不得其魂; 而且有时候是任务做了一半发现忘了开番茄钟, 有时候是番茄钟结束了但是任务差一丢丢做完, 总感觉没能用好番茄钟. 可能还是缺乏理论支撑吧, 这周会系统学习一下番茄钟的理论知识, 任务记录工具切换到了我更喜欢的 滴答清单. 20191221 - 还是有一点摸不着头脑, 比较好地坚持下来的就只剩下背单词和读书了. 20191222 - 学习了一下番茄钟的理论知识, 更换了另一个番茄钟工具 番茄土豆, 看看效果吧. 第三周 20191226 - 到南京出差了 3 天, 大部分时间都花在了交通上. 本周所有计划全线溃败.

2019/12/8
articleCard.readMore

ARTS-No.7

Algorithm 35. 搜索插入位置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Solution { public int searchInsert(int[] nums, int target) { if (nums == null) { return 0; } for (int i = 0; i < nums.length; i++) { if (target <= nums[i]) { return i; } } return nums.length; } } 执行用时: 0ms, 内存消耗: 38.5MB. Review 声明!!! 水平以及精力有限, 下文可能存在很多问题错译, 仅做个人学习之用, 如果需要学习 Docker, 还请查阅其他资料!!! Overview of Docker editions Docker 提供了三种版本: Docker Engine - Community 适用于个人开发者和小型团队尝试 Docker 和基于容器的应用. Docker Engine - Enterprise 考虑了安全性和企业级服务级别协议. 适合企业使用. Docker Enterprise 适用于构建, 传输, 并大规模运行生产中的关键 应用程序的企业或者 IT 团队. About Docker Engine - Community Docker Engine - Community 提供了三种更新渠道: Stable 提供最新版以供普遍使用. Test 提供预发布版本, 这些版本可以在正式发布前进行测试. Nightly 提供下一个主要版本的最新构建. Releases Docker 发行版的版本号格式是 YY.mm.<patch>. YY.mm 代表年月, 从 18.09 开始生命周期内每 6 个月发行一次. <patch> 是可能存在的附加信息, 比如 “pre-releases”. Nightly builds Night builds 会从主分支进行每日构建, 它的版本号采用如下格式: 1 0.0.0-YYYYmmddHHMMSS-abcdefabcdef 其中: 日期是最新提交的 commit 对应的 UTC 时间, 版本号的后缀是 commit hash 的前缀. 这些构建用于进行主分支最新代码的测试, 没有质量担保. Pre-releases 为了给新的 年-月 发行版做准备, 当 Docker 的主分支版本计划的里程碑功能已经完成时, 会从主分支创建一个 YY.mm 分支. 测试版和候选版本等预发行版是来自它们各自的发行分支. 补丁发布和相应的预发布来自相应的发布分支. 尽管进行了预发行版以协助稳定进程, 但是并不提供任何保证. 预发行版的二进制文件会以 test-YY.mm 格式的命名放置在测试通道. General availability 年-月 发行版是从主分支分离出来的发行分支创建的, 这个分支以 <year>.<month> 的格式创建. 年-月 名称表示期望最早发布的日历月, 所有进一步的补丁版本都从这个分支执行 这个发行版的二进制文件会以 stable-YY.mm 格式的命名放置在稳定通道. Relationship between Docker Engine - Community and Docker Engine - Enterprice code 对于给定的 年-月 版本, Docker 引擎同时发布企业版和社区版, Docker 引擎企业版是 Docker 引擎社区版代码的超集. Docker 维护团队会同时维护 Docker 引擎企业版的私有仓库和 Docker 引擎社区版的公共仓库. 一个自动化机器人会让社区版和企业版各分支代码保持同步, Docker 团队和他们的合作伙伴或尽可能把代码冲突保持最低并且会在一旦发生了冲突的情况下尽快解决. 支持 Docker 引擎社区版会在每年第一个 年-月 版本发布后对其提供 7 个月的支持, 企业版提供 24 个月的支持. 年-月 版本的生命周期结束以后, 对应分支可能会被删除. Docker 很重视安全问题, 支持 macOS 桌面版, Windows 桌面版和 CentOS, Debian, Fedora, Ubuntu 服务器版. Docker 承诺向后兼容. Tip 在这个信息泛滥的时代, 我们很容易就可以提升自己的精神层面, 但是想要改变自身处境并不简单. “道理我都懂, 但就是不想做.” 所以, 知行合一, 或许才能更 “实在” 地进步. Share 这周读到的一篇文章中说了职场中的 8 个逻辑, 感觉颇有道理, 稍加整理, 分享至此. 他人必有他人能耐之处 不要总是抱怨同事不行, 善于发现并发挥他人的优点, 虚心学习, 适当夸赞, 提建议时态度诚恳. 给发给你工资的主体创造价值 公司为你的劳动付费, 是因为你能为公司创造价值. 拿了钱就要给公司干活, 既然干了, 就把活干好. 在给公司创造价值的同时提升自我核心竞争力, 和公司一起成长. 管理者把功劳给团队, 个人把功劳给领导 功劳多分享, 困难多承担, 保持团队意识. 领导不是要问题, 而是要解决方案 不要总是找领导提问题, 要给出对应的解决方案并付诸实施. 你从哪个维度思考问题, 决定了你能上升的位置 思考问题的高度, 决定了你的上升空间, 努力扩大自己的上下文. 你想成为谁, 就先模仿谁. 先追赶, 再超越. 找准核心定位, 形成局部优势 找准核心定位, 形成局部优势, 形成核心价值. 不要反复诉苦, 要看结果 市场不相信眼泪, 职场只看重结果. 对事不对人 就事论事, 不要夹带个人情绪. 以上论点摘自 <陆晓明 | 如何利用碎片时间,提升自己的职场竞争优势>, 也穿插了一些我自己的感悟, 想要阅读原文的可以自行购买阅读.

2019/11/22
articleCard.readMore

ARTS-No.6

Algorithm 27. 移除元素 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Solution { public int removeElement(int[] nums, int val) { if (nums == null) { return 0; } int insertIndex = 0; for (int i = 0; i < nums.length; i++) { if (nums[i] != val) { nums[insertIndex] = nums[i]; insertIndex++; } } return insertIndex; } } 执行用时: 0ms, 内存消耗: 35.3MB. Review What is REST — A Simple Explanation for Beginners, Part 2: REST Constraints RESTful 接口需要满足如下六条约束: 统一接口 统一接口包含以下四个方面: 发送到服务器的请求必须包含资源标识; 服务器需要返回足够的信息, 以致于客户端可以操作对应的资源; 客户端的每一个请求需要提供包含服务器执行操作所需的全部信息; 服务器的每一个响应需要提供客户端需要的所有信息以便理解响应内容; 超媒体应作为应用程序状态引擎; 客户端 - 服务器分离 客户端和服务器是各自独立的, 它们之间的交互仅能通过客户端的请求和服务端的响应来实现. 服务端不会主动向客户端发送信息. 无状态的 无状态意味着服务端不会记忆调用 API 的用户的任何信息, 每一次独立的请求都包含了服务端执行对应操作需要的所有信息. 分层的系统 客户端和真正提供服务的服务端之间可能有很多类似安全层, 缓存层, 负载均衡层等中间服务, 这些中间层不应该影响客户端和服务器之间的请求和响应, 客户端也不会知道有多少分层. 可缓存的 服务端的响应需要标注自身是否可被缓存. 良好的缓存设计可以减少不必要的请求, 达到节约资源的目的. 按需代码 ( 可选 ) 客户端可以向服务端请求能够执行的代码, 代码通常是脚本的形式. 这个约束不作为 Restful 接口的的必要条件. 参考文献 【译】REST的6个约束 — 亚里士朱德 梳理REST API的设计原则 — 动力节点官方账号 Tip 「神器系列」软件终结者,没有它不能做的:uTools 这是一款和 Listary 相似的工具. 可以用作效率启动和文件搜索, 有点在于 uTools 拥有自己的插件系统, 如果插件生态做好的话优势会很明显. Share 这周接受了将近三个小时的保险推销, 成功从一个保险小小白变成了保险小白. 这里分享一下我了解到的保险基础知识. 保险保障的范围一般是包括三个方面: 身故, 健康和意外. 身故 保障身故的险种称为寿险, 保额取决于被保人所承担的家庭责任. 保额的确定需要考虑当下以及将来父母的赡养费, 子女的抚养费, 房贷和车贷等负担. 假如自己意外身故, 尽可能保证家人正常生活. 健康 保障健康的险种一般包括重疾险, 医疗险和社保. 社保 社保是现代人医疗的基础保障, 最大报销额度平均大概是 15 万元. 医疗险 医疗险是对社保的一个补充, 用来保障社保覆盖不到的范围. 年龄越大, 保费越高, 保期一般是一年, 有停售风险. 重疾险 重疾险用来保障重大疾病以及康复修养期间的收入损失, 目前国内重疾的治疗费用平均为 50 万元, 收入损失一般考虑 1 - 3 年的工作收入. 保额的规划原则大概 = (50 万) + (1 - 3 年工作收入) - (15 万社保报销额度). 意外 意外险用于保障各种意外情况, 比如车祸和意外伤残等. 最后补充一个原则: 保险的主要目的是规避风险, 不是理财. 不要被一些别有用心的分红险给忽悠了. 刚被洗脑, 在此记录. over!

2019/11/22
articleCard.readMore

修复 wine 微信黑色色块问题

前言 如果你看不懂标题, 那么恭喜你, 没有遇到这个心烦的问题. Linux 下使用 wine 微信有一定概率会出现烦人的黑色色块, 看它不爽, 却又无可奈何, 极大地影响了用户体验. 刚好在 github 看到了一个解决方案, 整理记录至此. 环境 编辑本文时笔者电脑的环境是: Ubuntu 19.10 deepin-wine-ubuntu 2018-12-ubuntu3 deepin-wine-wechat 其他环境请自行举一反三. 步骤 安装 xdotool 因为后面的脚本要使用到 xdotool 这个工具, 此处需要先行安装: 1 sudo apt-get install xdotool wine 微信启动脚本 新建脚本文件 “run-wechat.sh”, 写入如下内容: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 #!/bin/bash "/opt/deepinwine/apps/Deepin-WeChat/run.sh">/dev/null 2>&1 start_succ=false for i in {1..5} do xdotool search --onlyvisible --classname "WeChat.exe" if [ $? == 0 ] then start_succ=true break fi sleep 10 done if [ $start_succ == false ] then exit 1 fi windowclose=false while : do retval=$(xdotool search --onlyvisible --classname "WeChat.exe") if [ $? != 0 ] then exit 0 fi login=true for id in $retval do windowname=$(xdotool getwindowname $id) if [ "$windowname" == "登录" ] then login=false fi if [ $windowclose == true ] && ([ "$windowname" == "" ] || [ "$windowname" == "ChatContactMenu" ]) then xdotool windowclose $id fi done if [ $windowclose == true ] then exit 0 fi if [ $login == true ] then windowclose=true fi sleep 2 done 给脚本添加可执行权限: 1 chmod +x ./run-wechat.sh 此时脚本设置完成, 可以尝试执行此脚本. 脚本会在微信成功登录之后关闭黑色色块. 修改微信快捷方式 找到微信的快捷方式, deepin-wine-wechat 的快捷方式为 /usr/share/applications/deepin.com.wechat.desktop, 将 desktop 文件中 Exec 一行修改为: 1 Exec="${path}/run-wechat.sh" -u %u 其中: ${path} 表示 run-wechat.sh 脚本所在路径. 至此, 就可以通过快捷方式启动微信, 并且摆脱黑色色块的烦扰了. 参考文献 微信登陆后有一个窗口无法关掉 #207

2019/11/19
articleCard.readMore

ARTS-No.5

Algorithm 26. 删除排序数组中的重复项 解法一: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Solution { public int removeDuplicates(int[] nums) { if (nums.length <= 1) { return nums.length; } int scanIndex; int insertIndex = 1; int compareIndex = 0; int length = 1; for (scanIndex = 1; scanIndex < nums.length; scanIndex++) { if (nums[scanIndex] != nums[compareIndex]) { nums[insertIndex] = nums[scanIndex]; insertIndex++; length++; compareIndex = scanIndex; } } return length; } } 执行用时: 1ms, 内存消耗: 41.5MB. 解法二: 考虑到其实最后 length 和 insertIndex 的值是一样的, 那就…..删掉 length 呗. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Solution { public int removeDuplicates(int[] nums) { if (nums.length <= 1) { return nums.length; } int scanIndex; int insertIndex = 1; int compareIndex = 0; for (scanIndex = 1; scanIndex < nums.length; scanIndex++) { if (nums[scanIndex] != nums[compareIndex]) { nums[insertIndex] = nums[scanIndex]; insertIndex++; compareIndex = scanIndex; } } return insertIndex; } } 执行用时: 1ms, 内存消耗: 38.2MB. 解法三: 再想想 compareIndex, 其实也没啥必要. 毕竟是有序数组, 判断元素是否一样的话只需要比较当前元素和前一个元素就好了. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Solution { public int removeDuplicates(int[] nums) { if (nums.length <= 1) { return nums.length; } int insertIndex = 1; for (int scanIndex = 1; scanIndex < nums.length; scanIndex++) { if (nums[scanIndex] != nums[scanIndex - 1]) { nums[insertIndex] = nums[scanIndex]; insertIndex++; } } return insertIndex; } } 执行用时: 1ms, 内存消耗: 38.2MB. Review What is REST — A Simple Explanation for Beginners, Part 1: Introduction REST 是为 API 设计的一种风格规范, 于 2000 年由 Roy Fielding 在他的博士论文中提出. 和 REST 有关的两个术语: 客户端: API 的用户, 可以是一个人, 一款软件或者浏览器等; 资源: 资源是 API 可以提供信息的任何对象, 每一个资源都有一个独立的标识. 当 RESTful API 被调用时, 服务器提供客户端请求资源的状态. 当你想调用 RESTful API 的时候, 需要提供给服务器两样东西: 你所感兴趣的资源标识, 通常是资源的 URL; 你想让服务器对上述资源进行的操作, 经常使用的操作有 GET, POST, PUT 和 DELETE. Tip 修复 wine 微信黑色色块问题 发现了一个 Linux 下 wine 微信黑色色块的修复方法. Share 房东在 200 米外的楼下开了家便利店, 最近看到她经常发朋友圈说快递代收的事情. 大概就是非租户不代收, 租户的快递请当天领走等等. 我的房东是二手房东, 整个城中村不同的位置都有她的租户, 租户比较分散, 便利店也并不紧挨哪一栋出租楼. 这件事情引发了我的一个思考: 开了便利店的房东, 有必要为租户提供免费的快递代领服务吗? 首先从她的主业房屋租赁来看. 在客户决定要租赁一间房之前, 快递代领这项服务对客户最终决策的影响似乎微乎其微. 也就是说, 我是不是要租你这间房, 并不会因为快递租赁这项服务而改变. 在客户租赁一间房之后, 至少合同期内, 快递带领这项服务似乎也并不会影响客户终止或者继续租赁关系. 所以但从这方面来讲, 快递代领似乎并不会给房东带来任何利益. 其次是代领的角度. 代领这项服务本身是免费的, 但是代领的东西是有价值的. 如果发生了快递丢失的事情, 快递公司是不会买单的, 因为快递公司确实把快递送到了 “客户” 手中; 我想房东也是不会买单的, 毕竟她提供的服务是免费的, 我都没收你钱, 凭什么让我赔钱呢? 租户花了钱, 但是没拿到东西, 铁定也是不乐意呀. 这可能会引发矛盾, 反而不利于促进房东和租户之间的关系, 属于出力不讨好的行为. 想要不发生快递丢失的事情, 一是靠全民自觉, 二是靠严格监管. 害人之心不可有, 但是防人之心不可无, 所以指望别人自觉是不可靠的. 严格监管呢? 那就必须花费更多的精力去做这件本就没有利益的事情, 只能夸赞房东道德高尚了. 再从便利店的角度看. 十多平的便利店, 快递少的话还好, 如果多了, 会不会影响便利店客户的购物体验呢? 会不会进而影响便利店的生意? 当然不是所有事情都可以用利益衡量, 也或许不是房东的我体会不到房东的用意. 我想这些事情, 也纯属狗拿耗子, 多管闲事了. 你会怎么想呢?

2019/11/17
articleCard.readMore

ARTS-No.4

Algorithm 1108. IP 地址无效化 解法一: 直接调用 Java String 的 replaceAll() 方法, 因为该方法内部实现使用了正则匹配, 所以执行效率不高. 1 2 3 4 5 class Solution { public String defangIPaddr(String address) { return address == null ? null : address.replaceAll("\\.", "[\\.]"); } } 执行用时: 3ms, 内存消耗: 34.4MB. 解法二: 把 String 转换为 StringBuilder, 手动替换. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Solution { public String defangIPaddr(String address) { if (address == null) { return null; } StringBuilder sb = new StringBuilder(address); for (int index = sb.indexOf("."); index > -1; index = sb.indexOf(".", index + 2)) { sb.replace(index, index + 1, "[.]"); } return sb.toString(); } } 执行用时: 1ms, 内存消耗: 34MB. 解法三: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Solution { public String defangIPaddr(String address) { if (address == null) { return null; } StringBuilder sb = new StringBuilder(); for (int i = 0; i < address.length(); i++) { char c = address.charAt(i); if (c == '.') { sb.append("[.]"); } else { sb.append(c); } } return sb.toString(); } } 执行用时: 0ms, 内存消耗: 34.1MB. Review SOLID Principles: Explanation and examples SOLID 是由 Robert C.Martin 提出, 由 Michael Feathers 定义的面向对象编程的让程序更易于理解, 维护和扩展 5 个基本原则的首字母缩写. 这 5 个原则分别是: S - Single responsibility principle 单一职责原则: 一个类或者一个模块只应负责一项职责, 只能因为一个动机被改变. O - Open/closed principle 开放封闭原则: 包括模块, 类, 方法等在内的实体应该对扩展开放, 对修改闭合. L - Liskov substitution principle 里氏替换原则: 父类的实例应该随时可以被子类的实例替换而不会引起程序错误. I - Interface segregation principle 接口隔离原则: 任何一个客户不应被强迫依赖一个它不会使用的方法. 或者说, 不要通过添加新方法向现有接口添加新功能. D - Dependency inversion principle 依赖倒置原则: 依赖倒置原则是程序解耦的一种方法, 依赖倒置原则要求: 高层模块不应该依赖于低层模块, 它们都应该依赖于抽象层; 抽象不应该依赖细节, 细节应该依赖抽象. 通过这 5 个原则, 可以让我们的程序变得可重用, 可维护, 可扩展并且易于测试. 重述 之前对 SOLID 原则的认识只在 “听过” 的程度, 直接阅读英文文档可能造成理解偏差, 所以又参考了几篇中文文章, 对其中的原则简单重述. 单一职责原则 一个类只应承担一种责任. 换句话说: 让一个类只做一件事. 如果需要承担更多的工作, 那么分解这个类. 开放封闭原则 实体应该对扩展是开放的, 对修改是封闭的. 即: 可扩展, 不可修改. 里氏替换原则 一个对象在其出现的任何地方, 都可以用子类实例做替换, 并且不会导致程序的错误. 换句话说: 子类应该可以在任何地方替换父类且程序功能不受影响. 接口隔离原则 客户不应被强迫依赖它不使用的方法. 即, 一个类实现的接口中, 不应该包含它不需要的方法. 将接口拆分成更小和更具体的接口, 有助于解耦, 从而更容易重构和更改. 依赖倒置原则 高层次的模块不应依赖低层次的模块, 他们都应该依赖于抽象; 抽象不应依赖于具体实现, 具体实现应依赖抽象. 参考文献 面向对象的SOLID原则 Tip 这周发现了 IDEA 的又一款神级一键部署插件: Cloud Tookit , 简单配置之后, 便可以开启一键部署之旅. 中小型应用福音, 强烈建议尝试!!! Share 本周的review主题来自郑晔老师的《10x程序员工作法》系列课程, 课程中老师有提到: 设计模式是“术”,设计原则是“道”。 如果说设计模式是“术”,设计原则才是“道”。设计模式并不能帮你建立起知识体系,而设计原则可以。 道和术,是每个程序员都要有的功夫,在“术”上下过功夫,才会知道“道”的价值,“道”可以帮你建立更完整的知识体系,不必在“术”的低层次上不断徘徊。 不禁想起之前看过的一篇文章: 请停止学习框架. 文章叙述了类似的观点, 如果学习框架是学习 “术”. 那么基础知识就是 “道”. “术” 可能会过期, 但”道”不会. 回想我们的学习生涯, 如果缺乏 “术” 的应用, 往往很难理解 “道” 的精髓; 如果缺乏 “道” 的支撑, 很难发挥 “术” 的极致. 道术结合, 才能更加稳健地成长.

2019/11/13
articleCard.readMore

wine 应用程序全局快捷键无效的解决方案

前言 在 Ubuntu 下我们经常会使用 Wine 或者 Crossover 运行 Windows 应用程序. 当应用程序切换到后台时, 是无法响应预设的全局快捷键的. 比如”打开微信”的快捷键 ctrl + alt + w 在这种情况下就无法响应. 我们可以借助一个小工具 xdotool 来解决这个问题. 注: 此方法在 Ubuntu 17, 18, 19 全系列测试通过, 其他平台未作测试, 理论通用; 本文以 “打开微信” 快捷键为例, 其他应用以此类推; 方法 1. 安装 xdotool 直接在命令行运行以下命令即可: 1 sudo apt install --no-install-recommends xdotool 2. 编写 xdotool 脚本 思路: Wine 应用在后台无法接收到快捷键状态, 此时借助 xdotool 向 Wine 应用发送模拟按键信息即可. 在合适的位置新建一个脚本文件 “open_wechat.sh”, 写入以下内容: 1 2 3 4 #!/bin/sh #在当前运行的应用中找到名为WeChat.exe的应用程序,并向它发送按键事件"ctrl+alt+W" #WeChat的可执行文件名为WeChat.exe,如果是其它应用程序就修改成其它应用程序的可执行文件名, 应用名称大小写敏感, 一个字母都不能错! xdotool key --window $(xdotool search --limit 1 --all --pid $(pgrep WeChat.exe)) "ctrl+alt+W" 赋予脚本可执行权限: 1 chmod +x open_wechat.sh 如果此时你的微信正好运行在后台, 执行这个脚本就可以把它召唤到前台. 如果没有, 请检查脚本是否有错误. 3. 设置快捷键 图形界面依次打开 “设置” -> “设备” -> “键盘”, 点击列表最底部的 “+” 号添加自定义快捷键. 名称随便, 填写 “打开微信” 即可; 命令填写刚才编写的脚本的全路径; 快捷键设置自己想用的快捷键即可, 建议于应用内部快捷键相同; 最后点击”添加”即可. 4. 验证 到这里已经设置成功了, 打开微信, 切换到后台, 然后按下刚才设置的快捷键就能召唤应用至前台. 如果不能, 请检查自己前面的设置是否有误. 后记 其他应用以此类推, 还是那句话, 之后的事情对于敢玩 Ubuntu 的你只能是小菜一碟了, 加油! Q&A 如何获取 Wine 应用名称? 先启动目标应用, 然后使用 ps 命令查看即可, 运行: 1 ps aux | grep wine 从中查找自己的应用名称. 致谢 这个方法是一个 QQ 交流群中名为 “简单” 得大佬教给我的, 略加整理, 发布至此. 感谢大佬的贡献.

2019/11/9
articleCard.readMore

ARTS-No.3

Algorithm 771. 宝石与石头 解法一: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Solution { public int numJewelsInStones(String J, String S) { int total = 0; StringBuffer oldString = new StringBuffer(S), newString = new StringBuffer(); for (int j = 0; j < J.length(); j++) { char current = J.charAt(j); for (int s = 0; s < oldString.length(); s++) { char stone = oldString.charAt(s); if (stone == current) { total += 1; continue; } newString.append(stone); } oldString = new StringBuffer(newString); newString = new StringBuffer(); } return total; } } 执行用时: 4ms, 内存消耗: 35.7MB. 解法二: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Solution { public int numJewelsInStones(String J, String S) { int total = 0; for (int j = 0; j < J.length(); j++) { char current = J.charAt(j); for (int s = 0; s < S.length(); s++) { if (current == S.charAt(s)) { total++; } } } return total; } } 执行用时: 2ms, 内存消耗: 34.6MB. 解法三: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Solution { public int numJewelsInStones(String J, String S) { int total = 0; char[] JArray = J.toCharArray(); char[] SArray = S.toCharArray(); for (char j : JArray) { for (char s : SArray) { if (s == j) { total++; } } } return total; } } 执行用时: 1ms, 内存消耗: 34.5MB. 解法四: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Solution { public int numJewelsInStones(String J, String S) { if (J.length() == 0 || S.length() == 0) { return 0; } int total = 0; for (char s : S.toCharArray()) { if (J.indexOf(s) != -1) { total++; } } return total; } } 执行用时: 1ms, 内存消耗: 34.7MB Review Announcing Stack Overflow for Teams 为了满足个别用户隐私提问的需求, Stack Overflow 开启了一个叫作 Strack Overflow for Teams 的付费功能, 相当于是一个小型的 Strack Overflow, 用户的提问只会被自己团队, 公司或者组织成员看到. 背景: 几乎每个开发团队都需要找寻一种从一开始就建立起过往知识库的解决方案, 便于让每个人检索. 比如新加入团队的员工, 转入新项目的老员工, 抑或是忘记了自己多年前解决过的问题的人. 有一段时间人们认为 wiki 会是这个问题的答案. 但是 wiki 有时候更像是一种写文档的家庭作业, 前人可能要花费很大精力去整理 wiki, 但是并不知道这份 wiki 是否真的会帮到别人. 人们提到的另一个解决方案是 IRC 风格的网上聊天室, 这个产品希望用户在历史聊天记录中找到问题的答案, 但是用户找到的往往是一大堆繁杂的聊天记录. 所以 Stack Overflow 这种问答形式的社区才会兴起. 你在上面查找一个问题, 绿色对号标注的地方往往就是你要的答案. Tip wine 应用程序全局快捷键无效的解决方案 Share Inoreader 一款在线 RSS 订阅系统, 可以用来订阅喜欢的博客, 集成在一起阅读.

2019/11/5
articleCard.readMore

月刊-201911

见闻 61岁中国大爷靠“玩”木头火了,网友:简直是宝藏爷爷! 文章介绍了一位把传统手艺玩成精的木工爷爷. 当擅长和热爱指向一处, 往往能发挥出无穷潜力. 另, 网络以及自媒体的普及, 确实让真正有才华的人被看见. Java 之父:你至少得会两门语言 文章介绍了 Java 之父 James Gosling 的传奇经历, 文中有段话我觉得十分优秀: 你投入到程序中的精力越多,用户使用时遇到麻烦和折腾的精力就会越少,程序员要做的,正是在开发端和用户端之间尽量平衡开发和用户使用的复杂度。 生活中的 12 条法则 别人的生活永远都不会是你的,你只能去改变自己的人生。 你不可能让所有人满意 很多时候是想等一个完美的时机做完美的事情, 然后一次次在无止境的等待中错过. 体面人罗永浩 谁都能看到 “体面” 的罗永浩, 鲜有人会留意艰难的供应商. 刻意练习:从“知道很多道理”到“过好这一生”的必杀技 文章论述了”刻意练习”的作用. 不要轻易放弃自己的尊严 从小到大,做事不都应该先分对错,再看利弊么? 想要对等交流,需要你的能力匹配你的能力圈。 尽管赚钱很好。但拥有有意义的工作和人际关系要比赚钱好得多。 所有的想不通,都是因为你不懂 有些事情, 有些人, 劝说是没有用的, 只有经历过, 才能懂. 开始比计划重要,完成比完美重要 No.172 很多时候,开始真的比计划更重要,完成比完美重要。 工具 LightPDF 在线PDF编辑器及转换器. md-ppt 一款可以像 PPT 一样放映 Markdown 文件的小工具, 效果不太好, 但是想法很不错. 不夸张,高效信息聚合阅读“神器”:「快知」 一个信息聚合的 APP, 看起来不错. Molunerfinn/PicGo 一款跨平台的图床客户端. 技巧 这几个 IntelliJ IDEA 高级调试技巧,你用过吗? 文章介绍了 IDEA 调试的一些高级技巧. 如何科学的打开 Leetcode 文章介绍了刷 Leetcode 的一种思路. 老大,你为什么在代码中要求我们使用LocalDateTime而不是Date? Java 中日期类使用的一些建议. Spring Boot干货系列:(十二)Spring Boot使用单元测试 关于 SpringBoot 单元测试的一片很赞的文章. 科普 你所不知道的 AI 进展 文章介绍了现今 AI 领域的发展水平.

2019/11/3
articleCard.readMore

部署 Hexo 博客到 VPS

前言 Hexo + Github Pages 本是绝顶搭配, 无奈最近国内访问速度越来越慢, 实在不忍直视. 虽说国内也有 Gitee.com 和 Coding.net 提供类似服务, 但都不完全免费, 甚至还有广告, 不在考虑范围之内. 最终只好忍痛买了个云主机, 开启博客搬迁之旅. 准备 公网可以访问的 VPS 一台; 可以正常使用的 Hexo 博客; 我这边的服务器环境是 Ubuntu 18.04 LTS 64 位, 下面的操作也都以此为准. 步骤 登录服务器 使用自己熟悉的工具登录服务器, Unix 环境下直接使用 ssh 命令 ssh user@host 即可, 其中 “user” 代表服务器用户名, “host” 代表服务器 ip. 安装 git 和 nginx 在服务器上运行以下命令即可: 1 2 sudo apt-get update sudo apt-get install git-core nginx 配置 nginx 创建 “/var/www/hexo” 目录用于存放博客静态文件: 1 mkdir /var/www/hexo 修改 nginx 配置文件: 1 vim /etc/nginx/conf.d/hexo.conf 写入以下内容: 1 2 3 4 server { listen 80; root /var/www/hexo; } 重启 nginx: 1 sudo systemctl restart nginx.service 配置 Git Hooks 创建 Git 裸仓库: 1 2 3 mkdir ~/hexo.git cd ~/hexo.git git init --bare 其中: hexo.git 作为远程 git 仓库, 用于接收本地 Hexo 生成并 push 过来的静态文件. 配置 Hooks 脚本: 1 vim ./hooks/post-receive 填入以下内容: 1 2 3 4 #!/bin/bash rm -rf /var/www/hexo git clone /root/hexo.git /var/www/hexo 给脚本赋予可执行权限: 1 chmod +x ./hooks/post-receive post-receive 脚本会在 hexo.git 仓库接收到 push 动作时执行. 脚本执行的命令也很简单, 先删除 /var/www/hexo 目录, 再将 hexo.git 中最新的博客文件复制到 /var/www/hexo 目录下. 部署 Hexo 博客 回到本地, 修改 Hexo 配置文件 “_config.yml”: 1 2 3 deploy: typr: git repo: user@host:hexo.git 其中: “user” 代表远程主机用户名, “host” 代表远程主机 ip, “hexo.git” 是我们刚才在服务器建立的仓库. 本地执行命令: 1 2 hexo clean hexo g -d 即可将博客发布至服务器. 发布完成后, 通过 “http://host“ 接口访问博客. That’s All.

2019/11/2
articleCard.readMore

ARTS-No.2

Algorithm 整数反转 题解: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Solution { public int reverse(int x) { int result = 0; int temp; while (x != 0) { temp = x % 10; if (result > Integer.MAX_VALUE / 10 || (result == Integer.MAX_VALUE / 10 && Integer.MAX_VALUE - result < temp)) { return 0; } if (result < Integer.MIN_VALUE / 10 || (result == Integer.MIN_VALUE / 10 && Integer.MIN_VALUE - result > temp)) { return 0; } result *= 10; result += temp; x = x / 10; } return result; } } 执行用时: 1ms, 内存消耗: 33.7MB Review The 5 Whys Process We Use to Understand the Root of Any Problem 5个为什么常被用来查找问题的根源; 5 个为什么的目的不是为了责备; 5 个为什么的 5 个主要步骤: 邀请所有被问题影响到的人; 选出一个会议领导者 (实际上每个人都可以担任这个角色); 问出 5 次为什么; 如果问题出现分支, 只选择其中的一个分支继续深究; 如果同一个问题再次出现, 我们可以选择其他分支; 最后, 我们会根据 5 个为什么找寻到问题的原因和解决问题的步骤; 对讨论得出的解决方案进行分工; 把会议结果发送给公司所有人. Tip lightpdf - 在线PDF编辑器及转换器. Share 部署 Hexo 博客到 VPS

2019/11/2
articleCard.readMore

Windows 软件推荐

前言 分享一些自己喜欢的 Windows 软件, 希望能够帮助大家提高工作 / 学习效率. 系统安装 这里只介绍我喜欢用的工具, 不作深入探讨. WePE 简洁纯净的 PE 系统. 系统 纯净 Windows 镜像. 驱动 建议到主板官网下载, 不推荐使用 驱动精灵, 驱动人生 等流氓软件. 软件安装 系统增强 Chrome - 浏览器 自认为是目前市场上最强大的浏览器, 不作过多解释. Chrome 有着强大的插件库, 请自行研究. Listary - 效率启动/快捷搜索 Listary 是一款 Windows 下的效率启动与快捷搜索工具. 可以极大得提升工作效率. 我之前写过一篇关于 Listary 的介绍 - Listary – Windows 文件浏览与效率搜索工具, 可供参考. Snipaste - 截图+贴图 Windows 下当之无愧的最佳截图工具. 操作方法参见: 基础操作-Snipaste 7zip - 文件压缩/解压 功能强大的文件压缩/解压工具. GeekUninstaller - 软件卸载工具 用于卸载软件并完全清除文件残留. Everything - 效率搜索 能够以极快的速度找到你想要的文件. 安全工具 火绒 - 安全防护 我个人的话感觉是没必要使用杀软的, Windows 自带的 Defender 已经够用了. 但是考虑到如果我不推荐的话有人可能会安装 360 等流氓软件, 索性就写在这里吧. 日常办公 Office 不多说了, 自行安装. Typora - Markdown 编辑器 一款功能强大的 Markdown 编辑器, 适用于各种写作场景. 印象笔记 - 笔记应用 多终端同步, 用于记录一些学习或者工作笔记. Calibre - 电子书 强大的电子书阅读 / 管理 / 转换工具. 聊天工具 TIM 腾讯推出的精简版 QQ, 添加了一些办公场景, 拥有极致简单的体验, 可以完美替代 QQ. WeChat 这个不用过多介绍了. XmindZen - 思维导图 简洁清新的思维导图工具, 极力推荐! 影音视频 不要安装各种影视软件!!! 在线观看的用户体验不够好吗? ACG 播放器 - 本地视频 本地视频播放神器, 支持倍速播放. 网易云音乐 - 音乐播放 网易云音乐将 “云” 这个概念发挥得淋漓尽致, 是目前为止我个人最喜欢的音乐软件. 其他 至此, 上述软件已经完全满足日常办公娱乐要求. 下面会介绍一些好玩好用好看的其他软件给少部分有需求的人们. 多媒体 ScreenToGif 屏幕, 摄像头和白板录像机与集成编辑器. 可用于录制屏幕、摄像头或者编辑 Gif 动画. 免费开源, 不含广告. 桌面美化 MyDock + MyFinder - 仿 Mac 在 Windows 上完美还原 Dock 和 Finder. 扩展资料 Awesome-Windows Windows 极致软件列表.

2019/10/27
articleCard.readMore

ARTS-No.1

Algorithm Two Sum 解法一: 暴力循环 1 2 3 4 5 6 7 8 9 10 11 12 13 class Solution { public int[] twoSum(int[] nums, int target) { for (int i = 0; i < nums.length - 1; i++) { int temp = target - nums[i]; for (int j = i + 1; j < nums.length; j++) { if (nums[j] == temp) { return new int[]{i, j}; } } } return null; } } 执行耗时: 17ms, 内存消耗: 37.4MB 解法二: 借助 map 1 2 3 4 5 6 7 8 9 10 11 12 13 class Solution { public int[] twoSum(int[] nums, int target) { Map<Integer, Integer> map = new HashMap(nums.length); for (int i = 0; i < nums.length; i++) { Integer value = map.get(nums[i]); if (null != value) { return new int[]{value, i}; } map.put(target - nums[i], i); } return null; } } 执行耗时: 2ms, 内存消耗: 37.4MB Review How to think like a programmer — lessons in problem solving 文章介绍了一种解决问题的有效框架: Tip crossoverJie/blog.toolbox - 博客工具箱 因为之前使用的博客图床挂掉了, 所以选用这个工具批量替换了图床. 纯 Java 代码, 需要自定义的地方可以自己实现. Share 分享一些自己喜欢的 Windows 软件: Windows 软件推荐

2019/10/25
articleCard.readMore

月刊-201907

优秀文章 这十七个学习网站,值得你去浪费时光 文章总结了一些优秀的学习网站. Java8 lambda表达式10个示例 关于 Java lambda 表达式的一篇文章. 工具推荐 uupoop - 在线 PS 工具 信息来源: 买不起正版PS?试试这个免费的在线工具! DownGit | github Create GitHub Resource Download Link (git-github-direct-zip-directory-folder-file) gitMemory 一个帮助你可视化查看某一 github 账号近期动态的工具. 犸良 一个在线制作动效的小工具. 教程 mixkit 一个高质量插画图库, 使用时需要注意版权. 有趣的项目 程序员的婚礼邀请函 | github 颇具程序员风格的婚礼邀请函. Dollar Street 足不出户, 阅尽人间百态. Arch 配置教程 Arch Linux 的一个配置教程.

2019/7/7
articleCard.readMore

月刊-201906

优秀文章 YC 掌门人 Sam:7 种特质帮你成功 我的透明创业实验 一个正在进行的创业实验,一年时间做10个小产品,看看能否达到每月被动收入1000美元的目标。 工具推荐 用好这几个工具,能大幅提升你的 Git/GitHub 操作效率! 文章介绍了三款工具: gitignore.io 用于快速生成 .gitignore 文件. readme-md-generator 用于快速生成 readme 文件. repository templates Github 新推出的模板仓库功能. 如何在 GitHub 上找到免费且实用的软件? 文章分别介绍了 macOs, Windows 和 Linux 的 awesome 软件清单: iCHAIT/awesome-macOS Awesome-Windows/Awesome luong-komorebi/Awesome-Linux-Software uppy | github The next open source file uploader for web browsers. koken 一个搭建个人图片库网站的软件. i18ns 国际化翻译搜索工具, 制作国际化应用的利器. 图形计算器 该网站左侧输入公式,右侧自动输出图像,非常直观。 参考文献 每周分享第 60 期(20190614) — 阮一峰

2019/6/19
articleCard.readMore

月刊-201905

优秀文章 电子邮件,应该是这样写滴(n个好习惯让我们避免互相伤害) 写邮件的一些注意事项, 算是基础的办公技能 / 礼仪吧. 数据库怎么分库分表,垂直?水平? 文章分享了一些数据库分库分表的看法. 性能优化指南:性能优化的一般性原则与方法 完备的 AI 学习路线,最详细的资源整理! 工具锦集 cloudconvert 支持 218 种文件的格式转换. 在 Linux 下截屏并编辑的最佳工具 文章推荐了 Linux 下的几种常用的截图工具. carbon 一个给代码生成高亮截图的工具. IpAddress.com 可以用来查询网站的 IP 地址. Kdenlive 一个功能强大的开源的视频编辑器,主要针对 Linux 系统,但是 Mac 和 Windows 也可以安装. 如何把在公司电脑上复制的内容,粘贴到家里的电脑?超好用! 本文科普了一个云剪切板的概念, 因为自己主要使用 Ubuntu 系统, 所以我找到了一个 chrome 扩展来实现相同功能: Clip Could. Chartify 一个很不错的图表生成器. Markdown 转微信公众号排版 Markdown Nice | github WeChat Format | github Md2All 视频下载 Annie you-get youtube-dl 资源推荐 qianguyihao/Web Web前端入门和进阶学习笔记,超详细的前端学习图文教程。从零开始学前端,做一个Web全栈工程师。持续更新… Effective Java 第三版电子书 OpenStreetMap OpenStreetMap 是一个协作项目,用于创建可自由编辑的世界地图。它创立于2004年,目前拥有超过一百万社区成员参与编辑,已经有数千个网站、手机 App 和硬件设备上使用了它。它是唯一真正不受限制,可以自由使用的全球地图服务。 easychen/howto-make-more-money 程序员如何优雅的挣零花钱. weilanwl/ColorUI 鲜亮的高饱和色彩,专注视觉的小程序组件库. leon-ai/leon Leon is your open-source personal assistant. ZhuPeng/mp-githubtrending 微信公众号对外链的访问有严格的控制,文章中除特殊的链接都不能点击,但是对小程序的跳转是没有限制的。故提供如下能力,在公众号文章中可以插入 GitHub 仓库的小程序链接,方便访问仓库的详情提升阅读体验。 GridManager | Github 快速、灵活的对Table标签进行实例化,让Table标签充满活力。 今日掘金 每15分钟爬取一次掘金首页,筛选出前10篇点赞数大于55的文章. Learn X in Y minutes 在 Y 分钟内速成 X, 可以用来快速了解一门语言. manypixels 一个收集无版权插图的网站,提供 SVG / PNG 格式下载,并且允许更改颜色. instagram.css 一个 CSS 开源库,提供纯 CSS 的 Instagram 滤镜实现. Public APIs 这个网站收集了大量公开的 API, 让开发者可以快速集成到自己的产品中. zhui 一个前端工程师在 GitHub 上开源的中国风 React 组件库. 金句 软件、电影、游戏行业都具有可扩展性,作品的生产成本是固定的,但可以被消费无数次,所以有巨大的获利空间,创造出许许多多的富豪。另一方面,理发师、厨师、出租车司机一次劳动,只能服务少数几个人,就不具有可扩展性,很难获得暴利,生存得很辛苦。 — 阮一峰 代码是服务于业务的,也许是服务于最终用户,也许是服务于其他程序员。 — xybaby 大多数时候我们都知道代码要服务于最终用户, 却很容易忽略其他程序员. 高手都是花80%时间思考,20%时间实现;新手写起代码来很快,但后面是无穷无尽的修bug. — xybaby 个人收藏 AI王者局开黑与人类战队打成平手:腾讯王者荣耀AI首次亮相 文中介绍了王者荣耀 AI 的相关知识, 另外附了一篇论文: Hierarchical Macro Strategy Model for MOBA Game AI. 拒绝视觉中国,这些图库一年帮你省十几万 文中列举了一些拥有 CC0 协议的图片网站. 精致独居青年必备的20款APP 文中罗列了一些适合年轻人的APP. 最权威的免费学术翻译工具,帮你写出专业级的英文学术文章 参考文献 每周分享第 55 期(20190510) — 阮一峰 《HelloGitHub》第 36 期 — HelloGitHub

2019/5/3
articleCard.readMore

月刊-201904

优秀文章 基于MySQL数据库下亿级数据的分库分表 文章结合具体业务场景介绍了 MySQL 数据库下亿级数据的分库分表方案. Mysql高性能优化规范建议 如题. Linux发行版该如何选择 关于 Linux 各种发行版的一篇科普文章. 资源推荐 中国色 《色谱》颜色整理. 1c7/chinese-independent-developer 中国独立开发者项目列表 – 分享大家都在做什么. Papers With Code 该网站按照计算机的不同应用领域进行划分,收集并整理了众多开源项目,每个项目都包含着相关论文供学习参考。 项目分类涵盖了计算机视觉、自然语言处理、医学、方法论、语音、游戏、机器人等多个领域。你可以通过网站提供的排序与搜索功能,快速检索到你想要进行学习的项目。 GitHub 上的这几个项目或许能帮你学好英语 everyone-can-use-english 前新东方英语老师李笑来最近在 GitHub 上开源的一本电子书籍。 A Programmer’s Guide to English | 在线阅读 这是国内一名程序员根据自己的英语学习经历,所编制的英语学习指南。 English-level-up-tips-for-Chinese | GitBook 这份指南主要通过六篇文章(认知篇、单词篇、听力篇、阅读篇、口语篇、扯淡篇),来向我们多方面讲解英语学习过程中一些学习技巧及注意事项。 chinese-programmer-wrong-pronunciation 中国程序员容易发音错误的单词. 译学馆:一个边看TED边学英语的平台 | 译学馆 一个超赞的英语学习平台! 我 叫 陈 耀 军 无意间发现的一个很有意思的个人网站, 作者对自己的定位是: 一个健康的生活在自由的去中心化网络世界里的独立大龄程序员,独自游走在传统文化和开源技术的两端。 网站罗列了作者的很多项目, 找不到好用的工具? 那就自己写一个! 这就是我心中一个程序员最帅的样子. 工具推荐 image-conversion 在线图片压缩,可指定图片大小压缩图片. kezhenxu94/house-renting 租房信息聚合爬虫. 地图搜租房  聚合各大网站的租房信息并在地图显示. YEELOGO 一个在线制作 logo 的小工具. 易词云 在线生成词云. 数据库文档申城工具 wkiny / 数据库文档生成工具 可以生成 HTML, Excel 格式的数据库文档, 需要连接数据库. 暂时只支持 MySQL 数据库, 可以实现接口支持其他数据库. 2liang/AutoBuildDocFromDB 使用 SQL 文件自动生成 markdown 格式的数据库文档, 不需要连接数据库. 脚本使用 python 编写, 可以通过修改源代码定制自己想要的文档格式. 视频编辑工具 carykh/jumpcutter 它能够通过识别视频中的 👍👎手势对视频进行自动剪切,删掉无用片段,还可以通过声音识别,自动加速或剪切视频,堪称神器! smacke/subsync 它能通过对声音的识别自动匹配到视频字幕,你需要做的,就是确认匹配结果的准确度即可。 参考文献 程序员一般喜欢浏览哪些网站? — GitHubDaily 推荐两个 GitHub 上开源的视频编辑神器 — GitHubDaily

2019/4/2
articleCard.readMore

月刊-201903

+ 优质文章 为什么Python不用设计模式? 文章用故事的形式, 幽默诙谐地道明了 python 与 java 的不同之处. 看完之后最大的感悟是: 学设计模式, 一定要透彻理解其精髓, 运用了什么方法, 解决了什么问题. 只有明白了原理, 才能活学活用. 其他知识也是如此. 阿里毕玄现身说法:程序员成长路线 阿里大神毕玄分享的成长路线, 可以参考. 苹果发布会预示了什么? 苹果推出订阅式内容服务,而且特别强调,不会根据你的喜好和算法来给你个性化推荐,这其实表明了一种态度,对于有明辨能力的,对于相对高层次的人群,有能力知晓自己感兴趣的点,自己选择优质渠道订阅优质信息,而不用接受平台给你推荐的垃圾爽文,不用沉浸于不用思考就获取的自我满足感,这才是对自我提升有要求,对渴望获取优质信息人群的一种正确方式。 这才是推荐算法该有的样子. 【CSS】渐变背景(background-image) 文章很详细地介绍了 CSS 渐变背景的各种知识. SpringBoot 中统一包装响应 SpringBoot 中统一包装响应 springboot返回值拦截并格式化 SpringBoot中全局异常的捕获与包装 SpringBoot 日志 Spring Boot(十)日志Logback和Log4j2集成与日志发展史 文章简单对比了几种常见的 Java 日志框架的发展史, 另外还介绍了 LogBack 和 Log4j2 的使用. SpringBoot集成slf4j+log4j2 这个算是极简配置手册吧. SpringBoot + Log4j2使用配置 比别人多讲了一个异步日志. Angular Debugger 调试 文章介绍了 Angular 项目调试的多种方法. 基于angular-cli配置代理解决跨域请求问题 文章介绍了 Angular-CLI 跨域问题的一种解决方法. 工具推荐 mozilla/send Simple, private file sharing from the makers of Firefox https://send.firefox.com 相关报导: 火狐团队开发:免费分享大型加密文件的在线神器,支持到期即焚 老司机福利,来自 Mozilla 开源的加密文件分享工具! CMD5 一个在线解密 md5 的网站, 号称解密成功率 95%, 随便测试了两次, 真的很强大. md5 确实已经过时了. lllyasviel/style2paints 一个使用 AI 自动为线稿上色的工具, 试了以下, 效果及其惊艳. 原图: 上色之后: 这里有一个简单的使用教程: 教程 资源推荐 Hutool 一个丰富的 Java 工具包. Bootswatch: Free themes for Bootstrap | Github 这个网站收集了很多免费的 bootstrap 主题. BOOTSTRAP ZERO | Github 另一个免费的 bootstrap 主题收集网站. Metis | Github 一个 bootstrap 后台管理模板. CoreUI Free Bootstrap Admin Template 另一个 bootstrap 后台管理模板. cgoldsby/LoginCritter 一个很萌的移动端登录页面. 推荐4款不错的登录页面 文中收集了 4 款很赞的 web 端登录页面. 普通人的网页配色方案 嗯, 总是会产生一种 “我的审美还有救” 的错觉. 開發人員路線圖 2019 年成為 Web 開發人員的路線圖. 我破解了PPT地图的插入方式,动态地图1招搞定! 文章推荐了几个在线图表制作工具: 图表秀 图说 地图慧 高德开放平台 百度地图开放平台 趣文 为啥程序员下班后只关显示器从不关电脑? 文中道出了程序员下班不关电脑的原因. 另外, 如果天天使用的话, 我们也确实不需要关机, 科普: 用完电脑到底要不要关机?

2019/3/30
articleCard.readMore

使用 yarn 安装 packages 之后出现 command not found 问题的解决办法

前言 最近了解到 nodejs 的一款新的包管理器 yarn, 据说相对于 npm 来说有许多优点. 遂果断开启试用流程, 无奈遇到使用 yarn 安装 packages 之后出现 command not found 的问题, 这里写出我自己的解决方法. 解决 工作环境 1 2 3 OS: Ubuntu 18.10 x86_64 Kernel: 4.18.0-16-generic Shell: zsh 5.5.1 非 root 用户登录. 问题重现 使用 yarn 安装 Angular CLI 1 yarn global add @angular/cli 提示安装成功. 运行 ng 命令却提示 command not found. 解决方案 yarn 全局安装的包其实是在 ~/.yarn/bin/ 目录下的, 只需将这个目录配置到系统环境变量即可. 1 2 echo 'export PATH=~/.yarn/bin/:$PATH' >> ~/.zshrc source ~/.zshrc 问题解决. 非 zsh 终端的用户可以将上述命令中的 .zshrc 改为 .profile 即可. 参考文献 Installation Problem: bash: yarn: command not found

2019/3/15
articleCard.readMore

IDEA 下为 Spring Boot 项目开启热部署

前言 因为最近接触了一丢丢 Spring Boot 下的 Web 开发工作, 每次进行一丢丢改动都要重启项目甚是麻烦. 所以查了一下 IDEA 下 Spring 项目开启热部署的方法, 特此记录, 以便查阅. 修改 pom 文件 在 dependency 中添加 optional 属性并设置为 true: 1 2 3 4 5 6 7 <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency> </dependencies> 在 plugin 中配置属性 fork 并设置为 true: 1 2 3 4 5 6 7 8 9 10 11 <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <fork>true</fork> </configuration> </plugin> </plugins> </build> 配置 IDEA 进入设置界面, 选择 “Build, Execution, Deployment” 下的 “Compiler” 选项, 勾选右边的 “Build project automatically”; 使用快捷键 Ctrl + Shift + A 并搜索 “Registry”. 进入 “Registry” 界面后找到 “compile.automake.allow.when.app.running” 并勾选. 至此, 热部署就设置好啦.

2019/3/3
articleCard.readMore

月刊-201902

资源推荐 500 Lines or Less | 500 Lines or Less CN Python 入门神书, 中文翻译工作进行中. 口红颜色可视化 | Ovilia/lipstick 项目收集了上百种口红色号, 很是有趣. 1月份GitHub上最热门的Java开源项目 文中整理了数十个优秀的 Java 开源项目, 值得关注. 对不起,我以后只想用这几款手机浏览器 三款很赞的手机浏览器. 小米9还没发售就已经开源了,代号“cepheus”! pomber/git-history 可以查看 github 文件修改历史的小工具. Aria2 & YAAW 使用说明 一个相对详细的 aria2 使用说明. Motrix 一款支持下载 HTTP, FTP, BT, 磁力链接, 百度网盘等资源的下载工具, 基于 aria2. docsmall 在线图片压缩以及 PDF 处理工具. remote-browser A low-level browser automation framework built on top of the Web Extensions API standard. The 50 Best Free Datasets for Machine Learning | Gengo AI 50 个机器学习数据集. VectorLogoZone 高清 Logo 图库. notable/notable 一款基于 Markdown 的个人笔记应用. 1226 Projects tagged with “raspberry pi” 网页收集了一千多个树莓派相关项目. AnyKnew 5 分钟遍历全网热点. 优质文章 你以为的终身学习,可能只是自我麻痹 不要囤积知识, 消化才更重要. 现在的你,到底该不该换工作 “钱”是物质保障水平,“心”是精神满足水平,“路”是能力发展通道,而能力有了发展可以进一步提升前两条。 2019 蕉虑解决百宝箱 No.132 论如何成为像蕉哥一样优秀的人儿. 个人收藏 IDEA创建多个模块MavenSpringBoot项目 为什么越来越少的人用 jQuery? 文章分析了 JQuery 不太乐观的现状.

2019/1/31
articleCard.readMore

Java 逐行读取文本文件的几种方式以及效率对比

前言 上周负责的模块中需要逐行读取文件内容, 写完之后对程序执行效率不太满意, 索性上网查了一下 Java 逐行读取文件内容的各种方法, 并且简单地比对了一下执行效率. 在此记录, 希望能够帮到有需要的人. 注意: 本文比对的项目为 逐行读取文本内容, 并不能代表其他方式的文件读取效率优劣!!! 文末有完整代码. 先放结果 1000000 行文本读取结果比对: 1 2 3 4 5 6 7 8 BufferedReader 耗时: 49ms Scanner 耗时: 653ms Apache Commons IO 耗时: 44ms InputStreamReader 耗时: 191ms FileInputStream 耗时: 3171ms BufferedInputStream 耗时: 70ms FileUtils 耗时: 46ms Files 耗时: 99ms 24488656 行文本读取结果比对: 1 2 3 4 5 6 7 8 BufferedReader 耗时: 989ms Scanner 耗时: 11899ms Apache Commons IO 耗时: 568ms InputStreamReader 耗时: 3377ms FileInputStream 耗时: 78903ms BufferedInputStream 耗时: 1480ms FileUtils 耗时: 16569ms Files 耗时: 25162ms 可见, 当文件较小时: ApacheCommonsIO 流 表现最佳; FileUtils, BufferedReader 居其二; BufferedInputStream, Files 随其后; InputStreamReader, Scanner, FileInputStream 略慢. 当文件较大时, Apache Commons IO 流, BufferedReader 依然出色, Files, FileUtils 速度开始变慢. 简要分析 使用到的工具类包括: java.io.BufferedReader java.util.Scanner org.apache.commons.io.FileUtils java.io.InputStreamReader java.io.FileInputStream java.io.BufferedInputStream com.google.common.io.Files 其中: Apache Commons IO 流 和 BufferedReader 使用到了缓冲区, 所以在不消耗大量内存的情况下提高了处理速度; FileUtils 和 Files 是先把文件内容全部读入内存, 然后在进行操作, 是典型的空间换时间案例. 这种方法可能会大量消耗内存, 建议酌情使用; 其他几个工具类本来就不擅长逐行读取, 效率底下也是情理之中. 建议 在逐行读取文本内容的需求下, 建议使用 Apache Commons IO 流, 或者 BufferedReader, 既不会过多地占用内存, 也保证了优异的处理速度. 参考文献: [Java]读取文件方法大全 — lovebread java读取文件API速度对比 — fengxingzhe001 Java高效读取大文件 — Eugen Paraschiv[文] / ImportNew - 进林[译] 附录-源代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 import com.google.common.io.Files; import org.apache.commons.io.Charsets; import org.apache.commons.io.FileUtils; import org.apache.commons.io.LineIterator; import java.io.*; import java.util.List; import java.util.Random; import java.util.Scanner; /** * @Description: 逐行读取文件性能对比 * @Author: Seven-Steven * @Date: 19-1-25 **/ public class ReadByLineFromFileTest { public static void main(String[] args) { ReadByLineFromFileTest test = new ReadByLineFromFileTest(); String filePath = "./testFile.txt"; File file = new File(filePath); if (!file.exists()) { // 随机写入 1000000 行内容 test.writeRandom(filePath, 1000000); } long before, after, time; // 使用 BufferedReader 逐行读取文件 before = System.currentTimeMillis(); test.bufferedReader(filePath); after = System.currentTimeMillis(); time = after - before; System.out.println("BufferedReader 耗时: " + time + "ms"); // 使用 Scanner 逐行读取文件 before = System.currentTimeMillis(); test.scanner(filePath); after = System.currentTimeMillis(); time = after - before; System.out.println("Scanner 耗时: " + time + "ms"); // 使用 Apache Commons IO 流逐行读取文件 before = System.currentTimeMillis(); test.apacheCommonsIo(filePath); after = System.currentTimeMillis(); time = after - before; System.out.println("Apache Commons IO 耗时: " + time + "ms"); // 使用 InputStreamReader 逐字符读取文件 before = System.currentTimeMillis(); test.inputStreamReader(filePath); after = System.currentTimeMillis(); time = after - before; System.out.println("InputStreamReader 耗时: " + time + "ms"); // 使用 FileInputStream 逐字符读取文件 before = System.currentTimeMillis(); test.fileInputStream(filePath); after = System.currentTimeMillis(); time = after - before; System.out.println("FileInputStream 耗时: " + time + "ms"); // 使用 BufferedInputStream 逐字符读取文件 before = System.currentTimeMillis(); test.bufferedInputStream(filePath); after = System.currentTimeMillis(); time = after - before; System.out.println("BufferedInputStream 耗时: " + time + "ms"); // 使用 FileUtils 一次性读取文件所有行 before = System.currentTimeMillis(); test.fileUtils(filePath); after = System.currentTimeMillis(); time = after - before; System.out.println("FileUtils 耗时: " + time + "ms"); // 使用 Files 一次性读取文件所有行 before = System.currentTimeMillis(); test.files(filePath); after = System.currentTimeMillis(); time = after - before; System.out.println("Files 耗时: " + time + "ms"); } /** * @Description: 使用 Apache Commons IO 流逐行读取文件 * Maven 依赖: * <!-- https://mvnrepository.com/artifact/commons-io/commons-io --> * <dependency> * <groupId>commons-io</groupId> * <artifactId>commons-io</artifactId> * <version>2.6</version> * </dependency> * @Param: [filePath] 文件路径 * @Author: Seven-Steven * @Date: 19-1-24 **/ public void apacheCommonsIo(String filePath) { File file = new File(filePath); if (!file.exists()) { return; } try { LineIterator iterator = FileUtils.lineIterator(file, "UTf-8"); while (iterator.hasNext()) { String line = iterator.nextLine(); // TODO // System.out.println(line); } } catch (IOException e) { e.printStackTrace(); } } /** * @Description: 使用 Scanner 类逐行读取 * @Param: [filePath] 文件路径 * @Author: Seven-Steven * @Date: 19-1-24 **/ public void scanner(String filePath) { File file = new File(filePath); if (!file.exists()) { return; } FileInputStream fileInputStream = null; Scanner scanner = null; try { fileInputStream = new FileInputStream(file); scanner = new Scanner(fileInputStream, "UTF-8"); while (scanner.hasNextLine()) { // TODO things String line = scanner.nextLine(); // System.out.println(line); } } catch (FileNotFoundException e) { e.printStackTrace(); } finally { if (fileInputStream != null) { try { fileInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if (scanner != null) { scanner.close(); } } } /** * @Description: 使用 Files 一次性读取所有行 * Maven 依赖: * <!-- https://mvnrepository.com/artifact/com.google.guava/guava --> * <dependency> * <groupId>com.google.guava</groupId> * <artifactId>guava</artifactId> * <version>r05</version> * </dependency> * @Param: [filePath] 文件路径 * @Author: Seven-Steven * @Date: 19-1-24 **/ public void files(String filePath) { File file = new File(filePath); if (!file.exists()) { return; } try { List<String> fileLines = Files.readLines(file, Charsets.toCharset("UTF-8")); for (String str : fileLines) { // TODO things // System.out.println(str); } } catch (IOException e) { e.printStackTrace(); } } /** * @Description: 使用 FileUtils 一次性将文件所有行读入内存 * Maven 依赖: * <!-- https://mvnrepository.com/artifact/commons-io/commons-io --> * <dependency> * <groupId>commons-io</groupId> * <artifactId>commons-io</artifactId> * <version>2.6</version> * </dependency> * @Param: [filePath] 文件路径 * @Author: Seven-Steven * @Date: 19-1-24 **/ public void fileUtils(String filePath) { File file = new File(filePath); if (!file.exists()) { return; } try { List<String> fileLines = FileUtils.readLines(file, Charsets.UTF_8); for (String str : fileLines) { // TODO // System.out.println(str); } } catch (IOException e) { e.printStackTrace(); } } public void bufferedInputStream(String filePath) { File file = new File(filePath); if (!file.exists()) { return; } FileInputStream fileInputStream = null; BufferedInputStream bufferedInputStream = null; try { fileInputStream = new FileInputStream(file); bufferedInputStream = new BufferedInputStream(fileInputStream); int temp; char character; String line = ""; while ((temp = bufferedInputStream.read()) != -1) { character = (char) temp; if (character != '\n') { line += character; } else { // TODO // System.out.println(line); line = ""; } } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (fileInputStream != null) { try { fileInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if (bufferedInputStream != null) { try { bufferedInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * @Description: 使用 FileInputStream 逐字符读取文件 * @Param: [filePath] 文件路径 * @Author: Seven-Steven * @Date: 19-1-23 **/ public void fileInputStream(String filePath) { File file = new File(filePath); if (!file.exists()) { return; } FileInputStream fileInputStream = null; try { fileInputStream = new FileInputStream(file); int temp; char character; String line = ""; while ((temp = fileInputStream.read()) != -1) { character = (char) temp; if (character != '\n') { line += character; } else { // TODO // System.out.println(line); line = ""; } } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (fileInputStream != null) { try { fileInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * @Description: 使用 InputStreamReader 逐行读取文件 * @Param: [filePath] 文件路径 * @Author: Seven-Steven * @Date: 19-1-23 **/ public void inputStreamReader(String filePath) { File file = new File(filePath); if (!file.exists()) { return; } FileInputStream fileInputStream = null; InputStreamReader inputStreamReader = null; try { fileInputStream = new FileInputStream(file); inputStreamReader = new InputStreamReader(fileInputStream); int temp; char character; String line = ""; while ((temp = inputStreamReader.read()) != -1) { character = (char) temp; if (character != '\n') { line += character; } else { // TODO // System.out.println(line); line = ""; } } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (fileInputStream != null) { try { fileInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if (inputStreamReader != null) { try { inputStreamReader.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * @Description: 使用 BufferedReader 逐行读取文件内容 * @Param: [filePath] 文件路径 * @Author: Seven-Steven * @Date: 19-1-23 **/ public void bufferedReader(String filePath) { File file = new File(filePath); if (!file.exists()) { return; } FileReader fileReader = null; BufferedReader bufferedReader = null; try { fileReader = new FileReader(file); bufferedReader = new BufferedReader(fileReader); String line = ""; while ((line = bufferedReader.readLine()) != null) { // TODO things // System.out.println(line); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (fileReader != null) { try { fileReader.close(); } catch (IOException e) { e.printStackTrace(); } } if (bufferedReader != null) { try { bufferedReader.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * @Description: 随机往文件中写入 totalLines 行内容 * @Param: [filePath, totalLines] 文件路径, 内容行数 * @Author: Seven-Steven * @Date: 19-1-23 **/ public void writeRandom(String filePath, int totalLines) { RandomAccessFile file = null; Random random = new Random(); try { file = new RandomAccessFile(filePath, "rw"); long length = file.length(); for (int i = 0; i < totalLines; i++) { file.seek(length); int number = random.nextInt(1000000); String line = number + "\n"; file.writeBytes(line); length += line.length(); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (file != null) { try { file.close(); } catch (IOException e) { e.printStackTrace(); } } } } }

2019/1/23
articleCard.readMore

巧用 python 随时随地开启 http 服务器

前言 近来发现 Python 中内置了一个小巧的 HTTP 服务器, 轻微使用的话及其方便. 特撰此文, 作为记录. 方法 进入到自己想要开启 HTTP 服务器的目录; 输入对应命令即可: 1 2 # python3 python -m http.server 1 2 # python3 指定端口 python -m http.server 8000 1 2 # python2 python -m SimpleHTTPServer 此时, HTTP 服务器就启动在本机的 8000 端口了. 用途 预览前端项目: 如果当前目录下存在 HTML 文件, 那么这个 HTTP 服务器可以直接用来预览前端项目. 临时文件传输: 如果当前目录下不存在 index.html, 就会显示当前目录下的文件列表. 你可以在开启 HTTP 服务器后把自己的 ip 以及端口告诉朋友, 朋友就可以直接通过网页下载你对应目录下的文件啦. 参考文献 Python 也会传文件,再也不用看某某网盘的脸色了! — Rocky0429

2019/1/16
articleCard.readMore

月刊-201901

精选文章 5分钟经典英文技术演讲1:如何快速掌握新技术 - Kathy Sierra 如何快速掌握新技术, 除了一些小技巧, 文中还提到了一个很好地观点: 高质量的例子. 这一点我很赞同, 高质量的例子往往会给人最直观的感受, 这或许是我们开发者努力的一个方向. 此外, 作者博客中的其他文章也值得阅读, 貌似博主总能以最精简的语言表述最核心的内容. 博客地址: DecodeZ 10000字干货!这可能是最全的英语学习方法了 作者总结了一些学习英语的心得, 方法和资源, 个人感觉很是受用. 任正非万字采访昨夜刷屏!这20句话让全世界振聋发聩 这是我第一次认识任正非, 也是我第一次被他的大智慧, 大心胸, 大眼界所折服. 这是一篇值得我们反复品读的文章. 一千个不用 Null 的理由 文中解释了为什么 Mysql 数据库中不建议使用 Null. BBC曝光:每天10000步,竟是商家的营销骗局?! 日行一万步来源于日本计步器商家随口编出来的营销骗局. 对于部分人群来说, 日行一万步不仅无益, 还有可能伤及自身. 别再写 bug 了,避免空指针的 5 个案例! 文中简单列举了一些 Java 编程中避免空指针异常的方法和习惯. 马云后半生的目标:赔钱 | 2018 “马云乡村教师奖” 颁奖晚会 说实话, 马老师是我最佩服的一个人. 百度推广, 腾讯游戏, 唯有阿里, 改天换地. 阿里巴巴, 淘宝, 支付宝, 天猫, 菜鸟物流, 达摩院, 甚至蚂蚁森林, 无一不是伟大的创举. 在人生最巅峰的时候, 他又洗尽铅华, 投身于教育事业当中. 文章评论里有位朋友说道: “教育是最廉价的国防”. 这又何尝不是我们追逐更好的生活的的最佳方式呢? 如果能够更好地改善自己的生活, 对于国家, 已经算是一种贡献了. 工具 CODELF 脑洞清奇的变量命名小工具. 工具再好, 也好不过自身良好的英语素养, 还是老老实实学英语吧. Snapseed 很强大的手机修图 app, 这里是一篇介绍文章: 手机修图,用这一个App就够了 wonderfulsuccess / weixin_crawler 微信公众号历史文章和阅读数据爬虫. 感悟 所谓岁月静好, 不过是有在人替你负重前行. 正常情况下我们认为思想决定行动, 但是大多情况下行动更能影响思维. 所以, 如果想要改变, 那就大胆地行动吧! — 读 [正能量] 有感

2019/1/3
articleCard.readMore

Linux 命令 --- echo

简介 echo 命令用于在 shell 中打印 shell 变量的值, 或者直接输出指定的字符串. linux 的 echo 命令在 shell 编程中极为常用, 在终端下打印变量 value 的时候也是常常用到的, 因此有必要了解下 echo 的用法. 语法 1 echo [选项] [参数] 选项 -n — 不打印尾部换行 -e — 启用反斜线 \ 转义 使用 -e 选项时, 若参数中出现以下字符, 会加以特别处理: \\ 反斜线 \a 警告音 \b 回退 (删除前一个字符) \e 删除此标志后的一个字符 \c 不产生进一步输出 (\c 后面的字符不会被输出) \f 换行但光标仍旧停留在原来的位置 \r 光标移至行首,但不换行 \n 换行 \t 水平制表符 \v 垂直制表符 \0NNN 打印八进制值为 NNN 的字符 \xHH 打印十六进制值为 HH 的字符 -E — 禁用反斜线 \ 转义 (默认选项) 默认情况下, echo 命令不会对反斜线 \ 进行转义 Ubuntu 18.10 环境下测试 echo 命令默认会对反斜线进行转义, 与文档说法相悖. 所以使用 echo 命令时最好指明参数. –help — 显示帮助文档 Ubuntu 18.10 环境下测试此选项无效, 命令会输出 “–help” 字符串. –version — 显示版本信息 Ubuntu 18.10 环境下测试此选项无效, 命令会输出 “–version” 字符串. 参数 常规字符串 直接打印字符串. 系统变量 打印变量值. 示例 打印字符串 1 echo I love Linux 打印变量值 1 echo $PATH 打印转义字符 反斜线 \\ 1 echo -e \\ 警告音 \a 1 echo -e \\a 系统会发出警告音. 回退 \b 1 echo -e 123\\b456 Ubuntu 18.10 环境下测试, 当 \\b 位于字符串末尾时, 不会产生回退效果. 删除符 \e 1 echo -e \\e123 终止输出 \c 1 echo -e 1234\\c5678 水平制表符 \t 1 echo -e aaa\\tbbb\\tccc\\tddd\\teee 垂直制表符 \v 1 echo -e aaa\\vbbb\\vccc\\vddd\\veee 换行符 \n 1 echo -e 1\\n2\\n3\\n4\\n5 光标移至行首,但不换行 \r 1 echo -e 123456789\\rabcd \r 会将光标移至行首, 所以 “abcd” 覆盖掉了先前打印的 “1234”, 产生了 “abcd456789” 的输出. 换行但光标仍旧停留在原来的位置 \f 1 echo -e 1\\f2\\f3\\f4\\f5 \f 与垂直制表符 \v 并不一样, 命令行下展现不出他们之间的差异, 参照 r 符号理解即可. 八进制值为 NNN 的字符 1 echo -e \\0101 八进制值 101 对应十进制值 65, 也就是字符 “A”. 十六进制值为HH的字符 1 echo -e \\x41 十六进制值 41 对应十进制值 65, 也是字符 “A”. 打印颜色 "\e[M;NmString" M 代表文字属性: 0 关闭所有属性、1 设置高亮度(加粗)、4 下划线、5 闪烁、7 反显、8 消隐 N 代表文字颜色: 前景色: 重置=0,黑色=30,红色=31,绿色=32,黄色=33,蓝色=34,洋红=35,青色=36,白色=37 背景色: 重置=0,黑色=40,红色=41,绿色=42,黄色=43,蓝色=44,洋红=45,青色=46,白色=47 必须使用双引号包围参数. 设置文字颜色 1 echo -e "\e[31mThis is red text" 使用 \e[Nm 设置文字颜色: N 代表颜色码: 重置=0,黑色=30,红色=31,绿色=32,黄色=33,蓝色=34,洋红=35,青色=36,白色=37 设置文字背景色 1 echo -e "\e[42mGreed Background" 使用 \e[Nm 设置文字背景色: N 代表颜色码: 重置=0,黑色=40,红色=41,绿色=42,黄色=43,蓝色=44,洋红=45,青色=46,白色=47 文字闪动 1 echo -e "\033[37;31;5mMySQL Server Stop...\033[39;49;0m" 参考文献 man 文档 echo 命令,Linux echo 命令详解:输出指定的字符串或者变量 - Linux 命令搜索引擎 Linux中的15个‘echo’ 命令实例 — tecmint Avishek Kumar

2018/12/31
articleCard.readMore

月刊-201812

七大查找算法 作者详细介绍了顺序查找, 二分查找, 插值查找, 斐波那契查找, 数表查找, 分块查找, 哈希查找 等查找算法. 一些工作娱乐网站推荐 内容包含图片压缩网站 Squoosh, 视频下载网站 ParseVideo, 公益网站 大象孤儿院, 在线格式转换网站 cloudconvert, 设计网站 dribbble 等. Math is Fun | 数学乐 科普网站, 生动有趣地介绍了各种理科知识. awesome-mac 各种非常好用的 Mac 应用程序, 软件以及工具列表. Zerostwo 风格新奇的一个博客. 这十个免费自学网站,每天花30分钟受益一生 文章推荐了一些常用的自学网站, 有时间的话可以尝试一下. UNIX 高手的另外 10 个习惯 文章介绍了 Unix 的一些进阶技巧, 十年前的文章, 现在依然能从中受益. 学习论文写作,看免费公开课,千万别错过这十个网站! 关于论文写作的一些资源罗列. 知乎30条最高赞回复,看完之后你会开心很多 很有哲理的话语, 看完后会豁然开朗. 关于校招签约,你不得不知的事项 对校招签约流程以及注意事项做了一个很详细的解释, 还没有签约的应届毕业生可以看一下. 你还是放不下我。 毒鸡汤, 没什么用, 但我觉得挺好. Design Tips 关于设计的一些小技巧. 数字世界中的纸张——理解 PDF 文章对 PDF 格式做了一个详尽的解释, 对 PDF 的定位相当恰当. 拼命写代码的人生,有兜底吗? 文章很公道得介绍了一些保险的知识, 个人觉得还是很不错的. 久等了,花了365天我们终于做出了这篇文章 | 年刊 非常喜欢的一个公众号, 主要目标是教会学生学校学不到的东西. 这篇文章是这个公众号的年度总结. wonderfulsuccess/weixin_crawler 高效微信公众号历史文章和阅读数据爬虫powered by scrapy.

2018/12/31
articleCard.readMore

Linux 命令 --- whereis

简介 “whereis” 命令常用来定位指令的二进制程序, 源代码文件和 man 手册页相关文件的路径. “whereis” 命令只能用于程序名的搜索, 而且只搜索二进制文件 (参数 -b), man 说明文件 (参数 -m) 和源代码文件 (参数 -s), 如果省略参数, 则返回上述所有信息. 用法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 用法: whereis [选项] [-BMS <dir>... -f] <名称> 定位某个命令的二进制文件、源码和帮助页文件。 选项: -b 只搜索二进制文件 -B <目录> 定义二进制文件查找路径 -m 只搜索手册和信息 -M <目录> 定义 man 和 info 查找路径 -s 只搜索源代码 -S <目录> 定义源代码查找路径 -f 终止 <目录> 参数列表 -u 搜索不常见记录 -l 输出有效查找路径 -h, --help display this help -V, --version display version 注意: 如果使用 “-B”, “-M”, “-S” 参数指定目录列表, 需要在目录列表之后追加 “-f” 命令表示终止目录参数列表. 举例 查找 “whereis” 命令路径: 1 whereis whereis 命令输出: 1 whereis: /usr/bin/whereis /usr/share/man/man1/whereis.1.gz 这里显示了 “whereis” 命令二进制文件的路径和 man 手册信息. 查找 “whereis” 命令自身二进制文件所在路径: 1 whereis -b whereis 命令输出: 1 whereis: /usr/bin/whereis 这次只显示了 “whereis” 命令二进制文件的路径. 到指定目录查找 “python” 命令的二进制文件: 我们先看一次下指定目录前的效果: 1 whereis -b python 命令输出: 1 python: /usr/bin/python3.6m-config /usr/bin/python3.6-config /usr/bin/python3.6m /usr/bin/python3.6 /usr/bin/python2.7 /usr/bin/python /usr/lib/python3.6 /usr/lib/python2.7 /usr/lib/python3.7 /etc/python3.6 /etc/python2.7 /etc/python /usr/local/lib/python3.6 /usr/local/lib/python2.7 /usr/include/python3.6m /usr/include/python3.6 /usr/include/python2.7 /usr/share/python 命令输出了所有与 “python” 有关的命令的二进制文件路径. 如果我们只想查看个别目录下的搜索结果呢? 指定目录查找 “python” 命令的二进制文件: 1 whereis -b -B /usr/bin -f python 命令输出: 1 python: /usr/bin/python3.6m-config /usr/bin/python3.6-config /usr/bin/python3.6m /usr/bin/python3.6 /usr/bin/python2.7 /usr/bin/python 这次只显示了指定目录下与 “python” 命令有关的二进制文件路径.

2018/12/17
articleCard.readMore

为 Hexo 主题 next 添加图片背景

前言 next 主题默认背景是白色, 看多了难免感觉乏味. 我们可以通过自定义样式为其添加背景图片. 操作流程 我们直接在 NEXT 预留的自定义样式文件 “themes/next/source/css/_custom/custom.styl” 中添加自己的样式即可, 在自定义样式文件中写入以下代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 // 添加背景图片 body { background: url(https://source.unsplash.com/random/1600x900?wallpapers); background-size: cover; background-repeat: no-repeat; background-attachment: fixed; background-position: 50% 50%; } // 修改主体透明度 .main-inner { background: #fff; opacity: 0.8; } // 修改菜单栏透明度 .header-inner { opacity: 0.8; } 其中: background: url() 中填写的是背景图片的 url 地址, 这里调用了 Unsplash 的 API, 随机选用该网站的高清美图作为博客背景. 该网站所有图片都是免费商用的, 所以无须担心侵权问题; 网站 API 还有很多有趣的玩法, 参见: Documentation opacity 指定了对应元素的透明度, 这里是 “0.8”, 可以按需更改. 看一下完成后的效果: 参考文献 Next主题个性化之自动更换背景图片 — 芒果浩明 Hexo+Next主题优化 — 路人S

2018/12/5
articleCard.readMore

Ubuntu 18.10 开机自动打开数字键盘

前言 可能是为了照顾程序员? Ubuntu 开机默认是不打开数字键盘的, 本文主要描述如何让 Ubuntu 开机自动打开小键盘. 步骤 同时按下 “ctrl + alt + T” 打开终端; 获取 root 权限: 1 sudo -i 允许 gdm 和 X 服务建立连接: 1 xhost +SI:localuser:gdm 切换到 gdm 用户: 1 su gdm -s /bin/bash 设置开机自动打开数字键盘: 1 gsettings set org.gnome.settings-daemon.peripherals.keyboard numlock-state 'on' 启动触控板: 1 gsettings set org.gnome.desktop.peripherals.touchpad tap-to-click true 至此, 设置已经完成. 如果你像我一样强迫症, 可以取消 gdm 和 X 服务器的连接设置: 1 xhost -SI:localuser:gdm That’s all, enjoy it. 参考文献 Enable Numlock, Tap to Click in Ubuntu 18.04 Login Screen — ml

2018/12/1
articleCard.readMore

获取 Linux 登录页面截图

前言 这两天心血来潮, 想办法美化了自己 Ubuntu 系统的登录界面. 进而找到了获取 Linux 登录页面截图的方法. 这里记录一下流程, 以作日后查验之用. 步骤 首先, 我们需要新建一个脚本文件 “screenshot.sh”, 填入以下内容: 1 2 3 4 5 chvt 2 sleep 5 DISPLAY=:0.0 XAUTHORITY=/run/user/1000/gdm/Xauthority xwd -root > /tmp/shot.xwd convert /tmp/shot.xwd /tmp/screen.png mv /tmp/screen.png ~/ 其中: chvt N 表示跳转到 tty N, 我这里图形界面是 tty2, 所以写作 chvt 2; sleep N 表示程序等待 N 秒, 可以自由调节时间长短, 留出时间调整截屏页面; 第三行中 /run/user/1000/gdm/Xauthority 表示当前系统的登录管理器, 因为不同的 Linux 发行版可能使用不同的登录管理器, 所以出错的往往是在这里. 那么怎么获取当前登录界面 tty 编号以及登录管理器呢? 我们可以通过 ps 命令查看: 1 ps -ef | grep auth 可以看到, 我当前登录的终端为 tty2, 登录管理器为 “/run/user/1000/gdm/Xauthority”, 可以按照自己的情况修改上面的脚本. 脚本后两行是转换和转移图片, 这里不再多说. 赋予脚本文件可执行权限 1 chmod +x ./screenshot.sh 注销当前登录状态, 然后按下按键 “ctrl + alt + “ 进入 tty5 (也可以进入其他终端, 非图形界面即可). 从该终端进入到 screenshot.sh 脚本所在路径, 以 root 权限执行: 1 sudo ./screenshot.sh 这时终端会自动跳转到我们退出登录的图形界面, 在倒计时结束前调整好自己想要截屏的页面, 等待脚本截图即可. 截图文件会存储在当前用户主目录下, 至此, 目的达成, 附上我的登录页面: 参考文献 如何捕获 Linux 图形化登录界面的截图 — 潘家邦

2018/11/30
articleCard.readMore

Linux 命令 --- pwd

简介 pwd 命令全称 “print working directory”, 是 Linux 的基础命令之一, 用来打印当前工作目录. 用法 pwd 命令的用户手册描述如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 NAME pwd - print name of current/working directory SYNOPSIS pwd [OPTION]... DESCRIPTION Print the full filename of the current working directory. -L, --logical use PWD from environment, even if it contains symlinks -P, --physical avoid all symlinks --help display this help and exit --version output version information and exit If no option is specified, -P is assumed. NOTE: your shell may have its own version of pwd, which usually super‐ sedes the version described here. Please refer to your shell's docu‐ mentation for details about the options it supports. AUTHOR Written by Jim Meyering. REPORTING BUGS GNU coreutils online help: <http://www.gnu.org/software/coreutils/> Report pwd translation bugs to <http://translationproject.org/team/> COPYRIGHT Copyright © 2017 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>. This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. SEE ALSO getcwd(3) Full documentation at: <http://www.gnu.org/software/coreutils/pwd> or available locally via: info '(coreutils) pwd invocation' GNU coreutils 8.28 October 2018 PWD(1) 用例 使用 pwd 命令显示当前工作目录: 1 pwd pwd 命令 -P 参数和 -L 参数解析, 如图: 我们先是使用 pwd 命令查看了当前工作目录, 为 “/home/seven/Downloads”; 然后我们使用 mkdir 命令在这个目录下创建了一个名为 “test_pwd” 的文件夹; 使用 cd 命令进入 “test_pwd” 目录; 在 “test_pwd” 目录下创建三个文件夹: “a”, “a/aa” 和 “b”; 使用 ln 命令把 “a/aa” 目录链接到 “b/bb” 目录; 此时, 可以使用 tree 命令查看当前目录结构为: 1 2 3 4 5 6 7 . ├── a │   └── aa └── b └── bb -> /home/seven/Downloads/test_pwd/a/aa 4 directories, 0 files 使用 cd 命令进入到 “b/bb” 目录下; 不带参的 pwd 命令显示当前工作目录为 “/home/seven/Downloads/test_pwd/b/bb”; pwd -P 命令显示当前工作目录为 “/home/seven/Downloads/test_pwd/a/aa”; pwd -L 命令显示当前工作目录为 “/home/seven/Downloads/test_pwd/b/bb”; 可见: -P 参数会避开所有符号链接, 显示真实的物理路径; -L 参数不管是否包含符号链接, 都只会显示逻辑路径.

2018/11/29
articleCard.readMore

[转载]循序渐进带你学习时间复杂度和空间复杂度

前言 我们都知道,对于同一个问题来说,可以有多种解决问题的算法。尽管算法不是唯一的,但是对于问题本身来说相对好的算法还是存在的,这里可能有人会问区分好坏的标准是什么?这个要从「时效」和「存储」两方面来看。 人总是贪婪的,在做一件事的时候,我们总是期望着可以付出最少的时间、精力或者金钱来获得最大的回报,这个类比到算法上也同样适用,那就是花最少的时间和最少的存储做成最棒的解决办法,所以好的算法应该具备时效高和存储低的特点。这里的「时效」是指时间效率,也就是算法的执行时间,对于同一个问题的多种不同解决算法,执行时间越短的算法效率越高,越长的效率越低;「存储」是指算法在执行的时候需要的存储空间,主要是指算法程序运行的时候所占用的内存空间。 时间复杂度 首先我们先来说时间效率的这个问题,这里的时间效率就是指的算法的执行时间,时间的快慢本来就是一个相对的概念,那么到了算法上,我们该用怎样的度量指标去度量一个算法的时间效率(执行时间)呢? 刚开始我们想出了一种事后统计方法,我称它为「马后炮式」,顾名思义,就是对于要解决的某个问题,费尽心思想了 n 种解法,提前写好算法程序,然后攒了一堆数据,让它们分别在电脑上跑,跑完了然后比较程序的运行时间,根据这个来判断算法时效的高低。这种的判断技术计算的是我们日常所用的时间,但这并不是一个对我们来说有用的度量指标,因为它还依赖于运行的机器、所用的编程语言、编译器等等等等。相反,我们需要的是一个不依赖于所用机器或者编程语言的度量指标,这种度量指标可以帮助我们判断算法的优劣,并且可以用来比较算法的具体实现。 我们的科学家前辈们发现当我们试图去用执行时间作为独立于具体程序或计算机的度量指标去描述一个算法的时候,确定这个算法所需要的步骤数目非常重要。如果我们把算法程序中的每一步看作是一个基本的计量单位,那么一个算法的执行时间就可以看作是解决一个问题所需要的总步骤数。但是由于算法的执行过程又各不相同,所以这个每一步,即这个基本的计量单位怎么去选择又是一个令人头秃的问题。 下面我们来看一个简单的求和的函数: 1 2 3 4 5 6 7 def get_sum(n): sum = 0 for i in range(1,n+1): sum += i return sum print(get_sum(10)) 我们仔细去分析一下上述代码,其实可以发现统计执行求和的赋值语句的次数可能是一个好的基本计数单位,在上面 get_sum 函数中,赋值语句的数量是 1 (sum = 0)加上 n (执行 sum += i 的次数)。 我们一般用一个叫 T 的函数来表示赋值语句的总数量,比如上面的例子可以表示成 T(n) = n + 1。这里的 n 一般指的是「数据的规模大小」,所以前面的等式可以理解为「解决一个规模大小为 n,对应 n+1 步操作步数的问题,所需的时间为 T(n)」。 对于 n 来说,它可以取 10,100,1000 或者其它更大的数,我们都知道求解大规模的问题所需的时间比求解小规模要多一些,那么我们接下来的目标就很明确了,那就是「寻找程序的运行时间是如何随着问题规模的变化而变化」。 我们的科学家前辈们又对这种分析方法进行了更为深远的思考,他们发现有限的操作次数对于 T(n) 的影响,并不如某些占据主要地位的操作部分重要,换句话说就是「当数据的规模越来越大时,T(n) 函数中的某一部分掩盖了其它部分对函数的影响」。最终,这个起主导作用的部分用来对函数进行比较,所以接下来就是我们所熟知的大 O 闪亮登场的时间了。 大 O 表示法 「数量级」函数用来描述当规模 n 增加时,T(n) 函数中增长最快的部分,这个数量级函数我们一般用「大 O」表示,记做 O(f(n))。它提供了计算过程中实际步数的近似值,函数 f(n) 是原始函数 T(n) 中主导部分的简化表示。 在上面的求和函数的那个例子中,T(n) = n + 1,当 n 增大时,常数 1 对于最后的结果来说越来越没存在感,如果我们需要 T(n) 的近似值的话,我们要做的就是把 1 给忽略掉,直接认为 T(n) 的运行时间就是 O(n)。这里你一定要搞明白,这里不是说 1 对 T(n) 不重要,而是当 n 增到很大时,丢掉 1 所得到的近似值同样很精确。 再举个例子,比如有一个算法的 T(n) = 2n^2+ 2n + 1000,当 n 为 10 或者 20 的时候,常数 1000 看起来对 T(n) 起着决定性的作用。但是当 n 为 1000 或者 10000 或者更大呢?n^2 起到了主要的作用。实际上,当 n 非常大时,后面两项对于最终的结果来说已经是无足轻重了。与上面求和函数的例子很相似,当 n 越来越大的时候,我们就可以忽略其它项,只关注用 2n^2 来代表 T(n) 的近似值。同样的是,系数 2 的作用也会随着 n 的增大,作用变得越来越小,从而也可以忽略。我们这时候就会说 T(n) 的数量级 f(n) = n^2,即 O(n^2)。 最好情况、最坏情况和平均情况 尽管前面的两个例子中没有体现,但是我们还是应该注意到有时候算法的运行时间还取决于「具体数据」而不仅仅是「问题的规模大小」。对于这样的算法,我们把它们的执行情况分为「最优情况」、「最坏情况」和「平均情况」。 某个特定的数据集能让算法的执行情况极好,这就是最「最好情况」,而另一个不同的数据会让算法的执行情况变得极差,这就是「最坏情况」。不过在大多数情况下,算法的执行情况都介于这两种极端情况之间,也就是「平均情况」。因此一定要理解好不同情况之间的差别,不要被极端情况给带了节奏。 对于「最优情况」,没有什么大的价值,因为它没有提供什么有用信息,反应的只是最乐观最理想的情况,没有参考价值。「平均情况」是对算法的一个全面评价,因为它完整全面的反映了这个算法的性质,但从另一方面来说,这种衡量并没有什么保证,并不是每个运算都能在这种情况内完成。而对于「最坏情况」,它提供了一种保证,这个保证运行时间将不会再坏了,所以一般我们所算的时间复杂度是最坏情况下的时间复杂度,这和我们平时做事要考虑到最坏的情况是一个道理。 在我们之后的算法学习过程中,会遇到各种各样的数量级函数,下面我给大家列举几种常见的数量级函数: 为了确定这些函数哪些在 T(n) 中占主导地位,就要在 n 增大时对它们进行比较,请看下图(图片来自于 Google 图片): 在上图中,我们可以看到当 n 很小时,函数之间不易区分,很难说谁处于主导地位,但是当 n 增大时,我们就能看到很明显的区别,谁是老大一目了然: O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(n^3) < O(2^n) 我们下面就来分析几个上述所说的「数量级函数」: 1. 常数函数 1 2 3 n = 100  ## 1 次 sum  = (1 + n) *n / 2 ## 1 次 print(sum) ## 1 次 上述算法程序的 f(n) = 3,可能有人看到这会说那么时间复杂度就是 O(f(n)) = O(3),其实这个是错的,这个函数的时间复杂度其实是 O(1)。这个对于初学者来说是很难理解的一种结果,其实你可以把 sum = (1 + n) * n / 2 多复制几次再来看: 1 2 3 4 5 6 7 8 a = 100 ## 1 次 sum  = (1 + n) * n / 2 ## 1 次 sum  = (1 + n) * n / 2 ## 1 次 sum  = (1 + n) * n / 2 ## 1 次 sum  = (1 + n) * n / 2 ## 1 次 sum  = (1 + n) * n / 2 ## 1 次 sum  = (1 + n) * n / 2 ## 1 次 print(sum) ## 1 次 上述算法的 f(n) = 8,事实上你可以发现无论 n 为多少,上述两段代码就是 运行 3 次和运行 8 次的区别。这种与数据的规模大小 n 无关,执行时间恒定的算法我们就叫它具有 O(1) 的时间复杂度。不管这个常数是多少,我们都记作是 O(1),而不是 O(3) 或者是 O(8)。 2. 对数函数 1 2 3 cnt = 1 while cnt < n:    cnt *= 2 ## O(1) 上面的算法程序的时间复杂度就是 O(logn),这个是怎么算出来的呢?其实很简单:上述的代码可以解释成 cnt 乘以多少个 2 以后才能大于等于 n,我们假设个数是 x,也就是求 2^x = n,即 x = log2n,所以这个循环的时间复杂度就是 O(logn)。 最后呢,我们来看看下面的这个例子,借助这段代码来详细的说一下我们如何对其时间复杂度进行详细的分析: 1 2 3 4 5 6 7 8 9 10 11 12 a = 1 b = 2 c = 3 for i in range(n):    for j in range(n):  x = i * i y = j * j z = i * j for k in range(n):    u = a * k + b    v = c * cd = 4 上面的代码没有任何意义,甚至不是一个可运行的代码,我只是用来说明你在以后如何对代码进行执行分析,关于代码本身可不可以运行,就不需要你在这关心了。 上面的代码其实我们要分的话可以分成 4 部分:第 1 部分是 a,b,c 这 3 个赋值语句,执行次数也就是 3 次;第二部分是 3n^2,因为是循环结构,里面有 x,y,z 这 3 个赋值语句,每个语句执行了 n^2 次;第 3 部分是 2n,因为里面是 2 个赋值语句,每条语句被执行了 n 次;最后第 4 部分是常数 1,只有 d 这么 1 条赋值语句。所以我们得到的 T(n ) = 3+3n^2 +2n+1 = 3n^2+2n+4,看到指数项,我们自然的发现是 n^2 做主导,当 n 增大时,后面两项可以忽略掉,所以这个代码片段的数量级就是 O(n^2)。 空间复杂度 类比于时间复杂度的讨论,一个算法的空间复杂度是指该算法所耗费的存储空间,计算公式计作:S(n) = O(f(n))。其中 n 也为数据的规模,f(n) 在这里指的是 n 所占存储空间的函数。 一般情况下,我们的程序在机器上运行时,刨去需要存储程序本身的输入数据等之外,还需要存储对数据操作的「存储单元」。如果输入数据所占空间和算法无关,只取决于问题本身,那么只需要分析算法在实现过程中所占的「辅助单元」即可。如果所需的辅助单元是个常数,那么空间复杂度就是 O(1)。 空间复杂度其实在这里更多的是说一下这个概念,因为当今硬件的存储量级比较大,一般不会为了稍微减少一点儿空间复杂度而大动干戈,更多的是去想怎么优化算法的时间复杂度。所以我们在日常写代码的时候就衍生出了用「空间换时间」的做法,并且成为常态。比如我们在求解斐波那契数列数列的时候我们可以直接用公式去递归求,用哪个求哪个,同样也可以先把很多结果都算出来保存起来,然后用到哪个直接调用,这就是典型的用空间换时间的做法,但是你说这两种具体哪个好,伟大的马克思告诉我们「具体问题具体分析」。 后话 如果上面的文章你仔细看了的话,你会发现我不是直接上来就告诉你怎么去求时间复杂度,而是从问题的产生,到思考解决的办法,到 “马后炮”,再到 T(n),最后到 O(n) 一步一步来的。这样做的原因呢有两个:一是为了让你了解大 O 到底是怎么来的,有时候搞明白了由来,对于你接下来的学习和理解有很大的帮助;二是为了让这个文章看起来不是那么枯燥,我觉得很多时候上来扔给你一堆概念术语,很容易就让人在刚看到它的时候就打起了退堂鼓,循序渐进的来,慢慢引导着更容易接受一些。 很多人从大学到工作,代码写了不少依然不会估算时间复杂度,我感觉倒不是学不会,而是内心没有重视起来。你可能觉得计算机的更新换代很快,CPU 处理速度的能力越来越棒,没必要在一些小的方面斤斤计较,其实我觉得你是 too young too naive。我们随便来算一个简单的例子:有两台电脑,你的电脑的运算速度是我的电脑的 100 倍,同样一道问题,明明稍微想一想用 O(n) 可以做出来,你偏偏要懒,直接暴力 O(n^2),那么当 n 的数据稍微增大一些,比如上万上十万,到底谁的运算速度快还用我再告诉你吗? 所以今后在写算法的时候,请好好学会用时间复杂度估算一下自己的代码,然后想想有没有更有效率的方法去改进它,你只要这样做了,相信慢慢的你的代码会写的越来越好,头会越来越秃。(逃 最后说一点的是,估算算法的复杂度这件事你不要指望一下子看了一篇文章就想弄懂,这个还是要有意识的多练,比如看到一个程序的时候有意识的估算一下它的复杂度,准备动手写代码的时候也想想有没有更好的优化方法,有意识的练习慢慢就会来了感觉。这篇文章我就用了几个小例子,大概的估算方式就是这样。 作者: Rocky0429 某 985 计算机在读研究生,ACM 退役狗 & 亚洲区域赛银奖划水选手。喜欢 Python 和算法。个人公号「Python空间」。 原文链接: 循序渐进带你学习时间复杂度和空间复杂度

2018/11/29
articleCard.readMore

Linux 命令 --- cd

简介 cd 命令全称 “change directory”, 用于切换当前工作目录, 是 Linux 下最基础的命令之一. 语法 1 cd [path] 其中: [path] 表示目标路径, 可以是相对路径, 也可以是绝对路径. 举例 跳转到指定目录 1 cd /etc 跳转到根目录 1 cd / 跳转到用户主目录 1 cd ~ 1 cd 上述两个命令效果相同. 跳转到上一层目录 1 cd ../ 跳转到上一次跳转的目录 (同一 shell 内有效) 1 cd -1

2018/11/29
articleCard.readMore

Linux 命令 --- man

前言 man 命令可以说是初学 Linux 系统最应该接触的命令了. 使用 man 命令, 你几乎可以查看任何命令的用户手册. 简介 man 命令的全称叫 “manual”, 是 Linux 下的帮助指令, 可用于查看 Linux 中各种指令的帮助信息. 语法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 用法: man [选项...] [章节] 手册页... -C, --config-file=文件 使用该用户设置文件 -d, --debug 输出调试信息 -D, --default 将所有选项都重置为默认值 --warnings[=警告] 开启 groff 的警告 主要运行模式: -f, --whatis 等同于 whatis -k, --apropos 等同于 apropos -K, --global-apropos 在所有页面中搜索文字 -l, --local-file 把“手册页”参数当成本地文件名来解读 -w, --where, --path, --location 输出手册页的物理位置 -W, --where-cat, --location-cat 输出 cat 文件的物理位置 -c, --catman 由 catman 使用,用来对过时的 cat 页重新排版 -R, --recode=编码 以指定编码输出手册页源码 寻找手册页: -L, --locale=区域 定义本次手册页搜索所采用的区域设置 -m, --systems=系统 使用来自其它系统的手册页 -M, --manpath=路径 设置搜索手册页的路径为 PATH -S, -s, --sections=列表 使用以半角冒号分隔的章节列表 -e, --extension=扩展 将搜索限制在扩展类型为“扩展”的手册页之内 -i, --ignore-case 查找手册页时不区分大小写字母 (默认) -I, --match-case 查找手册页时区分大小写字母 --regex 显示所有匹配正则表达式的页面 --wildcard 显示所有匹配通配符的页面 --names-only 使得 --regex 和 --wildcard 仅匹配页面名称,不匹配描述信息 -a, --all 寻找所有匹配的手册页 -u, --update 强制进行缓存一致性的检查 --no-subpages 不要尝试子页面,如“man foo bar” => “man foo-bar” 控制格式化的输出: -P, --pager=PAGER 使用 PAGER 程序显示输出文本 -r, --prompt=字符串 给 less 分页器提供一个提示行 -7, --ascii 显示某些 latin1 字符的 ASCII 翻译形式 -E, --encoding=编码 使用选中的输出编码 --no-hyphenation, --nh 关闭连字符 --no-justification, --nj 禁止两端对齐 -p, --preprocessor=字符串 字符串表示要运行哪些预处理器: e - [n]eqn, p - pic, t - tbl, g - grap, r - refer, v - vgrind -t, --troff 使用 groff 对手册页排版 -T, --troff-device[=设备] 使用 groff 的指定设备 -H, --html[=浏览器] 使用 www-browser 或指定浏览器显示 HTML 输出 -X, --gxditview[=分辨率] 使用 groff 并通过 gxditview (X11) 来显示: -X = -TX75, -X100 = -TX100, -X100-12 = -TX100-12 -Z, --ditroff 使用 groff 并强制它生成 ditroff -?, --help 显示此帮助列表 --usage 显示一份简洁的用法信息 -V, --version 打印程序版本 选项完整形式所必须用的或是可选的参数,在使用选项缩写形式时也是必须的或是可选的。 示例 使用 man 命令查看 man 命令自己的用户手册 1 man man 执行效果:

2018/11/29
articleCard.readMore