<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Blog - Renfei Song</title><description></description><link>https://www.renfei.org/blog/</link><atom:link href="https://www.renfei.org/blog/feed.xml" rel="self" type="application/rss+xml" /><item><title>使用 iOS 8 Spring Animation API 创建动画</title><description>&lt;p&gt;Spring Animation 是一种特殊的动画曲线，自从 iOS 7 开始被广泛应用在系统动画中。&lt;/p&gt;&lt;p&gt;下图中演示的系统自带的动画效果，都使用了 Spring Animation：&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;//img.renfei.org/2014/09/0-example-2.gif&quot; alt=&quot;0-example-2&quot; /&gt;  &lt;img src=&quot;//img.renfei.org/2014/09/0-example-1.gif&quot; alt=&quot;0-example-1&quot; /&gt;&lt;/p&gt;&lt;p&gt;事实上，从 iOS 7 起几乎所有的系统动画都用的是 Spring Animation，包括 App 文件夹打开/关闭效果、键盘弹出效果、UISwitch 控件的开关效果、不同 View Controller 之间的 Push 动画、Modal 出现和消失的动画、Siri 的出现和消失动画，等等。下图为 Spring Animation 和普通的动画的运动曲线的对比：&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;//img.renfei.org/2014/09/cmp.png&quot; alt=&quot;cmp&quot; /&gt;&lt;/p&gt;&lt;p&gt;为了更加直观，我做了一组演示图，从左至右分别列出了 Spring Animation, Ease-Out Animation 和 Linear Animation 的动画效果：&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;//img.renfei.org/2014/09/spring-1.gif&quot; alt=&quot;spring-1&quot; /&gt;&lt;/p&gt;&lt;p&gt;可以看到，和系统自带的 ease-out 效果相比，Spring Animation 前期速度增加得更快，在动画时间一定的前提下，给人感觉更加快速、干净。&lt;/p&gt;&lt;h3 id=&quot;spring-animation-api&quot;&gt;Spring Animation API&lt;/h3&gt;&lt;p&gt;自 iOS 8 开始，Apple 公开了 Spring Animation 的 API，开发者也可以使用简单的代码创建这类动画效果了：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-objective-c&quot; data-lang=&quot;objective-c&quot;&gt;&lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;animateWithDuration:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NSTimeInterval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;duration&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;delay:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NSTimeInterval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;delay&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;usingSpringWithDamping:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CGFloat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;dampingRatio&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;initialSpringVelocity:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CGFloat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;velocity&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;options:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UIViewAnimationOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;animations:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;animations&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;completion:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;BOOL&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;finished&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;completion&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;该方法是&lt;code&gt;UIView&lt;/code&gt;的类方法。&lt;/p&gt;&lt;p&gt;Spring Animation 的 API 和一般动画相比多了两个参数，分别是&lt;code&gt;usingSpringWithDamping&lt;/code&gt;和&lt;code&gt;initialSpringVelocity&lt;/code&gt;。&lt;/p&gt;&lt;h4 id=&quot;usingspringwithdamping-&quot;&gt;usingSpringWithDamping 参数&lt;/h4&gt;&lt;p&gt;&lt;code&gt;usingSpringWithDamping&lt;/code&gt;的范围为&lt;code&gt;0.0f&lt;/code&gt;到&lt;code&gt;1.0f&lt;/code&gt;，数值越小「弹簧」的振动效果越明显。下图演示了在&lt;code&gt;initialSpringVelocity&lt;/code&gt;为&lt;code&gt;0.0f&lt;/code&gt;的情况下，&lt;code&gt;usingSpringWithDamping&lt;/code&gt;分别取&lt;code&gt;0.2f&lt;/code&gt;，&lt;code&gt;0.5f&lt;/code&gt;和&lt;code&gt;1.0f&lt;/code&gt;的情况。&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;//img.renfei.org/2014/09/spring-2.gif&quot; alt=&quot;spring-2&quot; /&gt;&lt;/p&gt;&lt;h4 id=&quot;initialspringvelocity-&quot;&gt;initialSpringVelocity 参数&lt;/h4&gt;&lt;p&gt;&lt;code&gt;initialSpringVelocity&lt;/code&gt;则表示初始的速度，数值越大一开始移动越快。下图演示了在&lt;code&gt;usingSpringWithDamping&lt;/code&gt;为&lt;code&gt;1.0f&lt;/code&gt;时，&lt;code&gt;initialSpringVelocity&lt;/code&gt;分别取&lt;code&gt;5.0f&lt;/code&gt;，&lt;code&gt;15.0f&lt;/code&gt;和&lt;code&gt;25.0f&lt;/code&gt;的情况。值得注意的是，初始速度取值较高而时间较短时，也会出现反弹情况。&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;//img.renfei.org/2014/09/spring-3.gif&quot; alt=&quot;spring-3&quot; /&gt;&lt;/p&gt;&lt;h3 id=&quot;section&quot;&gt;使用&lt;/h3&gt;&lt;p&gt;Spring Animation 是线性动画或 ease-out 动画的理想替代品。由于 iOS 本身大量使用的就是 Spring Animation，用户已经习惯了这种动画效果，因此使用它能使 App 让人感觉更加自然，用 Apple 的话说就是「instantly familiar」。此外，Spring Animation 不只能对位置使用，它适用于所有可被添加动画效果的属性。&lt;/p&gt;</description><pubDate>Fri, 26 Sep 2014 00:00:00 +0800</pubDate><link>https://www.renfei.org/blog/ios-8-spring-animation.html</link><guid isPermaLink="true">https://www.renfei.org/blog/ios-8-spring-animation.html</guid></item><item><title>用纯 CSS 为 HTML checkbox 添加自定义样式及动画效果</title><description>&lt;p&gt;一般情况下，&lt;code&gt;&amp;lt;input type=&quot;checkbox&quot;&amp;gt;&lt;/code&gt;元素（也包括一部分其他&lt;code&gt;input&lt;/code&gt;元素）是由操作系统（而非浏览器）进行渲染的。在不同操作系统下，这类特殊元素的样式往往和系统本身的风格一致，和浏览器无关。也正是由于这种机制，开发者无法像一般元素那样使用 CSS 来修饰这类元素。这里介绍一种利用纯 CSS 实现自定义 checkbox 样式的方法。思路很简单：由于控件所对应的&lt;code&gt;label&lt;/code&gt;元素是可以点击并切换控件状态的，而&lt;code&gt;label&lt;/code&gt;元素的样式又可以自由设定，因此我们可将&lt;code&gt;input&lt;/code&gt;元素隐藏，通过&lt;code&gt;label&lt;/code&gt;元素实现交互。&lt;/p&gt;&lt;p&gt;HTML 代码如下：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;checkbox&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;checkbox&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;checkbox&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;checkbox-wrapper&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;label&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;for=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;checkbox&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;checkbox-label&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/label&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;metro-ui-&quot;&gt;样式一：Metro UI 风格&lt;/h2&gt;&lt;p&gt;效果：&lt;/p&gt;&lt;div class=&quot;demo-1&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;checkbox&quot; class=&quot;checkbox&quot; /&gt;&lt;div class=&quot;checkbox-wrapper&quot;&gt;&lt;label for=&quot;checkbox&quot; class=&quot;checkbox-label&quot;&gt;&lt;/label&gt;&lt;/div&gt;&lt;/div&gt;&lt;style&gt;.demo-1 .checkbox-wrapper {width: 80px;height: 32px;position: relative;display: inline-block;overflow: hidden;}.demo-1 .checkbox {display: none;}.demo-1 .checkbox-label:before,.demo-1 .checkbox-label:after {font-family: sans-serif;font-size: 13px;color: #ffffff;position: absolute;font-weight: bold;line-height: 32px;height: 32px;width: 40px;text-align: center;}.demo-1 .checkbox-label:before {content: &#39;ON&#39;;left: -40px;background: #45b6af;}.demo-1 .checkbox-label:after {content: &#39;OFF&#39;;right: -40px;background: #f3565d;}.demo-1 .checkbox-label {display: block;position: absolute;left: 0;width: 40px;height: 32px;transition: all .4s ease;cursor: pointer;background: #dddddd;}.demo-1 .checkbox:checked + .checkbox-wrapper .checkbox-label {left: 40px;}&lt;/style&gt;&lt;p&gt;CSS 代码如下：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-css&quot; data-lang=&quot;css&quot;&gt;&lt;span class=&quot;nc&quot;&gt;.checkbox-wrapper&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;80px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;32px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;relative&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;inline&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;overflow&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;hidden&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.checkbox&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;none&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.checkbox-label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:before&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.checkbox-label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:after&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sans-serif&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;13px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#ffffff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;absolute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;bold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;line-height&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;32px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;32px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;40px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;text-align&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;center&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.checkbox-label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:before&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;ON&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;-40px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#45b6af&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.checkbox-label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:after&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;OFF&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;-40px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#f3565d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.checkbox-label&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;absolute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;40px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;32px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;all&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;.4s&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ease&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;pointer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#dddddd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.checkbox&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:checked&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.checkbox-wrapper&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.checkbox-label&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;40px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;ios-7-&quot;&gt;样式二：iOS 7 风格&lt;/h2&gt;&lt;p&gt;效果：&lt;/p&gt;&lt;div class=&quot;demo-2&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;checkbox-2&quot; class=&quot;checkbox&quot; /&gt;&lt;div class=&quot;checkbox-wrapper&quot;&gt;&lt;label for=&quot;checkbox-2&quot; class=&quot;checkbox-label&quot;&gt;&lt;/label&gt;&lt;/div&gt;&lt;/div&gt;&lt;style&gt;.demo-2 .checkbox-wrapper {width: 58px;height: 32px;position: relative;display: inline-block;background: #ffffff;border-radius: 16px;transition: all .3s ease-out;box-shadow: 0px 0px 0px 2px #ddd;}.demo-2 .checkbox {display: none;}.demo-2 .checkbox-label {display: block;position: absolute;left: 0px;top: 0px;width: 32px;height: 32px;cursor: pointer;background: #ffffff;border-radius: 16px;box-shadow: 0px 2px 3px rgba(0,0,0,0.2);transition: all .3s ease-out;}.demo-2 .checkbox:checked + .checkbox-wrapper {background: #0bd318;box-shadow: 0px 0px 0px 2px #0bd318;}.demo-2 .checkbox:checked + .checkbox-wrapper .checkbox-label {left: 26px;}&lt;/style&gt;&lt;p&gt;CSS代码如下：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-css&quot; data-lang=&quot;css&quot;&gt;&lt;span class=&quot;nc&quot;&gt;.checkbox-wrapper&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;58px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;32px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;relative&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;inline&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#ffffff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;radius&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;16px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;all&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;.3s&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ease&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;box&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shadow&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0px&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0px&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0px&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2px&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#ddd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.checkbox&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;none&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.checkbox-label&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;absolute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;32px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;32px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;pointer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#ffffff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;radius&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;16px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;box&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shadow&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0px&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2px&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3px&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rgba&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;all&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;.3s&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ease&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.checkbox&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:checked&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.checkbox-wrapper&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#0bd318&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;box&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shadow&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0px&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0px&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0px&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2px&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#0bd318&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.checkbox&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:checked&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.checkbox-wrapper&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.checkbox-label&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;26px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;值得一提的是，这个例子虽然比较完美的还原了 iOS 7 中 UISwitch 控件的外观，但是动画曲线却还有些差距。iOS 7 中使用的是一种名为 String Animation 的动画（该 API 在 iOS 8 中已经公开），仔细看的话可以发现开始滑块移动很快，到后面逐渐变慢，并且结束时还有小幅度的「反弹」效果。这里我们为了方便，使用 ease-out 来模拟。&lt;/p&gt;&lt;h2 id=&quot;jsfiddle&quot;&gt;JSFiddle&lt;/h2&gt;&lt;p&gt;点击&lt;a href=&quot;http://jsfiddle.net/gnu1qqnq/&quot;&gt;此处&lt;/a&gt;查看本教程在 JSFiddle 的示范代码。&lt;/p&gt;</description><pubDate>Sat, 23 Aug 2014 00:00:00 +0800</pubDate><link>https://www.renfei.org/blog/styling-html-checkboxes-using-pure-css.html</link><guid isPermaLink="true">https://www.renfei.org/blog/styling-html-checkboxes-using-pure-css.html</guid></item><item><title>腾讯暑期夏令营之旅</title><description>&lt;p&gt;2013.8.22 — 8.24 三天时间，我参加了腾讯暑期夏令营。这几天里腾讯为同学们准备了一系列的讲座和实践活动，收获很大。不仅腾讯的几个主营业务都有所涉及，而且讲座的水平也很高，所传达的信息很丰富，为我们从内部视角了解腾讯建立了一个渠道。&lt;/p&gt;&lt;p&gt;首先是腾讯 TEG（技术工程事业群）的 BOSS 王巨宏的总体介绍。第一，他传达了一种「以人为本」的核心设计理念。他说，要做产品，就要做到最好，互联网产品只有第一没有第二，我们要把产品做到自己感动得哭。同时，市场上许多产品的功能和用途都是类似的，如何防止被模仿？答案是：把产品做到极致。第二，毫无疑问，未来 IT 业发展的方向是移动互联网。第三，腾讯之所以把产品线做得很广，是希望占领市场，增加用户黏度，因为公司的产品不形成闭环，就容易受损。例如，腾讯的电商部门是赔钱的，但是腾讯坚决要做，就是处于这种原因。用王老师的比喻就是，自己的钱不经过自己的手进入自己的口袋，怎么能安心呢？&lt;/p&gt;&lt;p&gt;在接下来的几天时间里，我还分别听取了关于基础架构、游戏、微云和社交网络以及创新思维和微信方面的报告。&lt;/p&gt;&lt;h2 id=&quot;section&quot;&gt;基础架构&lt;/h2&gt;&lt;p&gt;介绍基础架构（Cloud Foundation）的专家是庄泗华。他简单介绍了腾讯「内部云」的必要性和原理。现在腾讯的产品线非常长，也非常复杂。许多产品有一些代码都是完全相似或者相同的，因此可以提取这些逻辑的共同点，简化开发流程，避免重复劳动。同时，借助腾讯的内部云，很多工作不再需要开发人员手工完成，开发、调度、修改的的成本也会大大降低，有助于提高开发效率，节省人力成本。&lt;/p&gt;&lt;h2 id=&quot;section-1&quot;&gt;游戏&lt;/h2&gt;&lt;p&gt;介绍游戏的专家是互动娱乐研发部的总经理聂志明。他以《深入浅出谈游戏》为题，简要介绍了开发一款游戏所面临的挑战和要解决的问题。实际上，一款游戏的运营除了表面的游戏内容以外，还有许多其他至关重要的因素，比如稳定性、性能、防外挂考虑等。&lt;/p&gt;&lt;p&gt;第一，为了保证稳定性，我们需要使用良好、易扩展的程序结构、尽量使用多线程，同时配备重建数据、恢复信息的能力。&lt;/p&gt;&lt;p&gt;第二，为了保证性能，我们需要同时提高客户端、服务器、数据库以及网络同步的能力；而这其中就涉及了减少阻塞调用（采取异步方式）、过载保护（排队系统）、减少内存拷贝、使用合适的架构等多种问题和技术。&lt;/p&gt;&lt;p&gt;第三，对于外挂，国内外挂有时可以决定产品命运，因此必须特别采取措施。比如，让大部分游戏逻辑在服务器端判断，客户端只是呈现，或者同步逻辑里加入对累计时间误差的控制，等等。&lt;/p&gt;&lt;h2 id=&quot;section-2&quot;&gt;微云&lt;/h2&gt;&lt;p&gt;接下来是欧阳老师关于微云的介绍。现在云存储还是一个很新兴的市场，虽然技术已经比较成熟，但是数据显示，国内使用云存储的用户只有不到 9%，因此还有很大的拓展空间。也正是因为这一点，各大厂商都在绞尽脑汁地开发和互相借鉴各种功能，导致云存储服务同质化比较严重。目前许多厂商都在「拼容量」，例如 360 推出了一个 360G 的盘，百度又推出了一个 T 盘（1T 容量），形势瞬息万变。&lt;/p&gt;&lt;p&gt;但是，腾讯对于盲目扩增容量是谨慎的。欧阳老师主要探讨的就是，云的容量到底有多重要；如果盲目扩大容量，可能影响其他正常使用的用户的体验。然而，用户对云服务的忠诚度是很低的，这又迫使服务提供商想办法去增加用户粘性，吸引用户。实际上并非单纯免费、容量大就可以抓住用户，我们需要做的是挖掘用户的实际诉求。&lt;/p&gt;&lt;h2 id=&quot;section-3&quot;&gt;社交网络&lt;/h2&gt;&lt;p&gt;然后是腾讯的专家研究员岳亚丁，来和我们探讨关于社交网络。他的报告分类简单，问题突出，短时间内使我们对社交网络相关的算法，以及面临的挑战有了一个初步认识，把该抛出的问题都抛出了，说的靠谱。&lt;/p&gt;&lt;p&gt;社交网络的分析向来是最复杂的一个部分，涉及到许多技术点，比如社区发现，LBSN，个性推荐，大数据处理等。充分应用这些技术，可以预测用户场景和行为，计算用户的社会属性；同时还可以识别滥用和恶意注册（虽然行为容易伪造，但是社会关系和沟通不易伪造）。同时，这一领域也存在许多挑战，例如用于大数据的高扩展性算法、数据预处理、如何构建特征（select feature）等等，这些都是机器学习领域的难题。因为时间关系不可能说的特别详细，但是起到了一个很好的启蒙作用，为我们的继续研究和学习提供了一个基础。&lt;/p&gt;&lt;h2 id=&quot;section-4&quot;&gt;微信背后的故事&lt;/h2&gt;&lt;p&gt;最后在广州，我们听取了微信产品部助理总经理刘乐君带给我们的主题演讲《微信背后的故事》。他为我们完整地展现了一个产品的研发流程：从最初的 15 人团队、历经 2 个月做出产品，到 1 年发布了 44 个版本、433 天获得一亿用户，再到两亿用户、100 人团队，呈现出了一个奋斗、发展、学习转变的路程。&lt;/p&gt;&lt;p&gt;一个团队包含很多个角色，比如产品经理、程序经理，还有交互设计师、视觉设计师，以及架构师、开发、运维、测试工程师等等。这些人员典型的日常行为，以及如何配合，他都从产品开发的角度进行了详细的介绍。最后，刘老师从团队的高度，和我们分享了提高团队效率的方法和技巧。&lt;/p&gt;&lt;h2 id=&quot;section-5&quot;&gt;学长学姐谈经验&lt;/h2&gt;&lt;p&gt;此外，22日下午腾讯还邀请了入职一段时间的「学长学姐」，来和我们交流工作经验。大家提问非常积极踊跃，从实习到工作，从经验到技巧，无一不包。给我印象最深刻的有如下几点：&lt;/p&gt;&lt;p&gt;第一，要想得到赏识，就要超过预期。腾讯的技术人员是分等级的，比如 T1, T2 等。刚入职的一般是 T1-1，但是有的毕业生一来就能达到 T1-2，这就是因为他们在实习的时候表现突出，超出了领导的期望。&lt;/p&gt;&lt;p&gt;第二，面对竞争，要学会扬长避短。每个职位和工作对于各种能力的要求都是有偏向的，要主动地发挥自己的长处。&lt;/p&gt;&lt;p&gt;第三，对于一份工作，不要先下结论自己喜欢不喜欢，要先试着去爱。先认真做过再确定自己是不是热爱。也不要羡慕他人有多牛，多么有成就，该有的都会有的，做好自己就行了。年轻的时候要多多尝试，年轻不怕失败，建立自己的核心竞争力。&lt;/p&gt;&lt;p&gt;第四，腾讯的工作氛围是比较轻松的，内部转部门也相对比较容易，言论相对也比较自由一些（据说有的公司员工经常在年终评中「企业文化理解」部分被莫名其妙地扣分Orz）。&lt;/p&gt;&lt;h2 id=&quot;section-6&quot;&gt;模拟产品研发&lt;/h2&gt;&lt;p&gt;除了讲座，我们还开展了一些其他有意思的活动，其中收获最大的就是模拟产品研发流程。&lt;/p&gt;&lt;p&gt;在活动开始，负责人先给我们讲清了活动的任务和目标：每个团队有 15 人，需要在一小时时间内给 QQ 邮箱开发一个新功能，并且完成功能说明、开发进度、人员分配等计划。过程中有 QQ 邮箱的产品研发团队进行观察（和少量指导），汇报时由他们和其他同学进行提问。活动进行时，大家的热情和参与度都非常高。&lt;/p&gt;&lt;p&gt;我们队的做法是，首先头脑风暴，集思广益，每个人都说一说自己对于 QQ 邮箱的改进建议。然后，我们把这些建议汇总起来，再分别讨论每个功能建议的可行性，选出最有代表性的一个，再针对这个功能制定详细的方案。整个过程井井有条，分工明确，有专门负责计时的同学和记录的同学，同时每个人都会发表自己的观点。评委对于这种高效、积极的合作和井井有条的思考方式给予了肯定和表扬。虽然只有一个小时，不过瘾，但是大家体验了一把产品经理的感觉，对于以后的工作和学习增添了一丝向往之情。&lt;/p&gt;&lt;p&gt;当然，主要的收获还是来自评委点评，这个环节类似于一次群面。&lt;/p&gt;&lt;p&gt;第一，活动重在过程，而非结果。得出新奇的观点必然很好，但是这并不容易。我们更需要把握的是切入点，如何切中要害，切入用户的痛点，这是关键。&lt;/p&gt;&lt;p&gt;第二，需要精准的定位分析。开发一个新功能，必须很好地抓准目标用户，涉及的功能最好是用户的强需求，这样才能有力地促使他们去使用。不一定要考虑大而全，不能为了创新而去创新。&lt;/p&gt;&lt;p&gt;第三，表达的艺术。都说，善于表达的程序员才可能成为伟大的程序员，有想法固然重要，但是把它清晰地表述出来才能让你的想法成为现实。这要求团队内成员观点一致，起码不要互相打断。此外，在讨论的时候，不能盲目地为了反驳而反驳，产品功能的讨论不是辩论会或者市场推销，要先去考虑对方观点的合理性。&lt;/p&gt;&lt;p&gt;第四，团队，既然是团队合作，每个同学都必须发表自己的意见，参与讨论和思考。一个团队最忌讳的就是有些成员随波逐流，只做事不表态。这类成员称为团队的「鸡肋」——虽然有一些用处，但对团队攻克问题并没有任何好处。&lt;/p&gt;</description><pubDate>Sat, 26 Apr 2014 00:00:00 +0800</pubDate><link>https://www.renfei.org/blog/tencent-summercamp.html</link><guid isPermaLink="true">https://www.renfei.org/blog/tencent-summercamp.html</guid></item><item><title>Ubuntu 配置 Apache, MySQL, PHP 以及 phpMyAdmin 过程记录</title><description>&lt;p&gt;在 Ubuntu Server 的纯命令行界面下成功配置好了 LAMP 环境，记录过程，供有相同需求的朋友们参考。&lt;/p&gt;&lt;h2 id=&quot;apache&quot;&gt;Apache&lt;/h2&gt;&lt;h3 id=&quot;section&quot;&gt;安装&lt;/h3&gt;&lt;pre&gt;&lt;code&gt;$ sudo apt-get install apache2&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;装好后，配置文件应该位于&lt;code&gt;/etc/apache2&lt;/code&gt;中，默认情况下无需修改即可使用。默认的网站目录为&lt;code&gt;/var/www/&lt;/code&gt;。&lt;/p&gt;&lt;p&gt;启动 Apache 的方法：&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ sudo /etc/init.d/apache2 start&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;重启 Apache：&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ sudo /etc/init.d/apache2 restart&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;停止 Apache：&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ sudo /etc/init.d/apache2 stop&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;上述命令也可以写成这种形式（以启动为例）：&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ sudo service apache2 start&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;section-1&quot;&gt;测试&lt;/h3&gt;&lt;p&gt;装好并启动 Apache 服务后，本地服务器应该就可以用了。可以利用&lt;code&gt;curl&lt;/code&gt;访问 localhost 来测试：&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ curl localhost&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Apache 的错误日志文件默认为&lt;code&gt;/var/log/apache2/error.log&lt;/code&gt;。&lt;/p&gt;&lt;h3 id=&quot;section-2&quot;&gt;其他&lt;/h3&gt;&lt;p&gt;启动的时候可能会出现如下警告：&lt;/p&gt;&lt;pre&gt;&lt;code&gt;apache2: Could not determine the server&#39;s fully qualified domain name, using 127.0.0.1 for ServerName&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;说明你没有指定&lt;code&gt;ServerName&lt;/code&gt;。如果想去掉这个错误，可以修改&lt;code&gt;/etc/apache2/apache2.conf&lt;/code&gt;文件：&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ sudo vi /etc/apache2/apache2.conf&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;添加如下行：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-apache&quot; data-lang=&quot;apache&quot;&gt;&lt;span class=&quot;nb&quot;&gt;ServerName&lt;/span&gt; localhost&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;php&quot;&gt;PHP&lt;/h2&gt;&lt;h3 id=&quot;php-1&quot;&gt;安装 PHP&lt;/h3&gt;&lt;p&gt;同样使用&lt;code&gt;apt-get&lt;/code&gt;命令安装 PHP：&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ sudo apt-get install php5&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;执行之后，PHP 应该就已经部署完毕了。可以使用&lt;code&gt;phpinfo()&lt;/code&gt;函数来测试 PHP 是否已经就绪：&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ sudo vi /var/www/phpinfo.php&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;在文件里输入：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;phpinfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后&lt;code&gt;curl localhost/phpinfo.php&lt;/code&gt;来查看。&lt;/p&gt;&lt;h3 id=&quot;php-&quot;&gt;安装其他 PHP 模块&lt;/h3&gt;&lt;pre&gt;&lt;code&gt;$ sudo apt-get install php5-mysql php5-curl php5-gd php5-intl php-pear php5-imagick php5-imap php5-mcrypt php5-memcache php5-ming php5-ps php5-pspell php5-recode php5-snmp php5-sqlite php5-tidy php5-xmlrpc php5-xsl&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;mysql&quot;&gt;MySQL&lt;/h2&gt;&lt;pre&gt;&lt;code&gt;$ sudo apt-get install mysql-server&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;安装过程中需要设定 root 账户的密码。至此，LAMP 环境已经配好。&lt;/p&gt;&lt;h2 id=&quot;phpmyadmin&quot;&gt;phpMyAdmin&lt;/h2&gt;&lt;h3 id=&quot;section-3&quot;&gt;安装&lt;/h3&gt;&lt;p&gt;首先执行：&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ sudo apt-get install phpmyadmin&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;安装过程比较长，会有几个选项，依次如下：&lt;/p&gt;&lt;p&gt;（1）选择服务器软件。这里选择刚刚安装的 Apache2。&lt;/p&gt;&lt;p&gt;（2）选择手动设定。&lt;/p&gt;&lt;p&gt;（3）输入之前安装 MySQL 时设的 MySQL root 账户的密码。&lt;/p&gt;&lt;p&gt;（4）设定 phpMyAdmin 的登录密码。&lt;/p&gt;&lt;p&gt;注意，phpMyAdmin 的配置文件为：&lt;code&gt;/etc/phpmyadmin/config.inc.php&lt;/code&gt;。&lt;/p&gt;&lt;h3 id=&quot;pma--apache&quot;&gt;包含 pma 的配置文件到 Apache&lt;/h3&gt;&lt;p&gt;安装后，还不能立即使用 phpMyAdmin，因为它不在网站目录下。为了正常使用，只需把 phpMyAdmin 的配置文件包含到 Apache 的配置中。编辑&lt;code&gt;apache2.conf&lt;/code&gt;：&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ sudo vi /etc/apache2/apache2.conf&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;在文件中添加如下行：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-apache&quot; data-lang=&quot;apache&quot;&gt;&lt;span class=&quot;nb&quot;&gt;Include&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;/etc/phpmyadmin/apache.conf&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;重启服务器：&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ sudo service apache2 restart&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;至此，你就可以通过&lt;code&gt;yourdomain/phpmyadmin&lt;/code&gt;来访问 phpMyAdmin 了。&lt;/p&gt;&lt;h3 id=&quot;mcrypt-&quot;&gt;mcrypt 模块丢失的错误&lt;/h3&gt;&lt;p&gt;一个 bug 可能导致访问 phpMyAdmin 时出现 mcrypt 模块丢失的错误。如果遇到，则可以采用下面的办法修复：&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ sudo ln -s /etc/php5/conf.d/mcrypt.ini /etc/php5/mods-available/$ sudo php5enmod mcrypt$ sudo service apache2 restart&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;index&quot;&gt;全局禁用 Index&lt;/h2&gt;&lt;p&gt;Index 就是访问一个不存在 index.html、index.php 等文件的目录时服务器列出的文件列表，这样会对用户展示文件结构，如果想禁用，可以修改 Apache 的配置文件：&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ sudo vi /etc/apache2/apache2.conf&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;找到并修改为&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-apache&quot; data-lang=&quot;apache&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Directory&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/var/www/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Options&lt;/span&gt; -Indexes&lt;span class=&quot;nb&quot;&gt;Options&lt;/span&gt; FollowSymLinks&lt;span class=&quot;nb&quot;&gt;AllowOverride&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Require&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;all&lt;/span&gt; granted&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Directory&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description><pubDate>Wed, 02 Apr 2014 00:00:00 +0800</pubDate><link>https://www.renfei.org/blog/set-up-apache-mysql-php-phpmyadmin-on-ubuntu-server.html</link><guid isPermaLink="true">https://www.renfei.org/blog/set-up-apache-mysql-php-phpmyadmin-on-ubuntu-server.html</guid></item><item><title>Slick2D 简介（二）：StateBasedGame 实例讲解</title><description>&lt;p&gt;&lt;a href=&quot;http://www.frums.nl&quot;&gt;Jelle Voost&lt;/a&gt; 上传了一系列 &lt;a href=&quot;https://www.youtube.com/user/frumsnl?feature=watch&quot;&gt;Slick2D 视频教程&lt;/a&gt;，做得非常棒。为了让更多的人看到，经过他的授权，我把教程的&lt;a href=&quot;https://www.youtube.com/watch?v=UEGdRY10HP8&quot;&gt;第二部分&lt;/a&gt;下载并发布到土豆网上，方便大家观看。此外，大家也可以去他的网站看一看。&lt;/p&gt;&lt;p&gt;这一部分教程主要以一个实际例子讲解了利用 Slick2D 引擎内置的&lt;code&gt;StateBasedGame&lt;/code&gt;类写一个简单游戏的方法，会涉及到逐帧重绘、数据更新等基本概念的运用，以及一些将贯穿开发始终的技术的应用。视频解说是英文的，为了方便大家观看，在这里我先把基本技术讲解一下：&lt;/p&gt;&lt;h2 id=&quot;section&quot;&gt;基本技术&lt;/h2&gt;&lt;p&gt;StateBasedGame 的思想就是一个游戏中包含若干个 State，每个 State 就相当于之前讲的一个 BasicGame，同时可以在这些 State 之间切换。因此，一个 StateBasedGame 本身不再含有&lt;code&gt;update&lt;/code&gt;、&lt;code&gt;render&lt;/code&gt;等方法（这些方法随后会出现在每个 State 中），它只需要包含&lt;code&gt;main&lt;/code&gt;和&lt;code&gt;initStatesList&lt;/code&gt;方法。例如代码框架如下：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SetupClass&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StateBasedGame&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetupClass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SlickException&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initStatesList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GameContainer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gc&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SlickException&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;每个 StateBasedGame 包含的 GameState 将在&lt;code&gt;initStatesList&lt;/code&gt;中初始化。每个 GameState 都是一个基于&lt;code&gt;BasicGameState&lt;/code&gt;类的子类的对象。它需要实现至少这几个方法：&lt;code&gt;getID&lt;/code&gt;、&lt;code&gt;init&lt;/code&gt;、&lt;code&gt;update&lt;/code&gt;、&lt;code&gt;render&lt;/code&gt;。代码框架：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;GameState&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BasicGameState&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GameContainer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gc&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StateBasedGame&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;game&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SlickException&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GameContainer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gc&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StateBasedGame&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;game&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;delta&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SlickException&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GameContainer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gc&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StateBasedGame&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;game&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Graphics&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SlickException&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其中&lt;code&gt;getID&lt;/code&gt;方法的返回值就是这个 State 的 ID，每个 State 必须拥有不同的 ID，以便在 State 之间切换。&lt;/p&gt;&lt;p&gt;&lt;span id=&quot;more-881&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;当我们创建好一个 State 的类（这个例子中叫做&lt;code&gt;GameState&lt;/code&gt;），就可以在&lt;code&gt;initStatesList&lt;/code&gt;中加入这个 State 了：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;addState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GameState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;你也可以加入多个 State，游戏默认会载入第一个 State。如果要在 States 之间切换，你应该在相应 State 的&lt;code&gt;update&lt;/code&gt;函数中执行如下代码：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;n&quot;&gt;game&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;enterState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;上面的代码会导致游戏切换到 ID 为 1 的 state。&lt;/p&gt;&lt;p&gt;游戏中，当你切换到一个新的 State 时，旧的 State 会被暂停，它的&lt;code&gt;update&lt;/code&gt;、&lt;code&gt;render&lt;/code&gt;函数也不会再被调用了。这点很关键，比如你可以很方便地创建一个「暂停」State，然后切换到这个 State 来使游戏暂停。&lt;/p&gt;&lt;h2 id=&quot;state&quot;&gt;切换 State&lt;/h2&gt;&lt;p&gt;你也可以使用一个动画效果来在不同 State 之间切换：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;n&quot;&gt;game&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;enterState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FadeOutTransition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FadeInTransition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;第一个参数是目标 State 的 ID，后两个是离开和进入的过渡效果。&lt;/p&gt;&lt;h2 id=&quot;section-1&quot;&gt;一个小游戏&lt;/h2&gt;&lt;p&gt;这个小游戏的设计是，一些不知名的小球会从天而降，位置是随机的；你需要用鼠标接住每个小球。如果有一个小球没有被接住，你就被扣一分。整个游戏的设计步骤不是很好描述，看原视频则很清晰，因此下面的内容大家看视频就可以了。视频内容是完整的，从本文一开始的内容讲起；相信大家如果看懂了我刚才写的东西，看懂这个视频以及游戏的制作过程应该也不是很难。&lt;/p&gt;&lt;p&gt;你可以&lt;a href=&quot;http://www.tudou.com/programs/view/LOaPFjLSyEo/&quot;&gt;在土豆网观看此视频&lt;/a&gt;。&lt;/p&gt;</description><pubDate>Sun, 02 Mar 2014 00:00:00 +0800</pubDate><link>https://www.renfei.org/blog/slick2d-introduction-2.html</link><guid isPermaLink="true">https://www.renfei.org/blog/slick2d-introduction-2.html</guid></item><item><title>Slick2D 简介（一）：开发环境搭建及基础知识</title><description>&lt;p&gt;Slick 2D 是一个轻量级、跨平台的 Java 2D 游戏引擎。&lt;/p&gt;&lt;p&gt;上学期的 Java 课程期末大作业我一直打算写一个游戏，研究了半天游戏引擎，最终选定了 Slick2D。要说明的是，对于大型游戏，它还比较单薄；但是对于一般的游戏已经足够了，也非常适合游戏开发者进行入门和练习。Slick2D 是基于 &lt;a href=&quot;http://www.lwjgl.org/index.php&quot;&gt;LWJGL&lt;/a&gt; 的 2D 游戏引擎，提供对状态、图像、动画、粒子、声音、字体、音乐、控制等等的支持，但是本身不是一个物理引擎。如果想搭配物理引擎使用，推荐 &lt;a href=&quot;http://www.jbox2d.org&quot; title=&quot;JBox 2D&quot;&gt;JBox2D&lt;/a&gt;。然而，如果你的游戏需要的物理效果不是很复杂，尝试一下自己写物理引擎也未尝不可。我的项目只需要用到球体的二维碰撞，于是干脆自己写了一个，非常有意思，也可以学到很多东西。&lt;/p&gt;&lt;p&gt;下载和基本的介绍可以参考&lt;a href=&quot;http://slick.ninjacave.com&quot; title=&quot;Slick2D Java Game Library&quot;&gt;官网&lt;/a&gt;。它的完整 API 文档在&lt;a href=&quot;http://slick.ninjacave.com/javadoc/&quot; title=&quot;Slick2D Documentation&quot;&gt;可以在这里找到&lt;/a&gt;。&lt;/p&gt;&lt;h2 id=&quot;section&quot;&gt;开发环境搭建&lt;/h2&gt;&lt;h3 id=&quot;section-1&quot;&gt;文件准备&lt;/h3&gt;&lt;p&gt;需要分别下载 &lt;a href=&quot;http://slick.ninjacave.com/slick.zip&quot;&gt;Slick2D&lt;/a&gt;（12.8MB） 和 &lt;a href=&quot;http://www.lwjgl.org/download.php&quot;&gt;LWJGL&lt;/a&gt;（5.8MB） 两部分组件。&lt;/p&gt;&lt;h3 id=&quot;section-2&quot;&gt;添加库文件&lt;/h3&gt;&lt;p&gt;为了使用 Slick2D，你需要把库文件添加到 IDE。使用「Add External Libraries」或者类似功能（有的 IDE 可能需要先创建一个 Project 才能选择 Libraries），把下载的文件中&lt;code&gt;slick/lib&lt;/code&gt;中的&lt;code&gt;slick.jar&lt;/code&gt;，以及&lt;code&gt;lwjgl-x.x.x/jar&lt;/code&gt;目录下的全部&lt;code&gt;jar&lt;/code&gt;文件全部添加到 IDE 即可。&lt;/p&gt;&lt;h3 id=&quot;vm-options&quot;&gt;设置 VM Options&lt;/h3&gt;&lt;p&gt;最后还需要把 LWJGL 的 native（位于&lt;code&gt;lwjgl-x.x.x/native/&lt;/code&gt;目录下）链接到 Java 虚拟机。LWJGL 是基于 OpenGL 的，而这些文件就描述了有关 OpenGL 的信息。先把这些文件放到你的项目目录中，然后找到你的 IDE 的 VM Options 设定，在里面填入如下内容：&lt;/p&gt;&lt;pre&gt;&lt;code&gt;-Djava.library.path=&quot;path/to/lwjgl-x.x.x/native/&amp;lt;linux|macosx|solaris|windows&amp;gt;&quot;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;注意要把其中的路径替换为真实路径。根据实际操作系统，选择 linux, maxosx, solaris 或者 windows 的其中一个即可。&lt;/p&gt;&lt;h2 id=&quot;hello-world&quot;&gt;Hello, World&lt;/h2&gt;&lt;p&gt;为了测试开发环境是否搭建正确，可以用一个简单的 Hello, World 级程序测试一下。&lt;/p&gt;&lt;p&gt;假设我们的主类叫做&lt;code&gt;HelloWorld&lt;/code&gt;，可以添加如下代码进行测试：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.newdawn.slick.AppGameContainer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.newdawn.slick.BasicGame&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.newdawn.slick.GameContainer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.newdawn.slick.Graphics&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.newdawn.slick.SlickException&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HelloWorld&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BasicGame&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HelloWorld&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GameContainer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;container&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SlickException&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GameContainer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;container&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;delta&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SlickException&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GameContainer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;container&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Graphics&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;graphics&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SlickException&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;graphics&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;drawString&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;Hello, World!&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AppGameContainer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AppGameContainer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HelloWorld&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;Hello World&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setDisplayMode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;800&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;600&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果能正常显示 FPS 和 “Hello, World!” 字样，则说明 Slick2D 配置正确。&lt;/p&gt;&lt;h2 id=&quot;section-3&quot;&gt;示例程序说明&lt;/h2&gt;&lt;p&gt;这一部分很重要，弄懂这个简单的示例程序是理解游戏开发的第一步。&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;BasicGame&lt;/code&gt;：我们的主类继承了&lt;code&gt;BasicGame&lt;/code&gt;类。BasicGame 是 Slick2D 提供的一种基本游戏类型。对应的还有 &lt;code&gt;StateBasedGame&lt;/code&gt;，在下节我们会讲到。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;init&lt;/code&gt;：这个方法在第一次创建场景的时候会自动被调用，一般用于初始化常数、载入资源等等。其中&lt;code&gt;GameContainer&lt;/code&gt;就是游戏「容器」，可以理解为整个游戏窗口。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;update&lt;/code&gt;和&lt;code&gt;render&lt;/code&gt;：这两个方法很重要。我们玩游戏的时候都了解 FPS 的概念，即每秒的帧数。因为游戏的画面不是静止的，所以实际上程序是通过「逐帧重绘」的方式向我们呈现画面的：每秒绘制 60 次（假如 FPS 为 60），&lt;strong&gt;每次根据需要改变绘制的内容&lt;/strong&gt;，从而呈现出动画效果。&lt;/li&gt;&lt;li&gt;Slick2D 也是这么做的。游戏运行的时候，每次绘制之前，系统会自动调用&lt;code&gt;update&lt;/code&gt;方法来更新数据，然后自动调用&lt;code&gt;render&lt;/code&gt;方法来绘制，周而复始，不断循环。通过 update-render-update-render 这样调用，来最终形成我们需要的效果。所以，你的任务就是提供这两个方法的实现。&lt;/li&gt;&lt;li&gt;&lt;p&gt;每次调用&lt;code&gt;update&lt;/code&gt;方法的时间差并不是一定的（甚至都不一定是均匀的），这取决于系统的性能和一些随机因素的影响。为了解决这个问题，引入了&lt;code&gt;update&lt;/code&gt;方法的参数&lt;code&gt;delta&lt;/code&gt;。它代表了本次调用&lt;code&gt;update&lt;/code&gt;和上次调用&lt;code&gt;update&lt;/code&gt;的时间差，有的时候，我们需要这个时间差才能决定&lt;code&gt;update&lt;/code&gt;的具体内容（比如移动的距离）。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;在例子中，我们在&lt;code&gt;render&lt;/code&gt;函数中干的事情就是渲染一个字符串，使用 Slick2D 提供的&lt;code&gt;drawString&lt;/code&gt;方法。这个方法的两个整数参数代表字符串的位置。游戏坐标系以 (0,0) 为左上角，(宽,高) 为右下角，和我们习惯的笛卡尔坐标系的原点位置（左下角）不同，这个要注意一下。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;而最后在&lt;code&gt;main&lt;/code&gt;函数中，我们设置分辨率为 800 x 600，并启动游戏。&lt;/li&gt;&lt;/ul&gt;</description><pubDate>Sat, 01 Mar 2014 00:00:00 +0800</pubDate><link>https://www.renfei.org/blog/slick2d-introduction-1.html</link><guid isPermaLink="true">https://www.renfei.org/blog/slick2d-introduction-1.html</guid></item><item><title>Universal Analytics：设置事件追踪和社交互动记录</title><description>&lt;p&gt;Google Analytics 从旧版的 ga.js 更新到 analytics.js 之后，事件与社交的设置也有了变化。本文只介绍新版 Google Analytics Event Tracking / Social Interactions 和以往的不同之处。有关事件追踪的基本概念与用途，可以参考&lt;a href=&quot;//www.renfei.org/blog/google-analytics-event-tracking-guidelines.html&quot; title=&quot;Google Analytics “事件追踪” 功能使用详解&quot;&gt;Google Analytics “事件追踪” 功能使用详解&lt;/a&gt;。&lt;/p&gt;&lt;h2 id=&quot;section&quot;&gt;事件追踪&lt;/h2&gt;&lt;h3 id=&quot;section-1&quot;&gt;实现&lt;/h3&gt;&lt;p&gt;一个事件主要由四个参数构成，分别是类别、动作、标签和值。前三个是一个字符串、最后一个是一个非负数。你可以像这样发送一个事件：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;ga&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;send&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;event&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;category&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;action&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;label&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;前两个参数是固定的，Google 从他们知道你是要发送一个事件。而&lt;code&gt;category&lt;/code&gt;（必需）、&lt;code&gt;action&lt;/code&gt;（必需）、&lt;code&gt;label&lt;/code&gt;（可选）和&lt;code&gt;0&lt;/code&gt;（可选）这四个参数应该根据需要来替换。&lt;/p&gt;&lt;p&gt;你也可以在最后附加一个参数，用于设置你需要的其他信息。这个参数是一个 object，例如：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;ga&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;send&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;event&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;category&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;action&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;page&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;/my-new-page&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;nonInteraction&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在这个例子中，&lt;code&gt;page&lt;/code&gt;项表示人工指定了页面地址（可以用来创建虚拟页面）；而&lt;code&gt;nonInteration&lt;/code&gt;设为&lt;code&gt;1&lt;/code&gt;则说明该页面发生此事件不会影响到跳出率的计算。&lt;/p&gt;&lt;p&gt;默认地，如果一个页面中发生了事件，那么即使只访问了该页面一次，也不算做跳出访问。可以通过设&lt;code&gt;nonInteration&lt;/code&gt;为&lt;code&gt;1&lt;/code&gt;来修改这一默认行为。&lt;/p&gt;&lt;h3 id=&quot;section-2&quot;&gt;例子&lt;/h3&gt;&lt;p&gt;以下示例代码来自 &lt;a href=&quot;https://developers.google.com/analytics/devguides/collection/analyticsjs/events&quot;&gt;Google 官方文档（英文）&lt;/a&gt;：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;downloadLink&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;button&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addListener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;downloadLink&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;click&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ga&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;send&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;event&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;button&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;click&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;nav-buttons&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&lt;span class=&quot;cm&quot;&gt;/**&lt;/span&gt;&lt;span class=&quot;cm&quot;&gt; * 为了同时兼容 IE 与 W3C 标准浏览器，自定义如下添加事件监听函数：&lt;/span&gt;&lt;span class=&quot;cm&quot;&gt; */&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;addListener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;attachEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;attachEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;on&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;section-3&quot;&gt;社交互动&lt;/h2&gt;&lt;p&gt;社交互动主要可以让你追踪用户点击「分享」类按钮的情况。虽然事件追踪也可以记录这些事件，但是社交互动功能是一个专用的工具，可以提供更加具体的分析和报告。&lt;/p&gt;&lt;p&gt;目前，analytics.js 并不会自动统计 Google+ 的社交互动情况（ga.js 是会自动统计的），需要手工设置。&lt;/p&gt;&lt;p&gt;一个社交互动包含如下三个属性：社交网络、动作、目标。例如，社交网络可以是「Facebook」或者「人人网」，动作可以是「分享」、「收藏」、「喜欢」等，而目标则是对应的页面地址。&lt;/p&gt;&lt;p&gt;为了发送一个社交互动记录，要执行如下代码：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;ga&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;send&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;social&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;network&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;action&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;target&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其中前两个参数是固定的，而后三个参数应该根据具体情况修改，意义如上文所述。这里的参数都是必需的。&lt;/p&gt;&lt;p&gt;例如：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;ga&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;send&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;social&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;facebook&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;like&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;http://example.com&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;和事件追踪类似，这里也可以在最后添加一个参数，是一个 object，用于规定一些额外信息。例如：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;ga&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;send&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;social&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;facebook&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;like&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;http://example.com&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;page&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;/my-new-page&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description><pubDate>Wed, 26 Feb 2014 00:00:00 +0800</pubDate><link>https://www.renfei.org/blog/google-universal-analytics-event-tracking-and-social-interactions.html</link><guid isPermaLink="true">https://www.renfei.org/blog/google-universal-analytics-event-tracking-and-social-interactions.html</guid></item><item><title>Google Analytics：几种过滤自己访问的方法</title><description>&lt;p&gt;有些博主可能希望 Google Analytics 不要记录自己的访问，下面是我发现的一些方法。&lt;/p&gt;&lt;h2 id=&quot;wordpress&quot;&gt;WordPress专用：修改模版&lt;/h2&gt;&lt;p&gt;如果你的站点是 WordPress 搭建的，那么可以通过修改模版代码的方式来实现。思路就是，当只有非登录用户访问时才输出统计代码；如果是已登录用户访问，则不输出统计代码。&lt;/p&gt;&lt;p&gt;例如，如果你是通过修改&lt;code&gt;header.php&lt;/code&gt;添加的统计代码，那么只需要改成这样：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;is_user_logged_in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&amp;lt;!-- Google Analytics Tracking Code --&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;endif&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;嗯，其实就是加一个&lt;code&gt;is_user_logged_in&lt;/code&gt;函数，这是 WordPress 内置的函数，用途是判断用户是否已登录。所以，只要你每次访问都保证登录就可以了。比如，你的博客地址为&lt;code&gt;example.com&lt;/code&gt;，那么每次你可以先直接访问&lt;code&gt;example.com/wp-admin&lt;/code&gt;登陆一下。&lt;/p&gt;&lt;p&gt;如果你的博客开放注册，那么可能需要用&lt;code&gt;current_user_can(&#39;manage_options&#39;)&lt;/code&gt;。&lt;/p&gt;&lt;h2 id=&quot;cookie&quot;&gt;利用 Cookie&lt;/h2&gt;&lt;p&gt;因为 PHP 和 JavaScript 都支持读写 Cookie，所以这也是个好办法。你可以给自己设置一个特殊的 Cookie，然后实现根据 Cookie 判断是否统计的代码。对于 PHP 页面，可以在后台判断，如果存在你设定的 Cookie 则不输出统计代码；对于静态页面，可以用 JavaScript 判断，如果存在你设定的 Cookie 则不执行统计代码。&lt;/p&gt;&lt;p&gt;需要注意的是，必须在自己的网站的域内设置 Cookie 才有效；不同域名下的 Cookie 是不共享的。&lt;/p&gt;&lt;p&gt;例如，下面的 PHP 脚本将会设置一个 Cookie &lt;code&gt;DO_NOT_TRACK&lt;/code&gt;，过期时间为一年：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;set_cookie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;DO_NOT_TRACK&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;true&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3600&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;24&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;365&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;访问一次之后，一年以内只要不清空的话 Cookie 都会在。然后，你可以选择自己喜欢的方法：&lt;/p&gt;&lt;p&gt;PHP 判断：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;isset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_COOKIE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;DO_NOT_TRACK&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&amp;lt;script&amp;gt;/* Tracking Code */&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;endif&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;或者 JavaScript 判断：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;cookie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;indexOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;DO_NOT_TRACK&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;cm&quot;&gt;/* Tracking Code */&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;universal-analytics-&quot;&gt;用于 Universal Analytics 的方法&lt;/h2&gt;&lt;p&gt;如果你已经升级到了 Universal Analytics，那么也可以利用 &lt;a href=&quot;https://developers.google.com/analytics/devguides/collection/analyticsjs/advanced#optout&quot;&gt;Google 官方提供的一个技巧&lt;/a&gt;来实现。具体来说就是：如果把下面这个值设为&lt;code&gt;true&lt;/code&gt;，那么访问信息就不会发送给 Google（需要先把 UA-XXXX-Y 设为你自己的编号）：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;ga-disable-UA-XXXX-Y&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;为了只对自己执行这句话，同样可以通过 Cookie 来完成。例如：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;isset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_COOKIE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;DO_NOT_TRACK&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&amp;lt;script&amp;gt;window[&amp;#39;ga-disable-UA-XXXX-Y&amp;#39;] = true;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;endif&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;或者：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;cookie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;indexOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;DO_NOT_TRACK&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;ga-disable-UA-XXXX-Y&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description><pubDate>Fri, 21 Feb 2014 00:00:00 +0800</pubDate><link>https://www.renfei.org/blog/exclude-yourself-from-google-analytics.html</link><guid isPermaLink="true">https://www.renfei.org/blog/exclude-yourself-from-google-analytics.html</guid></item><item><title>JavaScript 文件拖拽上传插件 dropzone.js 介绍</title><description>&lt;p&gt;&lt;a href=&quot;http://www.dropzonejs.com&quot;&gt;dropzone.js&lt;/a&gt; 是一个开源的 JavaScript 库，提供 AJAX 异步上传功能。&lt;/p&gt;&lt;h2 id=&quot;section&quot;&gt;安装&lt;/h2&gt;&lt;p&gt;下载&lt;code&gt;dropzone.js&lt;/code&gt;文件并添加到页面中即可。Dropzone 不依赖 jQuery 框架。&lt;/p&gt;&lt;h2 id=&quot;section-1&quot;&gt;启用&lt;/h2&gt;&lt;p&gt;可以新建一个&lt;code&gt;div&lt;/code&gt;元素，然后通过如下 JavaScript 代码启用 dropzone（如果你使用 jQuery）：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;dropz&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;script&amp;gt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;#dropz&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;dropzone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;handle-upload.php&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;maxFiles&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;maxFilesize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;512&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;acceptedFiles&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;.js,.obj,.dae&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果你没有使用 jQuery 框架，也可以这样来初始化：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;dropz&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;script&amp;gt;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;dropz&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Dropzone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;#dropz&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;handle-upload.php&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;maxFiles&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;maxFilesize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;512&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;acceptedFiles&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;.js,.obj,.dae&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其中&lt;code&gt;url&lt;/code&gt;是必须的值，指明文件上传提交到哪个页面。其他的值都是可选的，如果使用默认值的话可以省略。&lt;/p&gt;&lt;h2 id=&quot;section-2&quot;&gt;接收文件&lt;/h2&gt;&lt;p&gt;Dropzone 并不含任何服务器端的支持和实现，利用 Dropzone 上传文件和利用下面基本的 HTML 表单对于服务器来说是一样的：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;form&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;action=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;handle-upload.php&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;method=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;post&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;enctype=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;multipart/form-data&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;file&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;file&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;dropzone&quot;&gt;配置 Dropzone&lt;/h2&gt;&lt;p&gt;此插件的特色就在于非常灵活，提供了许多可选项、事件等。下面分类介绍常用的配置项。&lt;/p&gt;&lt;h3 id=&quot;section-3&quot;&gt;功能选项&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;url&lt;/code&gt;：最重要的参数，指明了文件提交到哪个页面。&lt;/li&gt;&lt;li&gt;&lt;code&gt;method&lt;/code&gt;：默认为&lt;code&gt;post&lt;/code&gt;，如果需要，可以改为&lt;code&gt;put&lt;/code&gt;。&lt;/li&gt;&lt;li&gt;&lt;code&gt;paramName&lt;/code&gt;：相当于&lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt;元素的&lt;code&gt;name&lt;/code&gt;属性，默认为&lt;code&gt;file&lt;/code&gt;。&lt;/li&gt;&lt;li&gt;&lt;code&gt;maxFilesize&lt;/code&gt;：最大文件大小，单位是 MB。&lt;/li&gt;&lt;li&gt;&lt;code&gt;maxFiles&lt;/code&gt;：默认为&lt;code&gt;null&lt;/code&gt;，可以指定为一个数值，限制最多文件数量。&lt;/li&gt;&lt;li&gt;&lt;code&gt;addRemoveLinks&lt;/code&gt;：默认&lt;code&gt;false&lt;/code&gt;。如果设为&lt;code&gt;true&lt;/code&gt;，则会给文件添加一个删除链接。&lt;/li&gt;&lt;li&gt;&lt;code&gt;acceptedFiles&lt;/code&gt;：指明允许上传的文件类型，格式是逗号分隔的 MIME type 或者扩展名。例如：&lt;code&gt;image/*,application/pdf,.psd,.obj&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;uploadMultiple&lt;/code&gt;：指明是否允许 Dropzone 一次提交多个文件。默认为&lt;code&gt;false&lt;/code&gt;。如果设为&lt;code&gt;true&lt;/code&gt;，则相当于 HTML 表单添加&lt;code&gt;multiple&lt;/code&gt;属性。&lt;/li&gt;&lt;li&gt;&lt;code&gt;headers&lt;/code&gt;：如果设定，则会作为额外的 header 信息发送到服务器。例如：&lt;code&gt;{&quot;custom-header&quot;: &quot;value&quot;}&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;init&lt;/code&gt;：一个函数，在 Dropzone 初始化的时候调用，可以用来添加自己的事件监听器。&lt;/li&gt;&lt;li&gt;&lt;code&gt;forceFallback&lt;/code&gt;：Fallback 是一种机制，当浏览器不支持此插件时，提供一个备选方案。默认为&lt;code&gt;false&lt;/code&gt;。如果设为&lt;code&gt;true&lt;/code&gt;，则强制 fallback。&lt;/li&gt;&lt;li&gt;&lt;code&gt;fallback&lt;/code&gt;：一个函数，如果浏览器不支持此插件则调用。&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&quot;section-4&quot;&gt;翻译选项&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;dictDefaultMessage&lt;/code&gt;：没有任何文件被添加的时候的提示文本。&lt;/li&gt;&lt;li&gt;&lt;code&gt;dictFallbackMessage：Fallback&lt;/code&gt; 情况下的提示文本。&lt;/li&gt;&lt;li&gt;&lt;code&gt;dictInvalidInputType&lt;/code&gt;：文件类型被拒绝时的提示文本。&lt;/li&gt;&lt;li&gt;&lt;code&gt;dictFileTooBig&lt;/code&gt;：文件大小过大时的提示文本。&lt;/li&gt;&lt;li&gt;&lt;code&gt;dictCancelUpload&lt;/code&gt;：取消上传链接的文本。&lt;/li&gt;&lt;li&gt;&lt;code&gt;dictCancelUploadConfirmation&lt;/code&gt;：取消上传确认信息的文本。&lt;/li&gt;&lt;li&gt;&lt;code&gt;dictRemoveFile&lt;/code&gt;：移除文件链接的文本。&lt;/li&gt;&lt;li&gt;&lt;code&gt;dictMaxFilesExceeded&lt;/code&gt;：超过最大文件数量的提示文本。&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&quot;section-5&quot;&gt;添加事件监听&lt;/h3&gt;&lt;p&gt;如果你希望在一个事件发生时采取一些额外的操作，而不干扰 Dropzone 的默认行为，那么你应该通过添加事件监听器的办法对事件做出响应，&lt;strong&gt;而非重写默认事件函数&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;重写默认事件函数的例子如下：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;#dropz&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;dropzone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addedfile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// actions...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果你重写默认事件函数，该事件发生时插件默认采取的动作将被覆盖。大多数情况下你仅仅想在事件发生时添加自己的行为，那么应该使用&lt;code&gt;on&lt;/code&gt;方法。&lt;/p&gt;&lt;p&gt;jQuery 版本：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;#dropz&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;dropzone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;addedfile&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// actions...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;非 jQuery 版本：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;dropz&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;addedfile&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// actions...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&quot;section-6&quot;&gt;常用事件&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;以下事件接收 file 为第一个参数&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;addedfile&lt;/code&gt;：添加了一个文件时发生。&lt;/li&gt;&lt;li&gt;&lt;code&gt;removedfile&lt;/code&gt;：一个文件被移除时发生。你可以监听这个事件并手动从服务器删除这个文件。&lt;/li&gt;&lt;li&gt;&lt;code&gt;uploadprogress&lt;/code&gt;：上传时按一定间隔发生这个事件。第二个参数为一个整数，表示进度，从 0 到 100。第三个参数是一个整数，表示发送到服务器的字节数。当一个上传结束时，Dropzone 保证会把进度设为 100。注意：这个函数可能被以同一个进度调用多次。&lt;/li&gt;&lt;li&gt;&lt;code&gt;success&lt;/code&gt;：文件成功上传之后发生，第二个参数为服务器响应。&lt;/li&gt;&lt;li&gt;&lt;code&gt;complete&lt;/code&gt;：当文件上传成功或失败之后发生。&lt;/li&gt;&lt;li&gt;&lt;code&gt;canceled&lt;/code&gt;：当文件在上传时被取消的时候发生。&lt;/li&gt;&lt;li&gt;&lt;code&gt;maxfilesreached&lt;/code&gt;：当文件数量达到最大时发生。&lt;/li&gt;&lt;li&gt;&lt;code&gt;maxfilesexceeded&lt;/code&gt;：当文件数量超过限制时发生。&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;以下事件接收一个 file list 作为第一个参数（仅当&lt;code&gt;uploadMultiple&lt;/code&gt;被设为&lt;code&gt;true&lt;/code&gt;时才会发生）&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;successmultiple&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;completemultiple&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;cancelmultiple&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;特殊事件&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;totaluploadprogress&lt;/code&gt;：第一个参数为总上传进度，第二个参数为总字节数，第三个参数为总上传字节数。&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;section-7&quot;&gt;例子&lt;/h2&gt;&lt;p&gt;这里我使用上面的选项、事件等写了一个例子，供参考：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;dropz&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;script&amp;gt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;.dropz&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;dropzone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;handle-upload.php&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addRemoveLinks&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;dictRemoveLinks&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;x&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;dictCancelUpload&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;x&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;maxFiles&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;maxFilesize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;acceptedFiles&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;.js&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;success&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;File &amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;uploaded&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;removedfile&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;File &amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;removed&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;section-8&quot;&gt;外观&lt;/h2&gt;&lt;p&gt;Dropzone 下载之后没有自带任何 CSS 样式（人家只有一个 js 文件嘛）。我觉得&lt;a href=&quot;http://www.dropzonejs.com/&quot;&gt;官网&lt;/a&gt;提供的 Demo 的&lt;a href=&quot;https://github.com/enyo/dropzone/tree/master/downloads/css&quot;&gt;外观设计&lt;/a&gt;就非常不错，可以供大家参考。&lt;/p&gt;&lt;h2 id=&quot;section-9&quot;&gt;其他教程&lt;/h2&gt;&lt;p&gt;Dropzone 的作者在&lt;a href=&quot;https://github.com/enyo/dropzone/wiki/&quot;&gt;插件的 GitHub Wiki 页面&lt;/a&gt;上提供了很多额外教程，非常好，也推荐大家看一看。&lt;/p&gt;&lt;p&gt;这里我只翻译一部分我觉得常用的教程的标题：&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/enyo/dropzone/wiki/FAQ#wiki-how-to-show-an-error-returned-by-the-server&quot;&gt;如何显示服务器返回的错误信息？&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/enyo/dropzone/wiki/FAQ#wiki-how-to-get-notified-when-all-files-finished-uploading&quot;&gt;如何在所有文件上传完毕时得到通知？&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/enyo/dropzone/wiki/FAQ#wiki-how-to-show-files-already-stored-on-server&quot;&gt;如何显示出已经保存在服务器上的文件？&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/enyo/dropzone/wiki/FAQ#wiki-use-own-confirm-implementation&quot;&gt;自己实现删除文件时的提示功能&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/enyo/dropzone/wiki/FAQ#wiki-provide-a-thumbnail-for-files&quot;&gt;为文件提供自定义缩略图&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/enyo/dropzone/wiki/Upload-all-files-with-a-button&quot;&gt;点击一个按钮再上传所有文件&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/enyo/dropzone/wiki/Remove-all-files-with-one-button&quot;&gt;点击一个按钮删除所有文件&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/enyo/dropzone/wiki/Combine-normal-form-with-Dropzone&quot;&gt;把 Dropzone 放到一个已存在的表单中&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;</description><pubDate>Wed, 19 Feb 2014 00:00:00 +0800</pubDate><link>https://www.renfei.org/blog/dropzone-js-introduction.html</link><guid isPermaLink="true">https://www.renfei.org/blog/dropzone-js-introduction.html</guid></item><item><title>Google Analytics：为链接点击设定事件追踪的方法</title><description>&lt;p&gt;在 Google Analytics 中，可以使用 Event Tracking 功能跟踪自定义的事件。但是，如果你要跟踪的是一个链接点击，那么单纯这样写则很有可能导致漏掉许多事件：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;http://www.example.com&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;onclick=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;_trackEvent(&amp;#39;link&amp;#39;, &amp;#39;click&amp;#39;, this.href)&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Visit example.com&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这是因为，每次自定义事件被触发的时候，浏览器都会向 Google 的服务器发送一个请求，从而发送数据。但是点击链接会直接进入下一个页面，如果此时需要发送的请求还没有完成，浏览器就会放弃该请求而直接跳转。所以，就会导致事件无法被记录。&lt;/p&gt;&lt;h2 id=&quot;section&quot;&gt;解决方法&lt;/h2&gt;&lt;h3 id=&quot;gajs-&quot;&gt;经典跟踪代码 (ga.js) 的解决方法&lt;/h3&gt;&lt;p&gt;既然事件没有记录是因为跳转得太快，那么我们可以通过&lt;code&gt;setTimeout&lt;/code&gt;函数设定一个比较小的延时来给浏览器充足的时间向 Google 的服务器发送数据。一般设为 500ms 或 1000ms 就足够了，同时用户也不会察觉到。&lt;/p&gt;&lt;p&gt;我们可以把触发事件的语句包装到一个自定义函数中：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;trackOutboundLink&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_trackEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;link&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;click&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;document.location=&amp;#39;&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&amp;#39;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;同时在 HTML 中这么写：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;http://www.example.com&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;onclick=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;trackOutboundLink(this.href);return false;&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Visit example.com&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其中&lt;code&gt;return false&lt;/code&gt;语句防止了默认行为（点击&lt;code&gt;a&lt;/code&gt;而跳转）的发生，实际跳转由我们自己来完成。&lt;/p&gt;&lt;h3 id=&quot;universal-analytics-analyticsjs-&quot;&gt;Universal Analytics (analytics.js) 的解决方法&lt;/h3&gt;&lt;h4 id=&quot;google-&quot;&gt;Google 建议的方法&lt;/h4&gt;&lt;p&gt;如果你已经升级到了 Universal Analytics，那么 Google 给出了这种情况下的&lt;a href=&quot;https://support.google.com/analytics/answer/1136920&quot;&gt;官方建议&lt;/a&gt;。在新版的跟踪代码中，设置事件的函数包含了一个 callback，在成功设置完毕后触发。于是我们可以把手工跳转的代码写到 callback 函数中，这样就不用显式地设置 timeout 了，同时浏览器也能够「尽快」跳转。&lt;/p&gt;&lt;p&gt;同样声明一个包装函数：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;trackOutboundLink&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ga&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;send&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;event&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;outbound&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;click&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;hitCallback&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这次，使用了&lt;code&gt;hitCallback&lt;/code&gt;，它所对应的函数将在成功发送事件信息后被调用。类似，HTML 代码中这么写：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;http://www.example.com&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;onclick=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;trackOutboundLink(&amp;#39;http://www.example.com&amp;#39;); return false;&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Check out example.com&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;（以上两段示例代码来自 Google 官方文档，链接见上文）&lt;/p&gt;&lt;h4 id=&quot;section-1&quot;&gt;还可以做得更好&lt;/h4&gt;&lt;p&gt;本来教程到这里就可以结束了，可是还有一点值得说明。上述解决方法在绝大多数情况下是完全没有问题的，但是除了一种情况：浏览器无法正常发送事件数据到 Google 服务器。例如，如果 Google 的服务器忽然「无法访问」（你懂的），或者加载&lt;code&gt;analytics.js&lt;/code&gt;失败，那么&lt;code&gt;hitCallback&lt;/code&gt;就将永远不会被调用。这种情况下这个链接就变成点了也没用的了。&lt;/p&gt;&lt;p&gt;在访问 Google 完全没有问题的情况下，这种情形自然不必考虑。不过为了提供最大程度的保障，可以人工加一个防御措施：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;trackOutboundLink&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;redirectTriggered&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ga&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;send&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;event&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;outbound&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;click&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;hitCallback&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;redirectTriggered&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;redirectTriggered&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1500&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;即，在进入&lt;code&gt;trackOutboundLink&lt;/code&gt;之后，设置 1500ms 的过期时间，如果时间到了还没有跳转，就人工跳转，保证访客可以正常访问。&lt;/p&gt;</description><pubDate>Tue, 18 Feb 2014 00:00:00 +0800</pubDate><link>https://www.renfei.org/blog/google-analytics-event-tracking-for-links.html</link><guid isPermaLink="true">https://www.renfei.org/blog/google-analytics-event-tracking-for-links.html</guid></item><item><title>SPF 记录：原理、语法及配置方法简介</title><description>&lt;p&gt;SPF，全称为 Sender Policy Framework，即发件人策略框架。&lt;/p&gt;&lt;p&gt;当前 Email 通信，还是在使用 SMTP 这个协议。SMTP 的全称为 Simple Mail Transfer Protocol，即「简单邮件传输协议」。正如它的名字锁暗示的，SMTP 实际上是一个非常简单（甚至简陋）的传输协议，本身并没有很好的安全措施。根据 SMTP 的规则，发件人的邮箱地址是可以由发信方任意声明的。在 SMTP 协议制定的时候也许还好，但在垃圾和诈骗邮件横行的今天，这显然是极不安全的。&lt;/p&gt;&lt;p&gt;SPF 出现的目的，就是为了防止随意伪造发件人。&lt;/p&gt;&lt;h2 id=&quot;spf-&quot;&gt;SPF 记录的原理&lt;/h2&gt;&lt;p&gt;SPF 记录实际上是服务器的一个 DNS 记录，原理其实很简单：&lt;/p&gt;&lt;p&gt;假设邮件服务器收到了一封邮件，来自主机的 IP 是&lt;code&gt;173.194.72.103&lt;/code&gt;，并且声称发件人为&lt;code&gt;email@example.com&lt;/code&gt;。为了确认发件人不是伪造的，邮件服务器会去查询&lt;code&gt;example.com&lt;/code&gt;的 SPF 记录。如果该域的 SPF 记录设置允许 IP 为&lt;code&gt;173.194.72.103&lt;/code&gt;的主机发送邮件，则服务器就认为这封邮件是合法的；如果不允许，则通常会退信，或将其标记为垃圾/仿冒邮件。&lt;/p&gt;&lt;p&gt;因为不怀好心的人虽然可以「声称」他的邮件来自&lt;code&gt;example.com&lt;/code&gt;，但是他却无权操作&lt;code&gt;example.com&lt;/code&gt;的 DNS 记录；同时他也无法伪造自己的 IP 地址。因此 SPF 是很有效的，当前基本上所有的邮件服务提供商（例如 Gmail、QQ 邮箱等）都会验证它。&lt;/p&gt;&lt;h2 id=&quot;spf--1&quot;&gt;SPF 记录的语法&lt;/h2&gt;&lt;p&gt;一条 SPF 记录定义了一个或者多个 mechanism，而 mechanism 则定义了哪些 IP 是允许的，哪些 IP 是拒绝的。&lt;/p&gt;&lt;p&gt;这些 mechanism 包括以下几类：&lt;/p&gt;&lt;pre&gt;&lt;code&gt;all | ip4 | ip6 | a | mx | ptr | exists | include&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;每个 mechanism 可以有四种前缀：&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&quot;+&quot;Pass（通过）&quot;-&quot;Fail（拒绝）&quot;~&quot;Soft Fail（软拒绝）&quot;?&quot;Neutral（中立）&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;测试时，将从前往后依次测试每个 mechanism。如果一个 mechanism 包含了要查询的 IP 地址（称为命中），则测试结果由相应 mechanism 的前缀决定。默认的前缀为&lt;code&gt;+&lt;/code&gt;。如果测试完所有的 mechanisms 也没有命中，则结果为 Neutral。&lt;/p&gt;&lt;p&gt;除了以上四种情况，还有 None（无结果）、PermError（永久错误）和 TempError（临时错误）三种其他情况。对于这些情况的解释和服务器通常的处理办法如下：&lt;/p&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;结果&lt;/td&gt;&lt;td&gt;含义&lt;/td&gt;&lt;td&gt;服务器处理办法&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Pass&lt;/td&gt;&lt;td&gt;发件 IP 是合法的&lt;/td&gt;&lt;td&gt;接受来信&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Fail&lt;/td&gt;&lt;td&gt;发件 IP 是非法的&lt;/td&gt;&lt;td&gt;退信&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Soft Fail&lt;/td&gt;&lt;td&gt;发件 IP 非法，但是不采取强硬措施&lt;/td&gt;&lt;td&gt;接受来信，但是做标记&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Neutral&lt;/td&gt;&lt;td&gt;SPF 记录中没有关于发件 IP 是否合法的信息&lt;/td&gt;&lt;td&gt;接受来信&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;None&lt;/td&gt;&lt;td&gt;服务器没有设定 SPF 记录&lt;/td&gt;&lt;td&gt;接受来信&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;PermError&lt;/td&gt;&lt;td&gt;发生了严重错误（例如 SPF 记录语法错误）&lt;/td&gt;&lt;td&gt;没有规定&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;TempError&lt;/td&gt;&lt;td&gt;发生了临时错误（例如 DNS 查询失败）&lt;/td&gt;&lt;td&gt;接受或拒绝&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;注意，上面所说的「服务器处理办法」仅仅是 SPF 标准做出的建议，并非所有的邮件服务器都严格遵循这套规定。&lt;/p&gt;&lt;h3 id=&quot;mechanisms&quot;&gt;Mechanisms&lt;/h3&gt;&lt;p&gt;下面介绍上面提到的 mechanism：&lt;/p&gt;&lt;h4 id=&quot;all&quot;&gt;all&lt;/h4&gt;&lt;p&gt;表示所有 IP，肯定会命中。因此通常把它放在 SPF 记录的结尾，表示处理剩下的所有情况。例如：&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&quot;v=spf1 -all&quot; 拒绝所有（表示这个域名不会发出邮件）&quot;v=spf1 +all&quot; 接受所有（域名所有者认为 SPF 是没有用的，或者根本不在乎它）&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&quot;ip4&quot;&gt;ip4&lt;/h4&gt;&lt;p&gt;格式为&lt;code&gt;ip4:&amp;lt;ip4-address&amp;gt;&lt;/code&gt;或者&lt;code&gt;ip4:&amp;lt;ip4-network&amp;gt;/&amp;lt;prefix-length&amp;gt;&lt;/code&gt;，指定一个 IPv4 地址或者地址段。如果&lt;code&gt;prefix-length&lt;/code&gt;没有给出，则默认为&lt;code&gt;/32&lt;/code&gt;。例如：&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&quot;v=spf1 ip4:192.168.0.1/16 -all&quot;只允许在 192.168.0.1 ~ 192.168.255.255 范围内的 IP&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&quot;ip6&quot;&gt;ip6&lt;/h4&gt;&lt;p&gt;格式和&lt;code&gt;ip4&lt;/code&gt;的很类似，默认的&lt;code&gt;prefix-length&lt;/code&gt;是&lt;code&gt;/128&lt;/code&gt;。例如：&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&quot;v=spf1 ip6:1080::8:800:200C:417A/96 -all&quot;只允许在 1080::8:800:0000:0000 ~ 1080::8:800:FFFF:FFFF 范围内的 IP&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&quot;a--mx&quot;&gt;a 和 mx&lt;/h4&gt;&lt;p&gt;这俩的格式是相同的，以&lt;code&gt;a&lt;/code&gt;为例，格式为以下四种之一：&lt;/p&gt;&lt;pre&gt;&lt;code&gt;aa/&amp;lt;prefix-length&amp;gt;a:&amp;lt;domain&amp;gt;a:&amp;lt;domain&amp;gt;/&amp;lt;prefix-length&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;会命中相应域名的 a 记录（或 mx 记录）中包含的 IP 地址（或地址段）。如果没有提供域名，则使用当前域名。例如：&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&quot;v=spf1 mx -all&quot;允许当前域名的 mx 记录对应的 IP 地址。&quot;v=spf1 mx mx:deferrals.example.com -all&quot;允许当前域名和 deferrals.example.com 的 mx 记录对应的 IP 地址。&quot;v=spf1 a/24 -all&quot;类似地，这个用法则允许一个地址段。&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;例如，这是一个比较常见的 SPF 记录，它表示支持当前域名的 a 记录和 mx 记录，同时支持一个给定的 IP 地址；其他地址则拒绝：&lt;/p&gt;&lt;pre&gt;&lt;code&gt;v=spf1 a mx ip4:173.194.72.103 -all&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&quot;include&quot;&gt;include&lt;/h4&gt;&lt;p&gt;格式为&lt;code&gt;include:&amp;lt;domain&amp;gt;&lt;/code&gt;，表示引入&lt;code&gt;&amp;lt;domain&amp;gt;&lt;/code&gt;域名下的 SPF 记录。注意，如果该域名下不存在 SPF 记录，则会导致一个&lt;code&gt;PermError&lt;/code&gt;结果。例如：&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&quot;v=spf1 include:example.com -all&quot; 即采用和 example.com 完全一样的 SPF 记录&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&quot;exists&quot;&gt;exists&lt;/h4&gt;&lt;p&gt;格式为&lt;code&gt;exists:&amp;lt;domain&amp;gt;&lt;/code&gt;。将对&lt;code&gt;&amp;lt;domain&amp;gt;&lt;/code&gt;执行一个 A 查询，如果有返回结果（无论结果是什么），都会看作命中。&lt;/p&gt;&lt;h4 id=&quot;ptr&quot;&gt;ptr&lt;/h4&gt;&lt;p&gt;格式为&lt;code&gt;ptr&lt;/code&gt;或者&lt;code&gt;ptr:&amp;lt;domain&amp;gt;&lt;/code&gt;。使用&lt;code&gt;ptr&lt;/code&gt;机制会带来大量很大开销的 DNS 查询，所以连官方都不推荐使用它。&lt;/p&gt;&lt;h3 id=&quot;vspf1&quot;&gt;关于v=spf1&lt;/h3&gt;&lt;p&gt;这是必须的，这个表示采用 SPF 1 版本，现在它的最新版本就是第 1 版。&lt;/p&gt;&lt;h3 id=&quot;modifiers&quot;&gt;Modifiers&lt;/h3&gt;&lt;p&gt;SPF 记录中还可以包括两种可选的 modifier；一个 modifier 只能出现一次。&lt;/p&gt;&lt;h4 id=&quot;redirect&quot;&gt;redirect&lt;/h4&gt;&lt;p&gt;格式为&lt;code&gt;redirect=&amp;lt;domain&amp;gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;将用给定域名的 SPF 记录替换当前记录。&lt;/p&gt;&lt;h4 id=&quot;exp&quot;&gt;exp&lt;/h4&gt;&lt;p&gt;格式为&lt;code&gt;exp=&amp;lt;domain&amp;gt;&lt;/code&gt;，目的是如果邮件被拒绝，可以给出一个消息。而消息的具体内容会首先对&lt;code&gt;&amp;lt;domain&amp;gt;&lt;/code&gt;执行 TXT 查询，然后执行宏扩展得到。&lt;/p&gt;&lt;h2 id=&quot;spf--2&quot;&gt;如何用 SPF 保护我的域名&lt;/h2&gt;&lt;p&gt;如果你拥有自己的域名，并且用它发送邮件，那么你应该为它添加 SPF。通过域名服务商提供的「域名解析」、「DNS Editor」或者「DNS Zone Editor」等功能添加，并填写正确的 SPF 数据就可以了。&lt;/p&gt;&lt;p&gt;严格来说，SPF 数据应该创建为 SPF 记录。但是鉴于很多 DNS 服务商不支持 SPF 记录，甚至有的邮件服务器也不支持 SPF 记录，因此也可以创建为一条 TXT 记录。目前，你应该至少创建一条 TXT 记录。&lt;/p&gt;&lt;p&gt;因为本质上 SPF 的作用是为一个域名指定合法的发件 IP，所以你需要知道自己使用的邮件服务器的发件 IP 是什么。如果你使用第三方的域名邮箱服务（比如腾讯的域名邮箱），那么他们应该有相应的文档告诉你该怎么填写。如果你用虚拟主机，则主机提供商也应该会告诉你。&lt;/p&gt;&lt;h2 id=&quot;section&quot;&gt;生效时间&lt;/h2&gt;&lt;p&gt;SPF 记录本质上是一个 DNS 记录，所以并不是修改之后立即生效的——通常需要几个小时的时间。&lt;/p&gt;&lt;h2 id=&quot;section-1&quot;&gt;一些工具&lt;/h2&gt;&lt;p&gt;虽然我不能帮你决定 SPF 该填什么，但是这里有一些非常好用的工具可以帮助你，点击它们在新窗口打开：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://www.openspf.org&quot;&gt;Open SPF&lt;/a&gt;：官方网站，有很多资料，值得一看。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://www.openspf.org/RFC_4408&quot;&gt;RFC 4408&lt;/a&gt;：SPFv1 的规范全文。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://www.kitterman.com/spf/validate.html&quot;&gt;SPF Recored Testing Tools&lt;/a&gt;：帮助你验证域名是否存在 SPF 记录、记录的语法是否正确，以及输入 IP 和 host 来测试是不是真正管用。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://www.openspf.org/Why&quot;&gt;SPF: Why?&lt;/a&gt;：为什么我发的邮件被拒绝了？&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://tools.bevhost.com/cgi-bin/dnslookup&quot;&gt;Beveridge Hosting DNS Lookup&lt;/a&gt;：图形界面的&lt;code&gt;dig&lt;/code&gt;，也可以显示查询到的 SPF 信息。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://tools.bevhost.com/spf/&quot;&gt;Beveridge SPF Test&lt;/a&gt;：另一个根据 IP 和 host 验证 SPF 是否通过的工具。&lt;/li&gt;&lt;li&gt;Gmail：如果你有 Gmail 的话，可以给自己的 Gmail 邮箱发一封邮件，然后用 Gmail 独有的 Show Original 功能查看 Gmail 服务器的 SPF 判断结果。&lt;/li&gt;&lt;/ul&gt;</description><pubDate>Mon, 17 Feb 2014 00:00:00 +0800</pubDate><link>https://www.renfei.org/blog/introduction-to-spf.html</link><guid isPermaLink="true">https://www.renfei.org/blog/introduction-to-spf.html</guid></item><item><title>HTML5 简介（七）：在线检测、contenteditable、classList 等特性介绍</title><description>&lt;p&gt;除了前五篇介绍的之外，HTML5 还有一些比较实用的新特性，今天把它们汇总起来介绍一下。&lt;/p&gt;&lt;h3 id=&quot;hidden-&quot;&gt;hidden 属性&lt;/h3&gt;&lt;p&gt;HTML 5 添加了一个hidden属性，所有元素都可以有，效果就是把当前元素隐藏起来。功能比较小，但是就不用为它搞 CSS 了。&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;p&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;tip&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;hidden&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;If your browser support this feature you will not see me.&lt;span class=&quot;nt&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;需要注意的是，不存在&lt;code&gt;hidden=&quot;false&quot;&lt;/code&gt;的写法。如果这样写，会被等效为上面代码中的情况，同样会被隐藏。JavaScript 操作该属性的方法如下：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;hidden&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// Enable&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;hidden&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Disable&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&quot;classlist-api&quot;&gt;classList API&lt;/h3&gt;&lt;p&gt;新增的classList API使得给元素添加、删除一个类更简单了。基本的用法如下：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;classList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;class-to-add&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;classList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;class-to-terminate&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;classList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toggle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;class-to-toggle&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// 下面这个方法用来检测是不是存在一个 class&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;classList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;class-to-check&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// 可以返回有几个类&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;classList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果你用过 jQuery，那么就会发现这个 API 和 jQuery 提供的addClass等函数比起来，有两个不足：不能首尾相连地使用、不能一次操纵多个类。&lt;/p&gt;&lt;h3 id=&quot;content-editable-&quot;&gt;Content Editable 属性&lt;/h3&gt;&lt;p&gt;又一个所有元素都能用的属性。添加之后，用户就可以更改其中的内容了。代码：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;contenteditable&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Hello, feel free to edit me!&lt;span class=&quot;nt&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;p&amp;gt;&lt;/span&gt;And mee!&lt;span class=&quot;nt&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;JavaScript 操作方法：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;contentEditable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// Enable&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;contentEditable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Disable&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;效果：&lt;/p&gt;&lt;div contenteditable=&quot;&quot; style=&quot;border: 1px solid #ddd; padding: 10px; margin: 15px 0;&quot;&gt;Hello, feel free to edit me!And mee!&lt;/div&gt;&lt;p&gt;特别说明的是，通过一些额外的 JavaScript 代码，利用这个属性，可以把它搞成一个编辑器类似物。但是比较麻烦，也超过了今天文章的范围，就不罗嗦了。&lt;/p&gt;&lt;h3 id=&quot;section&quot;&gt;在线/离线检测&lt;/h3&gt;&lt;p&gt;这是一个小巧、在某些情况下实用的功能。可以用表达式&lt;code&gt;navigator.onLine&lt;/code&gt;来检测现在用户是否联网。&lt;/p&gt;&lt;p&gt;同时，提供了事件监听器：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// 你可以把事件注册到 window, document 或者 document.body&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;online&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;status&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;innerHTML&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Online&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;offline&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;status&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;innerHTML&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Offline&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description><pubDate>Sun, 16 Feb 2014 00:00:00 +0800</pubDate><link>https://www.renfei.org/blog/html5-introduction-7.html</link><guid isPermaLink="true">https://www.renfei.org/blog/html5-introduction-7.html</guid></item><item><title>HTML5 简介（六）：本地存储简明教程</title><description>&lt;p&gt;HTM5 涉及存储的特性一共有三个，分别是 &lt;a href=&quot;http://dev.w3.org/html5/webstorage/&quot;&gt;Web Storage&lt;/a&gt;，&lt;a href=&quot;http://dev.w3.org/html5/webdatabase/&quot;&gt;Web SQL Database&lt;/a&gt; 和 &lt;a href=&quot;http://www.w3.org/TR/IndexedDB/&quot;&gt;IndexedDB&lt;/a&gt;。其中支持最广泛的是 Local Storage，IE8 开始竟然就支持了。而其他主流浏览器也在好几个版本之前就已经支持。而 Web SQL Database 目前 IE 和 Firefox 都不支持，IndexedDB 则 Safari 和 iOS 均不支持（嗯，这些新特性 Chrome 都支持）。这篇文章主要讲 Local Storage。&lt;/p&gt;&lt;h3 id=&quot;section&quot;&gt;基本方法&lt;/h3&gt;&lt;p&gt;Web Storage 主要存储键/值对。它的使用非常简单：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// 存储&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;localStorage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;setItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Name&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Shed&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;localStorage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;setItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Age&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// 方式1&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;localStorage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Gender&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Unspecified&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// 方式2&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// 读取&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;localStorage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Name&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 方式1&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;gender&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;localStorage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Gender&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// 方式2&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;age&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;localStorage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// 方式3&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// 删除&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;localStorage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;removeItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Gender&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;localStorage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;上述「存储」的两种方法中，方式 1 和方式 2 的区别为：只有键为以字母开头的时候才能用方式 2，并且方式 1 不会返回任何内容，而方式 2 会返回设置的值。&lt;/p&gt;&lt;p&gt;还有一些稍微不常用的方法，如下：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// local storage 存储的条目数&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;localStorage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// 内部节点序号为 i 的条目的值, 从 0 开始&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;localStorage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里需要解释一下&lt;code&gt;key&lt;/code&gt;方法。标准并没有规定所谓的「内部节点序号」的生成方式，所以并不能简单地假设&lt;code&gt;key(i)&lt;/code&gt;会返回第 i-1 次插入的值。实际上，&lt;a href=&quot;http://dev.w3.org/html5/webstorage/#the-storage-interface&quot;&gt;标准&lt;/a&gt;是这么说的：&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;The &lt;code&gt;key(n)&lt;/code&gt; method must return the name of the nth key in the list. The order of keys is user-agent defined, but must be consistent within an object so long as the number of keys doesn’t change. (Thus, adding or removing a key may change the order of the keys, but merely changing the value of an existing key must not.) If n is greater than or equal to the number of key/value pairs in the object, then this method must return null.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;也就是说，只能保证在&lt;strong&gt;不增加记录&lt;/strong&gt;的情况下每个值的序号不变，仅此而已。&lt;/p&gt;&lt;h3 id=&quot;storage-&quot;&gt;storage 事件&lt;/h3&gt;&lt;p&gt;HTML5 还为我们带来了和 Storage 相关的事件。事件发生有两个条件：&lt;/p&gt;&lt;ol&gt;&lt;li&gt;localStorage 的内容被改变；&lt;/li&gt;&lt;li&gt;改变&lt;strong&gt;发生在另一个网页中&lt;/strong&gt;（很多人都忽略了这个条件）。&lt;/li&gt;&lt;/ol&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;storage&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;old_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;oldValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;new_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;newValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;事件所包含的参数中，前三个可以自我解释。第四个的含义是触发事件的 URL。&lt;/p&gt;&lt;p&gt;HTML5demos.com 为我们提供了一个 &lt;a href=&quot;http://html5demos.com/storage-events&quot;&gt;Storage Event 的演示&lt;/a&gt;。打开两个窗口，修改其中一个窗口中文本框的内容，另一个窗口的内容也跟着发生了变化。它不仅使用了&lt;code&gt;storage&lt;/code&gt;事件，还使用了&lt;code&gt;keyup&lt;/code&gt;事件。&lt;/p&gt;&lt;h3 id=&quot;section-1&quot;&gt;容量限制&lt;/h3&gt;&lt;p&gt;每个域名下的 localStorage 可以存储 5M 的数据。需要说明的是，这个大小并不是标准强制规定的，只是一个建议。然而，所有浏览器都采纳了这个数值。&lt;/p&gt;&lt;p&gt;如果超过了限额，则会抛出&lt;code&gt;QUOTA_EXCEEDED_ERR&lt;/code&gt;异常。那么，5M 大小可以存储多少数据呢？网上有很多类似的测试，比如&lt;a href=&quot;http://arty.name/localstorage.html&quot;&gt;这里&lt;/a&gt;，可以测试你的浏览器最多可以保存多少个字符。&lt;/p&gt;&lt;h3 id=&quot;localstorage-&quot;&gt;localStorage 划分方法&lt;/h3&gt;&lt;p&gt;localStorage 是按照 scheme + hostname + unique port 来区分的。需要解释一下 scheme，即 http/https，所以 &lt;code&gt;http://example.com&lt;/code&gt; 和 &lt;code&gt;https://example.com&lt;/code&gt; 的 localStorage 是不能互相访问的。&lt;/p&gt;</description><pubDate>Sat, 15 Feb 2014 00:00:00 +0800</pubDate><link>https://www.renfei.org/blog/html5-introduction-6-local-storage.html</link><guid isPermaLink="true">https://www.renfei.org/blog/html5-introduction-6-local-storage.html</guid></item><item><title>HTML5 简介（五）：地理位置 API</title><description>&lt;p&gt;HTML5 允许你通过 &lt;a href=&quot;http://dev.w3.org/geo/api/&quot;&gt;Geolocation API&lt;/a&gt; 获取用户的地理位置。&lt;/p&gt;&lt;h2 id=&quot;geolocation-api&quot;&gt;Geolocation API介绍&lt;/h2&gt;&lt;h3 id=&quot;section&quot;&gt;请求用户许可&lt;/h3&gt;&lt;p&gt;HTML5 中的地理位置是通过经纬度提供的；为了获得用户的许可，应该首先执行以下 JavaScript 语句：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;navigator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;geolocation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getCurrentPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;on_success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;on_error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;getCurrentPosition&lt;/code&gt;包含三个参数，前两个为函数名，第三个为一个对象。其中只有第一个是必须的。当你执行上面的 JavaScript 语句后，浏览器通常会弹出一个提示，询问用户是否允许网站跟踪位置信息；同时&lt;code&gt;getCurrentPosition&lt;/code&gt;函数会立即返回。如果用户选择了允许，则会执行上述&lt;code&gt;on_success&lt;/code&gt;函数，这时你才真正得到位置信息（这就是这件事情为什么要分两步的原因——用户需要一定时间才能对请求作出反应，同时地理位置信息可能需要一定时间才能生成，而函数需要立即返回）。&lt;/p&gt;&lt;h3 id=&quot;section-1&quot;&gt;获得地理位置&lt;/h3&gt;&lt;p&gt;下面是函数&lt;code&gt;on_success&lt;/code&gt;的写法示例。它有一个参数，你将最终通过这个函数获取到用户的位置。经纬度分别存在&lt;code&gt;longitude&lt;/code&gt;和&lt;code&gt;latitude&lt;/code&gt;变量中，同时还有精度、时间戳，以及一些其他额外信息：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;on_success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// 将会获得以下信息&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;latitude&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;coords&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;latitude&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;longitude&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;coords&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;longitude&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;accuracy&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;coords&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;accuracy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;timestamp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// 以下信息不一定提供，和具体设备有关&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;altitude&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;coords&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;altitude&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;altitudeAccuracy&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;coords&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;altitudeAccuracy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;heading&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;coords&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;heading&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;speed&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;coords&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;speed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其中&lt;code&gt;altitude&lt;/code&gt;、&lt;code&gt;altitudeAccuracy&lt;/code&gt;、&lt;code&gt;heading&lt;/code&gt;和&lt;code&gt;speed&lt;/code&gt;则根据用户的终端不同，可能提供，可能为&lt;code&gt;null&lt;/code&gt;。&lt;/p&gt;&lt;h3 id=&quot;section-2&quot;&gt;错误处理&lt;/h3&gt;&lt;p&gt;上面我们还有一个&lt;code&gt;on_error&lt;/code&gt;。显然，如果用户不同意，那我们无法获得任何信息，这时候系统会自动调用这个函数。&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;on_error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;code&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PERMISSION_DENIED&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;User denied Geolocation.&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// handle other cases...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其中&lt;code&gt;error.code&lt;/code&gt;为一个枚举类型，可能的取值如下：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;PERMISSION_DENIED&lt;/code&gt;：用户拒绝&lt;/li&gt;&lt;li&gt;&lt;code&gt;POSITION_UNAVAILABLE&lt;/code&gt;：地理位置获取失败（可能是用户没网或卫星搜不到等原因）&lt;/li&gt;&lt;li&gt;&lt;code&gt;TIMEOUT&lt;/code&gt;：地理位置获取超时&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;而&lt;code&gt;error.message&lt;/code&gt;则为一个可以帮助开发者调试的错误信息（此信息一般不适合直接显示在网页中给用户查看）。&lt;/p&gt;&lt;h3 id=&quot;section-3&quot;&gt;可选项&lt;/h3&gt;&lt;p&gt;事实上，上述&lt;code&gt;getCurrentPosition&lt;/code&gt;函数还支持第三个可选的参数，是一个 Option Object，一共有三个选项可以设定：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;enableHighAccuracy&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;timeout&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;maximumAge&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;60000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其中&lt;code&gt;timeout&lt;/code&gt;是设定地理位置获取的超时时间（单位为毫秒，用户选择允许的时间不计算在内）；而&lt;code&gt;maximumAge&lt;/code&gt;表示允许设备从缓存中读取位置，缓存的过期时间，单位是毫秒，设为&lt;code&gt;0&lt;/code&gt;来禁用缓存读取。如果返回的是缓存中的时间，会在&lt;code&gt;timestamp&lt;/code&gt;中反映出来。&lt;/p&gt;&lt;h3 id=&quot;watchposition&quot;&gt;watchPosition&lt;/h3&gt;&lt;p&gt;除了&lt;code&gt;getCurrentPosition&lt;/code&gt;，HTML5 还提供了&lt;code&gt;watchPosition&lt;/code&gt;方法。这两个方法非常类似，都有同样的三个参数，都立即返回。下面介绍&lt;code&gt;watchPosition&lt;/code&gt;的特殊之处：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;该函数会返回一个整数，表示这个监视任务的 ID。&lt;/li&gt;&lt;li&gt;将持续追踪用户的位置；相应，&lt;code&gt;on_success&lt;/code&gt;函数会被多次地调用来更新信息。&lt;/li&gt;&lt;li&gt;通过&lt;code&gt;clearWatch(id)&lt;/code&gt;函数调用来停止监视。&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;google-maps-api&quot;&gt;调用 Google Maps API&lt;/h2&gt;&lt;p&gt;HTML5 为我们提供了用户的经纬度信息，利用这个信息你可以做很多事情。这里只介绍一个最简单的：利用 &lt;a href=&quot;https://developers.google.com/maps/?hl=zh-cn&quot;&gt;Google Maps API&lt;/a&gt; 在地图上显示用户所处的地点。&lt;/p&gt;&lt;p&gt;需要说明的是，Google Maps API 非常强大，这里只使用了它最基本的功能。以下代码来自官方示例：&lt;/p&gt;&lt;p&gt;HTML 部分：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;script &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;https://maps.googleapis.com/maps/api/js?v=3.exp&amp;amp;sensor=true&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;map-canvas&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;JavaScript 部分：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;mapOptions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;zoom&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;google&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;maps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;map-canvas&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;mapOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// Try HTML5 geolocation&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;navigator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;geolocation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;navigator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;geolocation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getCurrentPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;google&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;maps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;LatLng&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;coords&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;latitude&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;coords&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;longitude&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;infowindow&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;google&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;maps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;InfoWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Location found using HTML5.&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;setCenter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;handleNoGeolocation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// Browser doesn&amp;#39;t support Geolocation&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;handleNoGeolocation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;handleNoGeolocation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;errorFlag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;errorFlag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;content&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Error: The Geolocation service failed.&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;content&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Error: Your browser doesn\&amp;#39;t support geolocation.&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;google&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;maps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;LatLng&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;105&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;infowindow&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;google&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;maps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;InfoWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;setCenter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;google&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;maps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addDomListener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;load&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;section-4&quot;&gt;兼容性&lt;/h2&gt;&lt;h3 id=&quot;section-5&quot;&gt;兼容性列表&lt;/h3&gt;&lt;p&gt;支持 Geolocation API 的浏览器/终端/操作系统：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Firefox 3.5+&lt;/li&gt;&lt;li&gt;Google Chrome 5.0+&lt;/li&gt;&lt;li&gt;Safari 5.0+&lt;/li&gt;&lt;li&gt;Opera 10.60+&lt;/li&gt;&lt;li&gt;Internet Explorer 9.0+&lt;/li&gt;&lt;li&gt;Android 2.0+&lt;/li&gt;&lt;li&gt;iOS 3.0+&lt;/li&gt;&lt;li&gt;Opera Mobile 10.1+&lt;/li&gt;&lt;li&gt;Blackberry OS 6&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&quot;section-6&quot;&gt;测试兼容性&lt;/h3&gt;&lt;p&gt;要测试是否支持 Geolocation API 很简单：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;navigator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;geolocation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Geolocation is there for you.&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Geolocation is not supported.&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description><pubDate>Fri, 14 Feb 2014 00:00:00 +0800</pubDate><link>https://www.renfei.org/blog/html5-introduction-5-geolocation.html</link><guid isPermaLink="true">https://www.renfei.org/blog/html5-introduction-5-geolocation.html</guid></item><item><title>HTML5 简介（四）：新的表单类型、表单验证等特性介绍</title><description>&lt;p&gt;HTML5 为标准的 Web 表单增添了一系列新功能。&lt;/p&gt;&lt;h2 id=&quot;placeholder&quot;&gt;Placeholder&lt;/h2&gt;&lt;p&gt;即占位符，通过&lt;code&gt;placeholder&lt;/code&gt;属性实现，大部分 HTML5 浏览器都支持这一特性。再也不用麻烦 JavaScript 来完成这件事儿了。。用法：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;s&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;placeholder=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;Search&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;auto-focus&quot;&gt;Auto Focus&lt;/h2&gt;&lt;p&gt;就是默认使光标聚焦于某一文本框，这样用户打开网页即可输入文字，不用鼠标再选择。目前 Google、百度都已经实现了这个特性。用法：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;s&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;autofocus&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;datalist&quot;&gt;datalist&lt;/h2&gt;&lt;p&gt;&lt;code&gt;datalist&lt;/code&gt;属性提供了一个下拉列表，直接看代码：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;list=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;cars&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;datalist&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;cars&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;option&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;BMW&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;option&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;Ford&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;option&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;Volvo&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/datalist&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Chrome 下的效果：&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;//img.renfei.org/2014/02/datalist-1.png&quot; alt=&quot;datalist-1&quot; /&gt;&lt;/p&gt;&lt;p&gt;Firefox 和 IE10 也支持&lt;code&gt;datalist&lt;/code&gt;，Safari 不支持。此外，也可以给&lt;code&gt;option&lt;/code&gt;标签增加&lt;code&gt;label&lt;/code&gt;属性，但是目前这个属性在 Firefox 中有 bug。例如写成&lt;code&gt;&amp;lt;option label=&quot;$43K&quot; value=&quot;BMW&quot;&amp;gt;&lt;/code&gt;，在 Chrome 下是这个效果：&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;//img.renfei.org/2014/02/datalist-2.png&quot; alt=&quot;datalist-2&quot; /&gt;&lt;/p&gt;&lt;p&gt;但是在 Firefox 中却变成了这样，违背了我们本来的意图：&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;//img.renfei.org/2014/02/datalist-3.png&quot; alt=&quot;datalist-3&quot; /&gt;&lt;/p&gt;&lt;h2 id=&quot;input-types&quot;&gt;Input types&lt;/h2&gt;&lt;p&gt;HTML5 为&lt;code&gt;input&lt;/code&gt;标签新增了 13 中新类型。例如，为了使用新增的&lt;code&gt;email&lt;/code&gt;类型，只需要：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;email&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;另外提一下兼容性问题。对于老浏览器（包括 IE 6），遇到未知的「type」时一律会当成「text」类型的来对待，所以一般来说使用新标记不会对老浏览器造成干扰。&lt;/p&gt;&lt;p&gt;下面来看一看这些新类型都是啥。&lt;/p&gt;&lt;h3 id=&quot;email&quot;&gt;email&lt;/h3&gt;&lt;p&gt;目前使用&lt;code&gt;type=&quot;email&quot;&lt;/code&gt;在大部分浏览器中还没有特殊的视觉效果。iPhone 遇到这类文本框，会在键盘中添加&lt;code&gt;@&lt;/code&gt;和&lt;code&gt;.&lt;/code&gt;符号。&lt;/p&gt;&lt;p&gt;不过，虽然没有视觉效果，但是浏览器实际上却在「验证」这些设置了&lt;code&gt;type=&quot;email&quot;&lt;/code&gt;的文本框里的内容是否为合法的 Email 地址。开发者可以通过 CSS&lt;code&gt;:valid&lt;/code&gt;等特殊的伪类来人为地控制不同情况下的样式，详见后文。&lt;/p&gt;&lt;p&gt;&lt;span id=&quot;more-955&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;h3 id=&quot;url&quot;&gt;url&lt;/h3&gt;&lt;p&gt;同样，目前对于&lt;code&gt;type=&quot;url&quot;&lt;/code&gt;，大部分浏览器也不会添加样式；但是 iPhone 例外——对于这类文本框，你会看到键盘又变了：多了&lt;code&gt;.com&lt;/code&gt;键。&lt;/p&gt;&lt;h3 id=&quot;number&quot;&gt;number&lt;/h3&gt;&lt;p&gt;对于&lt;code&gt;type=&quot;number&quot;&lt;/code&gt;，又多了几个属性：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;number&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;min=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;0&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;max=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;100&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;step=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;25&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;即&lt;code&gt;min&lt;/code&gt;指定了最小值，&lt;code&gt;max&lt;/code&gt;最大值，&lt;code&gt;step&lt;/code&gt;则为步长。这些属性都是可以省略的，只规定需要的部分即可。同时，HTML5 还提供了对应的几个 JavaScript 方法和变量：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stepUp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stepDown&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;valueAsNumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;最新的 Safari, Chrome, Opera 支持&lt;code&gt;type=&quot;number&quot;&lt;/code&gt;，而 Firefox 和 IE 11 部分支持。&lt;/p&gt;&lt;h3 id=&quot;range&quot;&gt;range&lt;/h3&gt;&lt;p&gt;&lt;code&gt;range&lt;/code&gt;类型实际上是用一个划块表示的数字。例如：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;range&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;min=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;0&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;max=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;100&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;step=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;5&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;所需的属性除了&lt;code&gt;type&lt;/code&gt;之外，和上一个是完全一样的。另外，HTML 5 浏览器基本上都支持这一类型。&lt;/p&gt;&lt;h3 id=&quot;date-time-datetime-month-week&quot;&gt;date, time, datetime, month, week&lt;/h3&gt;&lt;p&gt;这一组是有关时间选择的。&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;date&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;time&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;datetime&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;month&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;week&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;datetime-local&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这些类型目前 IE 11、Firefox 和桌面版 Safari 全部不支持；iOS Safari 支持一点，Chrome 支持大部分。&lt;/p&gt;&lt;h3 id=&quot;search&quot;&gt;search&lt;/h3&gt;&lt;p&gt;专门用于搜索框。和一般文本框不同之处在于，Safari 和 Chrome 会把它们渲染成圆角的。并且当你输入文字之后，右面会多出来一个叉，点击叉可以清除所有文字。这和 OS X 系统的搜索框的体验是一致的。如果规定&lt;code&gt;results&lt;/code&gt;属性，左边就会自动出现一个放大镜图标，同时 Safari 还提供「最近的搜索」原生支持。&lt;/p&gt;&lt;h3 id=&quot;color&quot;&gt;color&lt;/h3&gt;&lt;p&gt;这是一个颜色选取器，不支持的浏览器则会显示为一个文本框。目前我发现 Chrome 是支持的。&lt;/p&gt;&lt;h2 id=&quot;section&quot;&gt;表单验证&lt;/h2&gt;&lt;h3 id=&quot;type-&quot;&gt;根据 type 进行验证&lt;/h3&gt;&lt;p&gt;如果你设定了某些特定的&lt;code&gt;type&lt;/code&gt;，则支持此功能的浏览器会自动对用户输入的合法性进行验证，并在尝试提交表单时对错误的字段给出提示。下面是 Chrome 下分别设定&lt;code&gt;type=&quot;email&quot;&lt;/code&gt;和&lt;code&gt;type=&quot;url&quot;&lt;/code&gt;并填错的提示效果：&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;//img.renfei.org/2014/02/validation-2.png&quot; alt=&quot;validation-2&quot; /&gt;&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;//img.renfei.org/2014/02/validation-3.png&quot; alt=&quot;validation-3&quot; /&gt;&lt;/p&gt;&lt;h3 id=&quot;minmax-&quot;&gt;根据 min/max 进行验证&lt;/h3&gt;&lt;p&gt;如果用户填写的数字不符合你的范围要求，则支持此功能的浏览器也会在用户尝试提交表单时给出提示。目前我只发现 Chrome 是支持的：&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;//img.renfei.org/2014/02/validation-1.png&quot; alt=&quot;validation-1&quot; /&gt;&lt;/p&gt;&lt;h3 id=&quot;pattern-&quot;&gt;pattern 属性&lt;/h3&gt;&lt;p&gt;&lt;code&gt;pattern&lt;/code&gt;属性很强大，可以根据一个正则表达式来判断用户输入的正确与否。&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;form&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;text&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;pattern=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;[A-Za-z]{3}&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;Just input three letters&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;submit&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;效果：也是 Chrome 浏览器下的效果：&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;//img.renfei.org/2014/02/validation-4.png&quot; alt=&quot;validation-4&quot; /&gt;&lt;/p&gt;&lt;p&gt;目前 IE 10+、Chrome、Firefox、Opera 支持&lt;code&gt;pattern&lt;/code&gt;属性，Safari 不支持。&lt;/p&gt;&lt;h3 id=&quot;required-&quot;&gt;required 属性&lt;/h3&gt;&lt;p&gt;如果某个字段是必填的，可以给它添加&lt;code&gt;required&lt;/code&gt;属性，支持这个属性的浏览器可能会做出一些外观/交互上的变化：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;text&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;效果：&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;//img.renfei.org/2014/02/validation-5.png&quot; alt=&quot;validation-5&quot; /&gt;&lt;/p&gt;&lt;p&gt;目前 IE 10+、Firefox、Chrome、Opera 支持&lt;code&gt;required&lt;/code&gt;属性，Safari 不支持。&lt;/p&gt;&lt;h3 id=&quot;section-1&quot;&gt;防止自动验证&lt;/h3&gt;&lt;p&gt;如果要防止自动验证，可以对整个&lt;code&gt;form&lt;/code&gt;使用&lt;code&gt;novalidate&lt;/code&gt;：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;form&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;novalidate&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;email&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;email&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- ..other stuff.. --&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;也可以对单个&lt;code&gt;input&lt;/code&gt;使用&lt;code&gt;formnovalidate&lt;/code&gt;：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;text&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;formnovalidate&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;css&quot;&gt;表单验证与 CSS&lt;/h2&gt;&lt;p&gt;为了配合 HTML5 强大的表单验证，CSS3 中同样有几个伪类被引入，来支持对不同验证状态的表单进行选择。你可以为它们添加样式。&lt;/p&gt;&lt;h3 id=&quot;required--optional-&quot;&gt;required 和 optional 伪类&lt;/h3&gt;&lt;p&gt;所有含有&lt;code&gt;required&lt;/code&gt;属性的表单元素可以被&lt;code&gt;:required&lt;/code&gt;选取；其余元素可以被&lt;code&gt;:optional&lt;/code&gt;选取。&lt;/p&gt;&lt;h3 id=&quot;valid--invalid-&quot;&gt;valid 和 invalid 伪类&lt;/h3&gt;&lt;p&gt;当一个输入框的内容被浏览器认为是「有效的」时，它就可以被&lt;code&gt;valid&lt;/code&gt;伪类选取。相反，当一个输入框的内容被视为「无效的」时，既可被&lt;code&gt;invalid&lt;/code&gt;伪类选取。&lt;/p&gt;&lt;ul&gt;&lt;li&gt;任何不加&lt;code&gt;required&lt;/code&gt;限制的空&lt;code&gt;input&lt;/code&gt;总是有效的。&lt;/li&gt;&lt;li&gt;一个输入了任何字符的&lt;code&gt;&amp;lt;input required&amp;gt;&lt;/code&gt;为有效的。&lt;/li&gt;&lt;li&gt;一个输入了合法 Email 地址的&lt;code&gt;&amp;lt;input type=&quot;email&quot; required&amp;gt;&lt;/code&gt;是有效的。&lt;/li&gt;&lt;li&gt;一个输入数值在限定范围内的&lt;code&gt;&amp;lt;input type=&quot;number&quot; min=&quot;0&quot; max=&quot;100&quot; required&amp;gt;&lt;/code&gt;是有效的。等等。&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&quot;in-range--out-of-range-&quot;&gt;in-range 和 out-of-range 伪类&lt;/h3&gt;&lt;p&gt;这两个伪类主要针对含有「范围」含义的输入类型，例如&lt;code&gt;number&lt;/code&gt;。&lt;/p&gt;&lt;ul&gt;&lt;li&gt;数值类型不合法算作&lt;code&gt;out-of-range&lt;/code&gt;。&lt;/li&gt;&lt;li&gt;没有规定&lt;code&gt;min&lt;/code&gt;和&lt;code&gt;max&lt;/code&gt;也可能发生&lt;code&gt;out-of-range&lt;/code&gt;（见上一种情况）。&lt;/li&gt;&lt;li&gt;数值在&lt;code&gt;min&lt;/code&gt;和&lt;code&gt;max&lt;/code&gt;范围之外算作&lt;code&gt;out-of-range&lt;/code&gt;。&lt;/li&gt;&lt;li&gt;属于&lt;code&gt;out-of-range&lt;/code&gt;则一定属于&lt;code&gt;invalid&lt;/code&gt;，反之不成立（还需要是范围类的标签类型）。&lt;/li&gt;&lt;li&gt;属于&lt;code&gt;in-range&lt;/code&gt;则一定属于&lt;code&gt;valid&lt;/code&gt;，反之也成立。&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;目前 Firefox 和 IE 不支持这两个伪类。&lt;/p&gt;&lt;h2 id=&quot;form-&quot;&gt;form 属性&lt;/h2&gt;&lt;p&gt;这个属性的作用是使得不在&lt;code&gt;form&lt;/code&gt;标签内的&lt;code&gt;input&lt;/code&gt;项目属于某个&lt;code&gt;form&lt;/code&gt;，需要用 ID 来指定。例如：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;form&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;method=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;get&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;demo&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;field1&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;text&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;submit&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;form&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;field2&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;text&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;form=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;demo&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;IE 11 不支持&lt;code&gt;form&lt;/code&gt;属性，而 Safari、Chrome、Opera、Firefox 都支持。&lt;/p&gt;&lt;p&gt;和别的 HTML5 特性不同，表单这块浏览器的支持普遍是参差不齐的；使用的时候需要意识到这一点。对不支持的浏览器使用这些新特性基本是无害的（不会影响原来的效果），但是必要的时候要为它们提供 Fallback。&lt;/p&gt;&lt;p&gt;再推荐给大家一个网站：&lt;a href=&quot;http://caniuse.com/&quot;&gt;Can I Use&lt;/a&gt;，这里可以查到各个特性浏览器的支持情况，很方便。&lt;/p&gt;</description><pubDate>Thu, 13 Feb 2014 00:00:00 +0800</pubDate><link>https://www.renfei.org/blog/html5-introduction-4-form-types-and-validation.html</link><guid isPermaLink="true">https://www.renfei.org/blog/html5-introduction-4-form-types-and-validation.html</guid></item><item><title>HTML5 简介（三）：利用 History API 无刷新更改地址栏</title><description>&lt;p&gt;HTML5 新增的历史记录 API 可以实现无刷新更改地址栏链接，配合 AJAX 可以做到无刷新跳转。&lt;/p&gt;&lt;p&gt;简单来说：假设当前页面为&lt;code&gt;renfei.org/&lt;/code&gt;，那么执行下面的 JavaScript 语句：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;history&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pushState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;/profile/&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;之后，地址栏的地址就会变成&lt;code&gt;renfei.org/profile/&lt;/code&gt;，但同时浏览器不会刷新页面，甚至不会检测目标页面是否存在。&lt;/p&gt;&lt;h2 id=&quot;pushstate-&quot;&gt;pushState 方法&lt;/h2&gt;&lt;p&gt;上面的语句实际上用到了 HTML5 的历史记录 API。这套 API 提供一种「人为操纵」浏览器历史记录的方法。&lt;/p&gt;&lt;p&gt;浏览器历史记录可以看作一个「栈」。栈是一种后进先出的结构，可以把它想象成一摞盘子，用户每点开一个新网页，都会在上面加一个新盘子，叫「入栈」。用户每次点击「后退」按钮都会取走最上面的那个盘子，叫做「出栈」。而每次浏览器显示的自然是最顶端的盘子的内容。&lt;/p&gt;&lt;p&gt;执行&lt;code&gt;pushState&lt;/code&gt;函数之后，会往浏览器的历史记录中添加一条新记录，同时改变地址栏的地址内容。它可以接收三个参数，按顺序分别为：&lt;/p&gt;&lt;ol&gt;&lt;li&gt;一个对象或者字符串，用于描述新记录的一些特性。这个参数会被一并添加到历史记录中，以供以后使用。这个参数是开发者根据自己的需要自由给出的。&lt;/li&gt;&lt;li&gt;一个字符串，代表新页面的标题。当前基本上所有浏览器都会忽略这个参数。&lt;/li&gt;&lt;li&gt;一个字符串，代表新页面的相对地址。&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;例如，我们可以这样写：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;profile&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;history&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pushState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;My Profile&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;/profile/&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;popstate-&quot;&gt;popstate 事件&lt;/h2&gt;&lt;p&gt;当用户点击浏览器的「前进」、「后退」按钮时，就会触发&lt;code&gt;popstate&lt;/code&gt;事件。你可以监听这一事件，从而作出反应。&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;popstate&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// do something...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里&lt;code&gt;e.state&lt;/code&gt;就是当初&lt;code&gt;pushState&lt;/code&gt;时传入的第一个参数。例如，在我们的例子中，有：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;profile&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;replacestate-&quot;&gt;replaceState 方法&lt;/h2&gt;&lt;p&gt;有时，你希望不添加一个新记录，而是替换当前的记录（比如对网站的 landing page），则可以使用&lt;code&gt;replaceState&lt;/code&gt;方法。这个方法和&lt;code&gt;pushState&lt;/code&gt;的参数完全一样。&lt;/p&gt;&lt;h2 id=&quot;ajax-ajax-&quot;&gt;应用：全站 AJAX，并使浏览器能够抓取 AJAX 页面&lt;/h2&gt;&lt;p&gt;这个可以干啥用？一个比较常用的场景就是，配合 AJAX。&lt;/p&gt;&lt;p&gt;假设一个页面左侧是若干导航链接，右侧是内容，同时导航时只有右侧的内容需要更新，那么刷新整个页面无疑是浪费的。这时我们可以使用 AJAX 来拉取右面的数据。但是如果仅仅这样，地址栏是不会改变的，用户无法前进、后退，也无法收藏当前页面或者把当前页面分享给他人；搜索引擎抓取也有困难。这时，就可以使用 HTML5 的 History API 来解决这个问题。&lt;/p&gt;&lt;p&gt;思路：首先绑定&lt;code&gt;click&lt;/code&gt;事件。当用户点击一个链接时，通过&lt;code&gt;preventDefault&lt;/code&gt;函数防止默认的行为（页面跳转），同时读取链接的地址（如果有 jQuery，可以写成&lt;code&gt;$(this).attr(&#39;href&#39;)&lt;/code&gt;），把这个地址通过&lt;code&gt;pushState&lt;/code&gt;塞入浏览器历史记录中，再利用 AJAX 技术拉取（如果有 jQuery，可以使用&lt;code&gt;$.get&lt;/code&gt;方法）这个地址中真正的内容，同时替换当前网页的内容。&lt;/p&gt;&lt;p&gt;为了处理用户前进、后退，我们监听&lt;code&gt;popstate&lt;/code&gt;事件。当用户点击前进或后退按钮时，浏览器地址自动被转换成相应的地址，同时&lt;code&gt;popstate&lt;/code&gt;事件发生。在事件处理函数中，我们根据当前的地址抓取相应的内容，然后利用 AJAX 拉取这个地址的真正内容，呈现，即可。&lt;/p&gt;&lt;p&gt;最后，整个过程是不会改变页面标题的，可以通过直接对&lt;code&gt;document.title&lt;/code&gt;赋值来更改页面标题。&lt;/p&gt;&lt;h2 id=&quot;section&quot;&gt;其他说明&lt;/h2&gt;&lt;h3 id=&quot;url-&quot;&gt;URL 的限制&lt;/h3&gt;&lt;p&gt;为了安全考虑，新 URL 必须和当前 URL 在同一个域名下。例如，你不能把地址改成 Google 的首页。否则不怀好心的人就可以把地址改成网银等关键网站的地址，来迷惑用户了。&lt;/p&gt;&lt;p&gt;但是，URL 允许使用 query string 的形式。例如：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;history&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pushState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;?id=1&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在某些情况下可能比较方便。&lt;/p&gt;&lt;h3 id=&quot;section-1&quot;&gt;浏览器兼容性&lt;/h3&gt;&lt;p&gt;根据 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Manipulating_the_browser_history&quot;&gt;MDN 提供的信息&lt;/a&gt;，IE 10, Chrome 5, Firefox 4, Safari 5 开始支持这个特性。Fallback 可以采用替换 hash 的方法。另外，&lt;a href=&quot;https://github.com/browserstate/history.js/&quot;&gt;History.js&lt;/a&gt; 库也提供了对老版本浏览器的 history API 支持（同样是利用替换 hash）。为了搜索引擎收录，可能需要使用&lt;code&gt;#!&lt;/code&gt;表示法。&lt;/p&gt;</description><pubDate>Wed, 12 Feb 2014 00:00:00 +0800</pubDate><link>https://www.renfei.org/blog/html5-introduction-3-history-api.html</link><guid isPermaLink="true">https://www.renfei.org/blog/html5-introduction-3-history-api.html</guid></item><item><title>HTML5 简介（二）：元素的 data-* 属性详解</title><description>&lt;p&gt;data-* 属性是 HTML 5 的新特性，允许用户在 DOM 中存储自定义信息。&lt;/p&gt;&lt;p&gt;以前，需要存储含有特定含义的信息通常是通过 class 完成的，但这并不是 class 本来的用途。现在，利用 HTML 5，可以为元素添加&lt;code&gt;data-*&lt;/code&gt;属性，从而存储自定义信息。其中&lt;code&gt;*&lt;/code&gt;是可以自定义的部分。例如：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;article&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;tu&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;data-category=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;Web Development&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;data-author=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;1&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt; ... &lt;span class=&quot;nt&quot;&gt;&amp;lt;/article&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;javascript-&quot;&gt;通过 JavaScript 访问&lt;/h2&gt;&lt;p&gt;通过 JavaScript 访问自定义的信息有两种方式：&lt;code&gt;getAttribute()&lt;/code&gt;和&lt;code&gt;dataset&lt;/code&gt;。&lt;/p&gt;&lt;h3 id=&quot;getattribute-&quot;&gt;getAttribute 方法&lt;/h3&gt;&lt;p&gt;这就是经典的取得一个元素属性的方式，和以前一样。&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;tu&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;data-category&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// &amp;quot;Web Development&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&quot;dataset-&quot;&gt;dataset 方法&lt;/h3&gt;&lt;p&gt;这是 HTML 5 新增的方法，可以更方便的读取所有的 data 信息。&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;article&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;tu&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;article&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;dataset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;alert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;category&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// &amp;quot;Web Development&amp;quot;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;alert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;jquery-&quot;&gt;通过 jQuery 访问&lt;/h2&gt;&lt;p&gt;jQuery 也提供了专门的&lt;code&gt;data&lt;/code&gt;方法来访问这些信息：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;#tu&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;category&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// &amp;quot;Web Development&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;css-&quot;&gt;通过 CSS 访问&lt;/h2&gt;&lt;h3 id=&quot;attr&quot;&gt;使用 attr&lt;/h3&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-css&quot; data-lang=&quot;css&quot;&gt;&lt;span class=&quot;nt&quot;&gt;article&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:before&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;category&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&quot;section&quot;&gt;使用属性选择器&lt;/h3&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-css&quot; data-lang=&quot;css&quot;&gt;&lt;span class=&quot;nt&quot;&gt;article&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;data-author&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;border-width&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;section-1&quot;&gt;修改信息&lt;/h2&gt;&lt;p&gt;在引入 jQuery 之前，使用 JavaScript 修改自定义信息也非常容易，两种方式都可以：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;tu&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;dataset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;category&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Uncategorized&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;data-category&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Uncategorized&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然而如果你用 jQuery 的&lt;code&gt;data&lt;/code&gt;方法进行修改：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;#tu&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;category&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Uncategorized&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;会发现 HTML 代码并没有改动，同时使用&lt;code&gt;dataset&lt;/code&gt;等标准 JavaScript 读出来的数据也是修改之前的数据。类似，如果先用 jQuery 读取一次，然后再用标准 JavaScript 修改，再用 jQuery 读取第二次，则发现读取的数据仍是修改之前的数据。可是如果没有经过 jQuery 读取直接用标准 JavaScript 修改，这时再用 jQuery 读取，读到的就是新数据了。&lt;/p&gt;&lt;p&gt;这是怎么回事儿？&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;http://api.jquery.com/data&quot; title=&quot;jQuery .data() Documentation&quot;&gt;jQuery 的文档&lt;/a&gt;对此有所说明：&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;The data- attributes are pulled in the first time the data property is accessed and then are no longer accessed or mutated (all data values are then stored internally in jQuery).&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;原来，标准 JavaScript 直接对 DOM 进行读写（和我们设想的一致），但是 jQuery 不同。jQuery 的内部也维护着一份这些 data 数据。当第一次使用 jQuery 读取时，数据被从 DOM 读到&lt;code&gt;jQuery.cache&lt;/code&gt;保存起来，以后使用 jQuery 修改时，修改的是内部维护的这个数据，同时再次读取也会从 jQuery 内部而非 DOM 读取数据。&lt;/p&gt;&lt;p&gt;因此，最好的办法是，要么一直用标准 JavaScript，要么一直用 jQuery。不要两者混用就好了。jQuery 如此设计的初衷就是为了减少 DOM 读写从而提高性能。&lt;/p&gt;</description><pubDate>Tue, 11 Feb 2014 00:00:00 +0800</pubDate><link>https://www.renfei.org/blog/html5-introduction-2-data-attribute.html</link><guid isPermaLink="true">https://www.renfei.org/blog/html5-introduction-2-data-attribute.html</guid></item><item><title>HTML5 简介（一）：新的写法、元素及兼容性</title><description>&lt;p&gt;来看看和 HTML 4 相比，HTML 5 有哪些最基本的变化吧。&lt;/p&gt;&lt;h2 id=&quot;doctype&quot;&gt;doctype&lt;/h2&gt;&lt;p&gt;HTML 5 的 doctype 是这样的：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;非常简洁，同时所有浏览器（包括 IE6）都支持。&lt;/p&gt;&lt;h2 id=&quot;section&quot;&gt;根元素&lt;/h2&gt;&lt;p&gt;HTML 5 的根元素也有所简化。当然你可以只写一个&lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt;；不过如果愿意指明语言，这样写即可：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;html&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;lang=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;zh-CN&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;section-1&quot;&gt;字符编码&lt;/h2&gt;&lt;p&gt;来回顾一下 HTML 4 时代的写法：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;meta&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;http-equiv=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;Content-Type&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;text/html; charset=utf-8&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;忘掉它吧。HTML 5 的标准写法是这样的：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;meta&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;charset=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;utf-8&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;css-&quot;&gt;外部 CSS 文件&lt;/h2&gt;&lt;p&gt;HTML 5 中，可以省略&lt;code&gt;type=&quot;text/css&quot;&lt;/code&gt;部分：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;link&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;rel=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;stylesheet&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;style.css&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;section-2&quot;&gt;新增元素&lt;/h2&gt;&lt;p&gt;HTML 5 新增了几个语义化的标签，分别为：&lt;code&gt;section&lt;/code&gt;, &lt;code&gt;nav&lt;/code&gt;, &lt;code&gt;article&lt;/code&gt;, &lt;code&gt;aside&lt;/code&gt;,&lt;code&gt;time&lt;/code&gt;, &lt;code&gt;header&lt;/code&gt;, &lt;code&gt;footer&lt;/code&gt;等。&lt;/p&gt;&lt;h3 id=&quot;section-3&quot;&gt;例子&lt;/h3&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 页面头部 --&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;header&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;My Site&lt;span class=&quot;nt&quot;&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 导航栏 --&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;nav&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;li&amp;gt;&lt;/span&gt; ... &lt;span class=&quot;nt&quot;&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;li&amp;gt;&lt;/span&gt; ... &lt;span class=&quot;nt&quot;&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 单篇文章 --&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;article&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;header&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;time&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;datetime=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;2014-12-10&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;pubdate&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Dec 10, 14&amp;#39;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/time&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Hello, World!&lt;span class=&quot;nt&quot;&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Lorem ipsum ...&lt;span class=&quot;nt&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/article&amp;gt;&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 页脚 --&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;footer&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;p&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;copyright&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Copyright &lt;span class=&quot;ni&quot;&gt;&amp;amp;copy;&lt;/span&gt; 2013&lt;span class=&quot;nt&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/footer&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&quot;section-4&quot;&gt;几点说明&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;nav&lt;/code&gt;元素一般包括一列导航链接，但是并非所有的「一列链接」都应该放在这个标签中。&lt;/li&gt;&lt;li&gt;&lt;code&gt;section&lt;/code&gt;元素一般用途是包含一个有共同主题的部分，比如多个 Tab 框的每个页面、网站的主页里的个人介绍、联系方式等分区，或者一篇文章的不同章节，等等；&lt;code&gt;section&lt;/code&gt;内部应该有标题。通用容器不应该使用&lt;code&gt;section&lt;/code&gt;，而应该继续使用&lt;code&gt;div&lt;/code&gt;。&lt;/li&gt;&lt;li&gt;如果是相对独立的文章、帖子、留言等，则可使用&lt;code&gt;article&lt;/code&gt;元素。内部可以包含&lt;code&gt;time&lt;/code&gt;标签指明发表时间。&lt;/li&gt;&lt;li&gt;&lt;code&gt;aside&lt;/code&gt;元素用来包含一些和主题相关（单并非不可或缺的）内容，典型的使用场景是网页的「侧边栏」。&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&quot;section-5&quot;&gt;浏览器兼容性&lt;/h3&gt;&lt;p&gt;主流浏览器都兼容 HTML 5 的新标签。对于 IE8 及以下版本，它不认识 HTML 5 的新元素，会把它们默认渲染为&lt;code&gt;display:inline&lt;/code&gt;并且拒绝为它们添加任何样式。同时，渲染未知元素时，会把它们作为一个「没有后代的空元素」插入到 DOM 中。&lt;/p&gt;&lt;p&gt;解决方法很简单，使用 JavaScript 创建一个「没用的」元素即可，例如：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;script&amp;gt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;article&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后，我们就可以把他们的样式设置为&lt;code&gt;display:block&lt;/code&gt;并正常使用了。有一个项目叫做 &lt;a href=&quot;https://code.google.com/p/html5shiv/‎&quot;&gt;HTML5 Shiv&lt;/a&gt;，可以帮助我们完整解决这个兼容性问题，推荐使用。&lt;/p&gt;</description><pubDate>Mon, 10 Feb 2014 00:00:00 +0800</pubDate><link>https://www.renfei.org/blog/html5-introduction-1.html</link><guid isPermaLink="true">https://www.renfei.org/blog/html5-introduction-1.html</guid></item><item><title>CSS 伪类和伪元素的区别</title><description>&lt;p&gt;伪类（pseudo class）和伪元素（pseudo element）这两个概念一直被混淆。&lt;/p&gt;&lt;p&gt;简而言之：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;伪类，会对现有的元素进行筛选。&lt;/li&gt;&lt;li&gt;伪元素，会创造出不存在的新元素。&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;我们来看看标准是怎么说的：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;伪类&lt;/li&gt;&lt;/ul&gt;&lt;blockquote&gt;&lt;p&gt;The pseudo-class concept is introduced to permit selection based on information that lies outside of the document tree or that cannot be expressed using the other simple selectors.&lt;/p&gt;&lt;/blockquote&gt;&lt;ul&gt;&lt;li&gt;伪元素&lt;/li&gt;&lt;/ul&gt;&lt;blockquote&gt;&lt;p&gt;Pseudo-elements create abstractions about the document tree beyond those specified by the document language. For instance, document languages do not offer mechanisms to access the first letter or first line of an element’s content. Pseudo-elements allow authors to refer to this otherwise inaccessible information. Pseudo-elements may also provide authors a way to refer to content that does not exist in the source document.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;「伪类」的作用为「permit selection」，即允许&lt;strong&gt;选择&lt;/strong&gt;一些无法用其他选择器选取的元素，必须对应某个&lt;strong&gt;现有的&lt;/strong&gt; HTML 元素。而「伪元素」则「create abstractions about the document tree beyond those specified by the document language」，意思是它并不依赖于 HTML 树的结构，即可以&lt;strong&gt;创造&lt;/strong&gt;新的元素。&lt;/p&gt;&lt;p&gt;例如，&lt;code&gt;:first-child&lt;/code&gt;是一个伪类，对应作为第一个子元素的元素；&lt;code&gt;:visited&lt;/code&gt;作为一个伪类，对应所有已经被访问过的&lt;code&gt;a&lt;/code&gt;元素。&lt;/p&gt;&lt;p&gt;而&lt;code&gt;::before&lt;/code&gt;作为一个伪元素，对应某元素之前一个（实际不存在的）元素。同时&lt;code&gt;::selection&lt;/code&gt;作为一个伪元素，它并不代表被选择的元素，而代表被选择的内容（可能是一个元素的一部分）。其他伪元素的例子还有&lt;code&gt;::first-line&lt;/code&gt;，对应第一行，等等。&lt;/p&gt;&lt;h3 id=&quot;section&quot;&gt;表示方法&lt;/h3&gt;&lt;p&gt;简而言之：伪类总是以一个冒号开头。伪元素通常以两个冒号开头。&lt;/p&gt;&lt;p&gt;在 CSS2 时代，伪元素和伪类均是以一个冒号开头的；在 CSS2.1 之后，为了对伪元素和伪类加以区分，规定伪类继续以一个冒号开头，而伪元素改为以两个冒号开头。但是为了向前兼容，浏览器同样接受 CSS2 时代&lt;strong&gt;已经存在&lt;/strong&gt;的伪元素（它们包括&lt;code&gt;:before&lt;/code&gt;, &lt;code&gt;:after&lt;/code&gt;, &lt;code&gt;:first-line&lt;/code&gt;, &lt;code&gt;:first-letter&lt;/code&gt;）的单冒号写法。但是对于 CSS2 之后所有新增的伪元素（例如&lt;code&gt;::selection&lt;/code&gt;），必须采用双冒号写法。&lt;/p&gt;&lt;h3 id=&quot;section-1&quot;&gt;浏览器兼容性&lt;/h3&gt;&lt;p&gt;一些老旧的浏览器不支持双冒号的写法，因此如果必须兼容旧浏览器，则应该使用单冒号写法。IE 从 9 开始支持双冒号写法。&lt;/p&gt;</description><pubDate>Mon, 10 Feb 2014 00:00:00 +0800</pubDate><link>https://www.renfei.org/blog/css-pseudo-class-and-pseudo-element.html</link><guid isPermaLink="true">https://www.renfei.org/blog/css-pseudo-class-and-pseudo-element.html</guid></item><item><title>几种使网页中的邮件地址更安全的方法</title><description>&lt;p&gt;现在 spammer 们很猖狂，因此直接把邮件地址写在网页中似乎不是一个好主意。如何公布你的邮件地址才能既安全又有效？下面分享几种方法，使得邮件地址更难被别有用心的人收集到。&lt;/p&gt;&lt;h3 id=&quot;at--dot&quot;&gt;使用 AT 和 DOT&lt;/h3&gt;&lt;p&gt;这应该是最古老，也最方便的办法了：&lt;/p&gt;&lt;pre&gt;&lt;code&gt;Contact: john.appleseed &amp;lt;AT&amp;gt; gmail &amp;lt;DOT&amp;gt; com&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;但是它往往还比较有效，因为这种方法形式复杂多变，spammer 们往往难以检测出来。只是…个人认为比较难看。&lt;/p&gt;&lt;h3 id=&quot;section&quot;&gt;使用图片代替&lt;/h3&gt;&lt;p&gt;这种方式不必多说，有效，但是缺点也很明显：增加了数据载入量、不易复制，有时不美观，等等。 有很多做这种图的网站。如果你用 Gmail，则可以选择&lt;a href=&quot;http://services.nexodyne.com/email/index.php&quot; title=&quot;Email Icon Generator&quot;&gt;Email Icon Generator&lt;/a&gt;，因为它有一些预定义的模版，生成的图比较好看。例如：&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;//img.renfei.org/2015/09/gmail-esp.png&quot; alt=&quot;Gmail&quot; /&gt;&lt;/p&gt;&lt;p&gt;当然，它也支持自定义：&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;//img.renfei.org/2015/09/custom-esp.png&quot; alt=&quot;Custom&quot; /&gt;&lt;/p&gt;&lt;h2 id=&quot;javascript&quot;&gt;使用 JavaScript&lt;/h2&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Contact: &lt;span class=&quot;nt&quot;&gt;&amp;lt;span&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;hidden-email&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;script&amp;gt;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;john.appleseed&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fromCharCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;gm&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;ail.com&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;hidden-email&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;innerHTML&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这种方式代码中根本不会出现&lt;code&gt;@&lt;/code&gt;字符，而且也可以直接复制，并且只要启用了 JavaScript 的浏览器都可以正常显示。只不过对于同样启用 JavaScript 的爬虫就无效了。&lt;a href=&quot;http://jsfiddle.net/Ukq5e/&quot; title=&quot;Demo 1&quot;&gt;查看 Demo&lt;/a&gt;&lt;/p&gt;&lt;h2 id=&quot;css&quot;&gt;使用 CSS&lt;/h2&gt;&lt;h3 id=&quot;displaynone&quot;&gt;使用 display:none&lt;/h3&gt;&lt;p&gt;这个方法即在地址中插入一些隐藏的文本，存在于 DOM 中，但是人眼看不到。例如：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Contact: john.apple&lt;span class=&quot;nt&quot;&gt;&amp;lt;span&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;hidden&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;stop spam&lt;span class=&quot;nt&quot;&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;seed@gmail.com&lt;span class=&quot;nt&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;style&amp;gt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.hidden&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;none&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&quot;css-&quot;&gt;使用 CSS 反转阅读顺序&lt;/h3&gt;&lt;p&gt;即，用从右向左的方式书写你的 Email 地址，然后再用 CSS 把它反过来。这样，在代码中实际出现的 Email 地址就是相反的。如下：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;p&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;reversed&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;moc.liamg@deeselppa.nhoj&lt;span class=&quot;nt&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;style&amp;gt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.reversed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;unicode-bidi&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;bidi-override&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;direction&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rtl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;但是这样做有一个明显缺点，就是…用户复制出来的内容也是反着的。&lt;/p&gt;&lt;p&gt;备注，通过专用的 HTML 标签也可以做到这个效果，不用自己写 CSS 代码。方法：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;bdo&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;dir=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;rtl&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;moc.liamg@deeselppa.nhoj&lt;span class=&quot;nt&quot;&gt;&amp;lt;/bdo&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&quot;css--before--after&quot;&gt;使用 CSS 伪元素 :before 和 :after&lt;/h3&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Contact: &lt;span class=&quot;nt&quot;&gt;&amp;lt;span&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;hidden-email&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;john.appleseed&lt;span class=&quot;nt&quot;&gt;&amp;lt;/span&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;style&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;#hidden-email&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:after&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;@gmail.com&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;使用这种方式，在 DOM 中根本不会出现完整的邮件地址，看到的仅仅是一种「视觉效果」。但是缺点是，用户无法复制隐藏起来的部分。&lt;a href=&quot;http://jsfiddle.net/bQrPz/&quot; title=&quot;Demo 2&quot;&gt;查看 Demo&lt;/a&gt;。&lt;/p&gt;&lt;p&gt;注意：如果使用 CSS3 双冒号的写法&lt;code&gt;::before&lt;/code&gt;和&lt;code&gt;::after&lt;/code&gt;，则 IE 8 及以下的浏览器是不支持的。&lt;/p&gt;&lt;h2 id=&quot;section-1&quot;&gt;其他方法&lt;/h2&gt;&lt;h3 id=&quot;google-recaptcha-mailhide&quot;&gt;使用 Google reCAPTCHA Mailhide&lt;/h3&gt;&lt;p&gt;这是 Google 提供的一项免费服务，用户需要输入一个验证码才能查看邮件地址，因此除了麻烦一些，是非常有效的。&lt;a href=&quot;http://www.google.com/recaptcha/mailhide/&quot; title=&quot;recaptcha mailhide&quot;&gt;点击前往&lt;/a&gt;。&lt;/p&gt;&lt;h3 id=&quot;section-2&quot;&gt;使用一个「联系我」表单&lt;/h3&gt;&lt;p&gt;如果支持动态页面，则可以使用一个联系表单，用户提交表单之后通过服务器端语言来发送邮件到你的邮箱。使用这种方法绝对不会在网页中暴露邮件地址，并且还可以添加验证码等机制进行验证，甚至提供上传附件等功能。&lt;/p&gt;</description><pubDate>Sun, 09 Feb 2014 00:00:00 +0800</pubDate><link>https://www.renfei.org/blog/ways-to-implement-email-obfuscation-to-prevent-spam.html</link><guid isPermaLink="true">https://www.renfei.org/blog/ways-to-implement-email-obfuscation-to-prevent-spam.html</guid></item><item><title>网站优化：浏览器缓存控制简介及配置策略</title><description>&lt;p&gt;每次访问网页，通常浏览器会从服务器下载所需的资源，例如 HTML 文档、图片、CSS、JavaScript，甚至包括字体文件等。这里面的许多文件（例如图片）都是很少变动的，如果每次都要从服务器重新下载，会不必要地增加网页载入时间，同时也会对服务器造成一定压力。通过合理配置缓存策略，可令浏览器以某种方式把这些静态的文件缓存起来，下次请求同一资源时，直接使用本地存储的副本，而不是从服务器重新下载。&lt;/p&gt;&lt;p&gt;启用缓存至少有两点显而易见的好处：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;减少页面加载时间&lt;/li&gt;&lt;li&gt;减少服务器负载&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;浏览器是否使用缓存、缓存多久，是由服务器控制的。准确来说，当浏览器请求一个网页（或者其他资源）时，服务器发回的响应的「响应头」部分的某些字段指明了有关缓存的关键信息。&lt;/p&gt;&lt;h4 id=&quot;cache-control&quot;&gt;Cache-Control&lt;/h4&gt;&lt;p&gt;&lt;code&gt;Cache-Control&lt;/code&gt;HTTP 响应头是 HTTP 1.1 协议新增的指令，每个资源都可以通过设定 Cache-Control 来建立缓存策略。通常，可为它指定一个&lt;code&gt;max-age&lt;/code&gt;，表示缓存的最长时间，单位为秒。例如，若设定&lt;code&gt;Cache-Control: max-age=604800&lt;/code&gt;，则表示这个资源的有效时间为 7 天。浏览器第一次获取这个资源后，7 天之内若再次请求，通常都不会与服务器进行任何通信，而是直接使用本地副本。&lt;/p&gt;&lt;p&gt;此外，还可以为 Cache-Control 指定&lt;code&gt;public&lt;/code&gt;或&lt;code&gt;private&lt;/code&gt;标记。如果使用 private，则表示该资源仅仅属于发出请求的最终用户，这将禁止中间服务器（如代理服务器）缓存此类资源。对于包含用户个人信息的文件（如一个包含用户名的 HTML 文档），可以设置 private，一方面由于这些缓存对其他用户来说没有任何意义，另一方面用户可能不希望相关文件储存在不受信任的服务器上。需要指出的是，private 并不会使得缓存更加安全，它同样会传给中间服务器（如果网站对于传输的安全性要求很高，应该使用传输层安全措施）。对于 public，则允许所有服务器缓存该资源。通常情况下，对于所有人都可以访问的资源（例如网站的 logo、图片、脚本等），Cache-Control 设为 public 是合理的。&lt;/p&gt;&lt;h4 id=&quot;expires&quot;&gt;Expires&lt;/h4&gt;&lt;p&gt;同样是用来控制缓存，&lt;code&gt;Expires&lt;/code&gt;响应头从另一个角度——指明缓存的具体过期日期，来控制资源何时过期。在过期时间以内，若再次发起请求，通常浏览器都不会与服务器进行任何通信，而是直接使用本地副本。Apache 服务器允许以多种方式，例如基于该资源的访问时间或上次修改时间来设定 Expires 的值。注意，这里的时间一律使用格林威治时间（Greenwich Mean Time, GMT），而非本地时间。&lt;/p&gt;&lt;p&gt;当 Expires 和 Cache-Control 同时出现时，通常后者会覆盖前者的设定。由于 Expires 对用户的系统时间有所依赖，因此通常认为使用 Cache-Control 是更好的选择（基本上所有的浏览器都支持 Cache-Control 指令）。&lt;/p&gt;&lt;h4 id=&quot;last-modified--etag&quot;&gt;Last-Modified 和 ETag&lt;/h4&gt;&lt;p&gt;服务器可在 HTTP 返回头中包含&lt;code&gt;Last-Modified&lt;/code&gt;字段或者&lt;code&gt;ETag&lt;/code&gt;字段。Last-Modified 表示被请求资源在服务器端的上次修改时间，而 ETag 则是一个唯一文件标识符，每次文件修改后都会生成一个新的 ETag。服务器通过向浏览器发送这两个字段，来告知浏览器其获得的资源的版本。&lt;/p&gt;&lt;p&gt;无论通过 Cache-Control 还是 Expires 设置缓存，在过期时间以内，当用户点击浏览器刷新按钮时，为了&lt;strong&gt;确保&lt;/strong&gt;用户所加载的资源是最新的，大部分浏览器不会再直接使用缓存中的数据，而是发出一个条件请求（Conditional GET Request）。对于这类请求，浏览器会在请求头中包含&lt;code&gt;If-Modified-Since&lt;/code&gt;或&lt;code&gt;If-None-Match&lt;/code&gt;字段。前者即浏览器当初得到的 Last-Modified；后者即浏览器当初得到的 ETag。当服务器发现资源的更新时间晚于 If-Modified-Since 所提供的时间，或者资源在服务器端当前的 ETag 和 If-None-Match 提供的不符时，会响应整个资源，否则只会响应一个 304 Not Modified 状态码（因此浏览器将不需要重新下载整个资源）。这种机制可以最大程度上减少数据下载量。此外，如果缓存的资源已过期，浏览器通常有两种选择：重新下载这个资源，或发出一个条件请求。很多浏览器都会采取后者，以节约资源。&lt;/p&gt;&lt;p&gt;由于 Last-Modified 和 ETag 的作用是相同的（均为向服务器验证资源是否最新），因此只使用一个即可。通常认为 Last-Modified 更好（它和 Expires 不同，由服务器生成，不依赖浏览器端时间）。&lt;/p&gt;&lt;h3 id=&quot;section&quot;&gt;我的网站启用缓存了吗？&lt;/h3&gt;&lt;h4 id=&quot;section-1&quot;&gt;用浏览器的开发者工具或插件查看&lt;/h4&gt;&lt;p&gt;为了确定是否启用了缓存，只需要检查服务器发回的「响应头」就可以。许多浏览器以及工具都可以检查这些信息，我们以 Firefox 的插件 Firebug 为例。如图所示：&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;//img.renfei.org/2014/02/cache-optimization-1.png&quot; alt=&quot;cache-optimization-1&quot; /&gt;&lt;/p&gt;&lt;p&gt;下面再来看一个没有启用缓存的资源的例子：&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;//img.renfei.org/2014/02/cache-optimization-2.png&quot; alt=&quot;cache-optimization-2&quot; /&gt;&lt;/p&gt;&lt;p&gt;没有包含&lt;code&gt;Cache-Control&lt;/code&gt;以及&lt;code&gt;Expires&lt;/code&gt;信息。&lt;/p&gt;&lt;h4 id=&quot;section-2&quot;&gt;在线检测&lt;/h4&gt;&lt;p&gt;也有一些方便的在线检测服务，用于对网站速度给出建议，其中就会检测缓存设置情况。比如 Yahoo! 公司的 YSlow，以及&lt;a href=&quot;http://zhanzhang.baidu.com/optimization&quot; title=&quot;百度页面优化建议&quot;&gt;百度站长工具&lt;/a&gt;等，都有相应的功能。大家可以去百度那里检测一下，目前是不需要登录即可检测的。&lt;/p&gt;&lt;h2 id=&quot;section-3&quot;&gt;使用缓存的策略&lt;/h2&gt;&lt;h4 id=&quot;section-4&quot;&gt;为静态资源设置长缓存时间&lt;/h4&gt;&lt;p&gt;有些资源是很长时间不会改变的，比如网站的 logo 图片、jQuery 库、字体等，因此可以为它们设定「永不过期」的缓存时间，例如设定为 10 年。&lt;/p&gt;&lt;h4 id=&quot;section-5&quot;&gt;确保文件修改生效&lt;/h4&gt;&lt;p&gt;有些时候我们会修改一些资源，比如更新了 jQuery 版本，或网站的 CSS 样式。如果这些资源已经被缓存，那么除非用户手工刷新页面，否则要等缓存自然过期之后用户才会获得新版本。如何在这种情况下强制浏览器重新下载呢？最有效的一个办法就是在这类资源的文件名中包含版本信息，并在更改之后对应地修改文件名。浏览器发现文件更换后，自然无法使用缓存，而会重新下载。&lt;/p&gt;&lt;h4 id=&quot;html-&quot;&gt;对于 HTML 文档谨慎设定过期时间&lt;/h4&gt;&lt;p&gt;大部分情况下，对于其他图片、CSS、JavaScript 等资源的请求都来自一个单一的 HTML 文档。对于这类页面通常应该设定比较短的过期时间，或者干脆不设定。因为如果这类页面被缓存，那么页面中包含的资源的文件名等等信息都会一并被缓存，导致对它的更新难以确保立即对用户生效。&lt;/p&gt;&lt;h4 id=&quot;query-string&quot;&gt;引用静态资源时，不要使用 Query String&lt;/h4&gt;&lt;p&gt;Query String 就是例如&lt;code&gt;?key=val&lt;/code&gt;的字符串，如&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;script &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;/static/js/func.js?v=a87ff8&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这会阻止一部分较老的浏览器（包括 IE6 ）对该资源进行缓存。&lt;/p&gt;&lt;h2 id=&quot;section-6&quot;&gt;设定缓存的方法&lt;/h2&gt;&lt;p&gt;对于 Apache 服务器，可以通过 &lt;a href=&quot;http://httpd.apache.org/docs/2.0/mod/mod_expires.html&quot;&gt;mod_expires&lt;/a&gt; 模块来设定&lt;code&gt;Expires&lt;/code&gt;HTTP 头或&lt;code&gt;Cache-Control&lt;/code&gt;HTTP 头的&lt;code&gt;max-age&lt;/code&gt;指令。编辑相应目录下的 &lt;code&gt;.htaccess&lt;/code&gt; 文件，或直接对 Apache 的配置文件（根据服务器系统版本不同，可能为&lt;code&gt;httpd.conf&lt;/code&gt;或&lt;code&gt;apache2.conf&lt;/code&gt;等）作出修改。&lt;/p&gt;&lt;h4 id=&quot;section-7&quot;&gt;分文件类别设定&lt;/h4&gt;&lt;p&gt;使用&lt;code&gt;ExpiresByType&lt;/code&gt;可以按照文件的 MIME Type 设定某一类文件的过期日期。例如：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-apache&quot; data-lang=&quot;apache&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;IfModule&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mod_expires.c&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;ExpiresActive&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;On&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;ExpiresByType&lt;/span&gt; text/css&lt;span class=&quot;s2&quot;&gt;&amp;quot;access plus 1 week&amp;quot;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;ExpiresByType&lt;/span&gt; application/javascript&lt;span class=&quot;s2&quot;&gt;&amp;quot;access plus 2 weeks&amp;quot;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;ExpiresByType&lt;/span&gt; image/x-icon&lt;span class=&quot;s2&quot;&gt;&amp;quot;access plus 6 months&amp;quot;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;ExpiresByType&lt;/span&gt; image/gif&lt;span class=&quot;s2&quot;&gt;&amp;quot;access plus 6 months&amp;quot;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;ExpiresByType&lt;/span&gt; image/png&lt;span class=&quot;s2&quot;&gt;&amp;quot;access plus 6 months&amp;quot;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;ExpiresByType&lt;/span&gt; image/jpeg&lt;span class=&quot;s2&quot;&gt;&amp;quot;access plus 6 months&amp;quot;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;ExpiresByType&lt;/span&gt; video/x-flv&lt;span class=&quot;s2&quot;&gt;&amp;quot;access plus 6 months&amp;quot;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;ExpiresByType&lt;/span&gt; application/pdf&lt;span class=&quot;s2&quot;&gt;&amp;quot;access plus 6 months&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/IfModule&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其中&lt;code&gt;access plus 1 week&lt;/code&gt;表示将缓存过期设置为访问时间（即当前时间）之后的一周。如果将&lt;code&gt;access&lt;/code&gt;替换为&lt;code&gt;modification&lt;/code&gt;，则缓存过期会被设定为文件修改时间之后的一周。可以使用的时间单位包括：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;years&lt;/li&gt;&lt;li&gt;months&lt;/li&gt;&lt;li&gt;weeks&lt;/li&gt;&lt;li&gt;days&lt;/li&gt;&lt;li&gt;hours&lt;/li&gt;&lt;li&gt;minutes&lt;/li&gt;&lt;li&gt;seconds&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;不同的时间也可以进行组合，例如：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-apache&quot; data-lang=&quot;apache&quot;&gt;&lt;span class=&quot;nb&quot;&gt;ExpiresByType&lt;/span&gt; text/html &lt;span class=&quot;s2&quot;&gt;&amp;quot;access plus 1 month 15 days 2 hours&amp;quot;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;ExpiresByType&lt;/span&gt; image/gif &lt;span class=&quot;s2&quot;&gt;&amp;quot;modification plus 5 hours 3 minutes&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&quot;section-8&quot;&gt;根据文件扩展名进行设置&lt;/h4&gt;&lt;p&gt;如果希望根据扩展名来指定缓存规则，可以使用&lt;code&gt;FilesMatch&lt;/code&gt;配合正则表达式。为了简洁，我这里只规定了&lt;code&gt;ExpiresDefault&lt;/code&gt;。它的优先级很低，只会在对应文件没有任何其他规则能够匹配（包括上层目录下的缓存规则）时生效。&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-apache&quot; data-lang=&quot;apache&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;IfModule&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mod_expires.c&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;FilesMatch&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;\.(css|js)$&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;ExpiresActive&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;ExpiresDefault&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;access plus 1 week&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/FilesMatch&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/IfModule&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&quot;section-9&quot;&gt;对某些文件设定&lt;/h4&gt;&lt;p&gt;同理，也可以对某些文件启用特定的缓存策略。注意，文件名中的点(&lt;code&gt;.&lt;/code&gt;)是需要转义的。&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-apache&quot; data-lang=&quot;apache&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;IfModule&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mod_expires.c&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;FilesMatch&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;^(example\.css|example\.js)$&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;ExpiresActive&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;ExpiresDefault&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;access plus 1 week&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/FilesMatch&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/IfModule&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&quot;section-10&quot;&gt;对某一文件夹下的所有文件设定&lt;/h4&gt;&lt;p&gt;对于静态文件，一个比较方便的做法是将它们全部放到一个目录下，并对该目录下的所有文件设定。但是，此处需要注意防止其他规则将&lt;code&gt;ExpiresDefault&lt;/code&gt;覆盖掉。&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-apache&quot; data-lang=&quot;apache&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;IfModule&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mod_expires.c&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;ExpiresActive&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;On&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;ExpiresDefault&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;access plus 10 years&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/IfModule&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;section-11&quot;&gt;有用的工具及参考资料&lt;/h2&gt;&lt;ol&gt;&lt;li&gt;&lt;a href=&quot;http://highloadtools.com/cachecontrol&quot;&gt;Cache-Control header checker&lt;/a&gt;（可检测给定的地址是否启用了 Cache-Control，还会教你如何设定）&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.mnot.net/cache_docs/#IMP-SERVER&quot;&gt;Caching Tutorial for Web Authors and Webmasters&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching?hl=zh-cn&quot;&gt;HTTP 缓存 - Web Fundamentals&lt;/a&gt;（来自 Google Developers）&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://zhanzhang.baidu.com/optimization&quot;&gt;百度站长工具-页面优化建议&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/h5bp/server-configs&quot;&gt;h5bp/server-configs&lt;/a&gt;（HTML5 Boilerplate 提供了所有最流行的服务器的配置文件样例）&lt;/li&gt;&lt;/ol&gt;</description><pubDate>Sun, 02 Feb 2014 00:00:00 +0800</pubDate><link>https://www.renfei.org/blog/http-caching.html</link><guid isPermaLink="true">https://www.renfei.org/blog/http-caching.html</guid></item><item><title>Git 快速开始简明教程</title><description>&lt;p&gt;Git 是一个分布式版本管理系统，即每个终端都有一套完整的项目代码（包括历史文件）。为了使用 Git，先要安装 Git。可以在 Command Line 或者 Terminal 中输入&lt;code&gt;git&lt;/code&gt;命令检查是否已经安装。&lt;/p&gt;&lt;h2 id=&quot;section&quot;&gt;开始使用&lt;/h2&gt;&lt;h3 id=&quot;section-1&quot;&gt;创建本地工作目录&lt;/h3&gt;&lt;p&gt;先要创建一个文件夹来保存项目代码。例如我使用如下目录：&lt;/p&gt;&lt;pre&gt;&lt;code&gt;/Users/renfei/Sites/project&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;命令行中&lt;code&gt;cd&lt;/code&gt;到这个目录，然后执行&lt;code&gt;git init&lt;/code&gt; 命令来初始化：&lt;/p&gt;&lt;pre&gt;&lt;code&gt;bogon:~ renfei$ cd /Users/renfei/Sites/projectbogon:project renfei$ git initInitialized empty Git repository in /Users/renfei/Sites/project/.git/&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;section-2&quot;&gt;添加远程仓库&lt;/h3&gt;&lt;p&gt;使用&lt;code&gt;git remote add&lt;/code&gt;命令：&lt;/p&gt;&lt;pre&gt;&lt;code&gt;bogon:buaa-weixin renfei$ git remote add proj https://renfeisong@bitbucket.org/developers/proj.git&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;上面的 proj 是一个本地别名，可以起得短一些，以便以后使用。后面的地址则是远程仓库得地址。添加之后，可以使用&lt;code&gt;git remote&lt;/code&gt;命令查看当前已有的远程仓库。&lt;/p&gt;&lt;h3 id=&quot;section-3&quot;&gt;从远程仓库得到数据&lt;/h3&gt;&lt;p&gt;使用 &lt;code&gt;git fetch&lt;/code&gt; 命令。&lt;/p&gt;&lt;pre&gt;&lt;code&gt;bogon:project renfei$ git fetch projremote: Counting objects: 39, done.remote: Compressing objects: 100% (34/34), done.remote: Total 39 (delta 5), reused 0 (delta 0)Unpacking objects: 100% (39/39), done.From https://bitbucket.org/renfeisong/project* [new branch]develop-&amp;gt; proj/develop* [new branch]master-&amp;gt; proj/master&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;会看到得到了服务器上的文件，以及两个分支，develop 和 master。这里得到得分支数目取决于远程仓库的内容。虽然得到了这些东西，但是现在这些文件还没有出现在你的工作目录中。&lt;/p&gt;&lt;p&gt;这是因为，我们需要利用&lt;code&gt;git checkout&lt;/code&gt;命令「检出」（实际就是把工作目录切换到某个分支），才可以看到对应分支下的那些文件。&lt;/p&gt;&lt;h3 id=&quot;section-4&quot;&gt;切换分支&lt;/h3&gt;&lt;p&gt;使用 &lt;code&gt;checkout&lt;/code&gt; 命令。&lt;/p&gt;&lt;pre&gt;&lt;code&gt;bogon:project renfei$ git checkout developBranch develop set up to track remote branch develop from proj.Switched to a new branch &#39;develop&#39;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;例子中我们切换到了 develop 分支，其中对应的文件就都出现了（如果有的话）。&lt;/p&gt;&lt;h2 id=&quot;section-5&quot;&gt;再说分支&lt;/h2&gt;&lt;p&gt;分支是 Git 的一个很重要的功能，因此有必要解释一下。如果把随着时间的推移，代码的变化想象成前进的路线，那么分支就是从路线上的某一点衍生出来的一条支线，在这条支线上的开发不会对原先路线的代码造成任何影响。例如，现在我们有一个 master 分支和一个 develop 分支，master 分支发布稳定版本的应用，而 develop 分支则进行日常开发。一旦达到某个进度节点，develop 分支上的代码足够稳定可以发布，就把 develop 分支合并（merge）到 master 分支，同时继续 develop 分支的开发。&lt;/p&gt;&lt;p&gt;再如，develop 分支开发的过程中出现了某个 bug，一个开发人员想解决这个 bug，那么为了不影响其他人的工作，他可以从 develop 分支再创建一个新的分支 hotfix，然后在这个分支下修改，完成后把它合并回 develop 分支，并删除 hotfix 分支（它的历史使命已经完成）。&lt;/p&gt;&lt;p&gt;可以看出，每个分支都有一套代码。每个开发人员本地都保存了全部分支的全部代码（这些代码连同其他信息被 git 有序地组织在了工作目录下 .git 目录中，这个目录应当是隐藏的），当通过&lt;code&gt;checkout branch_name&lt;/code&gt;切换分支时，工作目录下可见的文件也会被切换成当前分支下的文件。而其他分支的文件也并没有丢失，所以非常方便，你可以根据需要切换分支进行不同的工作。&lt;/p&gt;&lt;h2 id=&quot;section-6&quot;&gt;文件操作&lt;/h2&gt;&lt;h3 id=&quot;section-7&quot;&gt;添加新文件&lt;/h3&gt;&lt;p&gt;在工作目录中创建的新文件处于 untracked 状态，即没有被 git 跟踪。新文件不会自动被 git 跟踪，因为许多临时文件、日志文件不需要保存到仓库中。如果要往仓库中添加文件，使用 &lt;code&gt;git add&lt;/code&gt; 命令。&lt;/p&gt;&lt;pre&gt;&lt;code&gt;bogon:project renfei$ git add *&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;上述做法添加所有文件。如果包含文件夹，则会递归地添加里面的文件。也可以添加单独的文件或者在文件名中使用通配符。&lt;/p&gt;&lt;p&gt;要查看当前状态（哪些文件 untracked，哪些文件处于其他状态等）可以使用 &lt;code&gt;git status&lt;/code&gt; 命令。&lt;/p&gt;&lt;p&gt;执行 &lt;code&gt;git add&lt;/code&gt; 后，新添加的文件状态（可通过&lt;code&gt;git status&lt;/code&gt;查看）变为 change to be committed，或者称为 staged，暂存。意思就是，只要把它 commit 上去就可以了。&lt;/p&gt;&lt;h3 id=&quot;section-8&quot;&gt;修改文件&lt;/h3&gt;&lt;p&gt;如果修改了某个已跟踪的文件，那么这个文件会变为 modified，not staged，未暂存。此时必须再次 &lt;code&gt;git add&lt;/code&gt; 这个文件使它变成 staged 状态，才能够被 commit 上去。&lt;/p&gt;&lt;h3 id=&quot;section-9&quot;&gt;提交&lt;/h3&gt;&lt;p&gt;当有文件状态为暂存的（staged）时，就可以执行 &lt;code&gt;git commit&lt;/code&gt; 命令。这个命令会提交暂存的文件。git 要求为每次提交添加一个说明，因此一般使用一个&lt;code&gt;-m&lt;/code&gt;选项来添加说明。例如：&lt;/p&gt;&lt;pre&gt;&lt;code&gt;bogon:project renfei$ git commit -m &quot;background admin site (backend) added&quot;[develop 7d17136] background admin site (backend) added16 files changed, 10372 insertions(+)create mode 100644 bootstrap/v2/css/bootstrap-responsive.csscreate mode 100644 bootstrap/v2/css/bootstrap-responsive.min.csscreate mode 100644 bootstrap/v2/css/bootstrap.csscreate mode 100644 bootstrap/v2/css/bootstrap.min.csscreate mode 100644 bootstrap/v2/img/glyphicons-halflings-white.pngcreate mode 100644 bootstrap/v2/img/glyphicons-halflings.pngcreate mode 100644 bootstrap/v2/js/bootstrap.jscreate mode 100644 bootstrap/v2/js/bootstrap.min.jscreate mode 100644 define.phpcreate mode 100644 index.phpcreate mode 100644 jquery/jquery-1.10.2.min.jscreate mode 100644 login.phpcreate mode 100644 logout.phpcreate mode 100644 pref.phpcreate mode 100644 signup.phpcreate mode 100644 util.php&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这个操作完成后，代码就被提交到了本地仓库。每次进行 commit 后都会对整个项目做一个快照，以后可以随时回滚到某个快照中。&lt;/p&gt;&lt;p&gt;如果希望省略 add 过程而直接提交所有跟踪的文件，可以给 &lt;code&gt;commit&lt;/code&gt; 命令加上&lt;code&gt;-a&lt;/code&gt;选项。如下：&lt;/p&gt;&lt;pre&gt;&lt;code&gt;bogon:project renfei$ git commit -a -m &quot;background admin site (backend) added&quot;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;section-10&quot;&gt;推送到远程仓库&lt;/h3&gt;&lt;p&gt;&lt;code&gt;commit&lt;/code&gt;仅仅把更改提交到了本地仓库。如果想推送到远程仓库，需要使用 &lt;code&gt;git push&lt;/code&gt; 命令。&lt;/p&gt;&lt;pre&gt;&lt;code&gt;bogon:project renfei$ git push proj developCounting objects: 25, done.Delta compression using up to 8 threads.Compressing objects: 100% (22/22), done.Writing objects: 100% (24/24), 122.16 KiB | 0 bytes/s, done.Total 24 (delta 1), reused 0 (delta 0)To https://renfeisong@bitbucket.org/renfeisong/project.gitd4eeb86..7d17136develop -&amp;gt; develop&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;其中 &lt;code&gt;proj&lt;/code&gt; 是之前设定的代号，而 &lt;code&gt;develop&lt;/code&gt; 表示推送本地仓库的 &lt;code&gt;develop&lt;/code&gt; 分支。&lt;/p&gt;&lt;h3 id=&quot;section-11&quot;&gt;更新本地仓库&lt;/h3&gt;&lt;p&gt;此时，可以随时使用 &lt;code&gt;git pull&lt;/code&gt; 命令来从远程仓库拉取更新。&lt;/p&gt;&lt;pre&gt;&lt;code&gt;bogon:project renfei$ git pullAlready up-to-date.&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;如果没有更新，就会显示 &lt;code&gt;Already up-to-date&lt;/code&gt;.&lt;/p&gt;&lt;h2 id=&quot;section-12&quot;&gt;补充&lt;/h2&gt;&lt;p&gt;Git 还有很多很多功能，比如从仓库移除文件、重命名、比较等。可以参考 Pro Git 这本书，是基于 CC 协议发布的。地址：&lt;a href=&quot;http://git-scm.com/book/zh&quot;&gt;http://git-scm.com/book/zh&lt;/a&gt;&lt;/p&gt;</description><pubDate>Sun, 02 Feb 2014 00:00:00 +0800</pubDate><link>https://www.renfei.org/blog/git-quick-start-guide.html</link><guid isPermaLink="true">https://www.renfei.org/blog/git-quick-start-guide.html</guid></item><item><title>为固定高度的网页元素添加 iOS Safari 滚动「惯性效果」的方法</title><description>&lt;p&gt;最近在做一个手机端的 Web 应用，需要建立一个和浏览器窗口一样大的固定高度的 div，并且允许里面包含的内容进行上下滚动。&lt;/p&gt;&lt;p&gt;完成这个事情非常简单：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-css&quot; data-lang=&quot;css&quot;&gt;&lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;overflow-y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;但是实际在 iPhone 上测试时，发现 Safari（也包括很多 iOS App 内置浏览器使用的 UIWebView）竟然完全禁用掉了著名的「惯性滚动」效果。然而在桌面 Safari 是没问题的。听着似乎没啥，但是实际操作起来会感觉滑动时非常非常不爽。&lt;/p&gt;&lt;p&gt;虽然原因尚不清楚，但解决方法是有的，令人吃惊的是还非常简单：只有一条 CSS 属性即可解决问题。&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-css&quot; data-lang=&quot;css&quot;&gt;&lt;span class=&quot;nt&quot;&gt;-webkit-overflow-scrolling&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;touch&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;有的同学添加了这一条属性后遇到了向下滑动后页面变成空白的问题，实际上可以通过一条 CSS 属性把页面加载到内存来解决：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-css&quot; data-lang=&quot;css&quot;&gt;&lt;span class=&quot;nt&quot;&gt;-webkit-transform&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;translate3d&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;但是如果你通过这个属性把太多内容加入了内存会导致一些性能/卡顿问题。如何权衡就看各位了。&lt;/p&gt;&lt;p&gt;参考资料：&lt;/p&gt;&lt;p&gt;[1] http://stackoverflow.com/questions/9801687/using-webkit-overflow-scrolling-touch-hides-content-while-scrolling-dragging&lt;br /&gt;[2] http://cooshtee.com/blog/2012/11/add-inertial-scrolling-to-a-fixed-height-div-in-ios/&lt;/p&gt;</description><pubDate>Sun, 17 Nov 2013 00:00:00 +0800</pubDate><link>https://www.renfei.org/blog/how-to-add-ios-inertial-scrolling-to-a-fixed-height-element.html</link><guid isPermaLink="true">https://www.renfei.org/blog/how-to-add-ios-inertial-scrolling-to-a-fixed-height-element.html</guid></item><item><title>网络流－最大流问题 ISAP 算法解释</title><description>&lt;p&gt;ISAP 是图论求最大流的算法之一，它很好的平衡了运行时间和程序复杂度之间的关系，因此非常常用。&lt;/p&gt;&lt;h2 id=&quot;section&quot;&gt;约定&lt;/h2&gt;&lt;p&gt;我们使用邻接表来表示图，表示方法可以见文章&lt;a href=&quot;//www.renfei.org/blog/weighted-shortest-path.html&quot;&gt;带权最短路 Dijkstra, SPFA, Bellman-Ford, ASP, Floyd-Warshall 算法分析&lt;/a&gt;或&lt;a href=&quot;//www.renfei.org/blog/bipartite-matching.html&quot;&gt;二分图的最大匹配、完美匹配和匈牙利算法&lt;/a&gt;的开头（就不重复贴代码了）。在下文中，图的源点（source）表示为 $ s $ ，汇点（sink）表示为 $ t $ ，当前节点为 $ u $ 。建图时，需要建立双向边（设反向的边容量为 $ 0 $）才能保证算法正确。&lt;/p&gt;&lt;h2 id=&quot;section-1&quot;&gt;引入&lt;/h2&gt;&lt;p&gt;求解最大流问题的一个比较容易想到的方法就是，每次在残量网络（residual network）中任意寻找一条从 $ s $ 到 $ t $ 的路径，然后增广，直到不存在这样的路径为止。这就是一般增广路算法（labeling algorithm）。可以证明这种不加改进的贪婪算法是正确的。假设最大流是 $ f $ ，那么它的运行时间为$ O\left(\ f\cdot \mid E\mid\right) $ 。但是，这个运行时间并不好，因为它和最大流 $ f $ 有关。&lt;/p&gt;&lt;p&gt;人们发现，如果每次都沿着残量网络中的最短增广路增广，则运行时间可以减为 $ O\left(\mid E\mid^2\cdot\mid V\mid\right)\ $ 。这就是最短增广路算法。而 ISAP 算法则是最短增广路算法的一个改进。其实，ISAP 的意思正是「改进的最短增广路」 (Improved Shortest Augmenting Path)。&lt;/p&gt;&lt;p&gt;顺便说一句，上面讨论的所有算法根本上都属于增广路方法（Ford-Fulkerson method）。和它对应的就是大名鼎鼎的预流推进方法（Preflow-push method）。其中最高标号预流推进算法（Highest-label preflow-push algorithm）的复杂度可以达到 $ O\left(\mid V\mid ^2 \sqrt{\mid E\mid}\right) $。虽然在复杂度上比增广路方法进步很多，但是预流推进算法复杂度的上界是比较紧的，因此有时差距并不会很大。&lt;/p&gt;&lt;h2 id=&quot;section-2&quot;&gt;算法解释&lt;/h2&gt;&lt;p&gt;概括地说，ISAP 算法就是不停地找最短增广路，找到之后增广；如果遇到死路就 retreat，直到发现$s$, $t$不连通，算法结束。找最短路本质上就是无权最短路径问题，因此采用 BFS 的思想。具体来说，使用一个数组$d$，记录每个节点到汇点$t$的最短距离。搜索的时候，只沿着满足$d[u]=d[v]+1$的边$u\rightarrow v$（这样的边称为允许弧）走。显然，这样走出来的一定是最短路。&lt;/p&gt;&lt;p&gt;原图存在两种子图，一个是残量网络，一个是允许弧组成的图。残量网络保证可增广，允许弧保证最短路（时间界较优）。所以，在寻找增广路的过程中，一直是在残量网络中沿着允许弧寻找。因此，&lt;strong&gt;允许弧应该是属于残量网络的，而非原图的&lt;/strong&gt;。换句话说，我们沿着允许弧，走的是残量网络（而非原图）中的最短路径。当我们找到沿着残量网络找到一条增广路，增广后，残量网络肯定会变化（至少少了一条边），因此决定允许弧的$d$数组要进行相应的更新（顺便提一句，Dinic 的做法就是每次增广都重新计算$d$数组）。然而，ISAP 「改进」的地方之一就是，其实没有必要马上更新$d$数组。这是因为，去掉一条边只可能令路径变得更长，而如果增广之前的残量网络存在另一条最短路，并且在增广后的残量网络中仍存在，那么这条路径毫无疑问是最短的。所以，ISAP 的做法是继续增广，直到遇到死路，才执行 retreat 操作。&lt;/p&gt;&lt;p&gt;说到这里，大家应该都猜到了，retreat 操作的主要任务就是更新$d$数组。那么怎么更新呢？非常简单：假设是从节点$u$找遍了邻接边也没找到允许弧的；再设一变量$m$，令$m$等于残量网络中$u$的所有邻接点的$d$数组的最小值，然后令$d[u]$等于$m+1$即可。这是因为，进入 retreat 环节说明残量网络中$u$和 $t$已经不能通过（已过时）的允许弧相连，那么$u$和$t$实际上在残量网络中的最短路的长是多少呢？（这正是$d$的定义！）显然是残量网络中$u$的所有邻接点和$t$的距离加$1$的最小情况。特殊情况是，残量网络中$u$根本没有邻接点。如果是这样，只需要把$d[u]$设为一个比较大的数即可，这会导致任何点到$u$的边被排除到残量网络以外。（严格来说只要大于等于$\mid V\mid$即可。由于最短路一定是无环的，因此任意路径长最大是$\mid V\mid−1$）。修改之后，只需要把正在研究的节点$u$沿着刚才走的路退一步，然后继续搜索即可。&lt;/p&gt;&lt;p&gt;讲到这里，ISAP 算法的框架内容就讲完了。对于代码本身，还有几个优化和实现的技巧需要说明。&lt;/p&gt;&lt;ol&gt;&lt;li&gt;算法执行之前需要用 BFS 初始化$d$数组，方法是从$t$到$s$逆向进行。&lt;/li&gt;&lt;li&gt;算法主体需要维护一个「当前节点」$u$，执行这个节点的前进、retreat 等操作。&lt;/li&gt;&lt;li&gt;记录路径的方法非常简单，声明一个数组$p$，令$p[i]$等于增广路上到达节点$i$的边的序号（这样就可以找到从哪个顶点到的顶点$i$）。需要路径的时候反向追踪一下就可以了。&lt;/li&gt;&lt;li&gt;判断残量网络中$s, t$不连通的条件，就是$d[s]\ge\mid V\mid$ 。这是因为当$s, t$不连通时，最终残量网络中$s$将没有任何邻接点，对$s$的 retreat 将导致上面条件的成立。&lt;/li&gt;&lt;li&gt;GAP 优化。GAP 优化可以提前结束程序，很多时候提速非常明显（高达 100 倍以上）。GAP 优化是说，进入 retreat 环节后，$u, t$之间的连通性消失，但如果$u$是最后一个和$t$距离$d[u]$（更新前）的点，说明此时$s, t$也不连通了。这是因为，虽然$u, t$已经不连通，但毕竟我们走的是最短路，其他点此时到$t$的距离一定大于$d[u]$（更新前），因此其他点要到$t$，必然要经过一个和$t$距离为$d[u]$（更新前）的点。GAP 优化的实现非常简单，用一个数组记录并在适当的时候判断、跳出循环就可以了。&lt;/li&gt;&lt;li&gt;另一个优化，就是用一个数组保存一个点已经尝试过了哪个邻接边。寻找增广的过程实际上类似于一个 BFS 过程，因此之前处理过的邻接边是不需要重新处理的（残量网络中的边只会越来越少）。具体实现方法直接看代码就可以，非常容易理解。需要注意的一点是，下次应该从上次处理到的邻接边继续处理，而非从上次处理到的邻接边的下一条开始。&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;最后说一下增广过程。增广过程非常简单，寻找增广路成功（当前节点处理到$t$）后，沿着你记录的路径走一遍，记录一路上的最小残量，然后从$s$到$t$更新流量即可。&lt;/p&gt;&lt;h2 id=&quot;section-3&quot;&gt;实现&lt;/h2&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot; data-lang=&quot;cpp&quot;&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// 源点&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sink&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// 汇点&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_nodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// 可增广路上的上一条弧的编号&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_nodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 和 t 的最短距离等于 i 的节点数量&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cur&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_nodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 当前弧下标&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_nodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// 残量网络中节点 i 到汇点 t 的最短距离&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_nodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// 预处理, 反向 BFS 构造 d 数组&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;bfs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;memset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;visited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;visited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;queue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sink&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;visited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sink&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sink&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;front&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;iterator_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ix&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;begin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ix&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Edge&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;visited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;capacity&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;flow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;visited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// 增广&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;augment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sink&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;df&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__inf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// 从汇点到源点通过 p 追踪增广路径, df 为一路上最小的残量&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Edge&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]];&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;df&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;capacity&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;flow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sink&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// 从汇点到源点更新流量&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;flow&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;flow&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;max_flow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;flow&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bfs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;memset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num_nodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;memset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cur&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cur&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num_nodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sink&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;flow&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;augment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;advanced&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cur&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Edge&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]];&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;capacity&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;flow&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;advanced&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cur&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;advanced&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// retreat&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num_nodes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;iterator_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ix&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;begin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ix&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;capacity&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;flow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// gap 优化&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cur&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;flow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description><pubDate>Wed, 07 Aug 2013 00:00:00 +0800</pubDate><link>https://www.renfei.org/blog/isap.html</link><guid isPermaLink="true">https://www.renfei.org/blog/isap.html</guid></item><item><title>带权最短路 Dijkstra, SPFA, Bellman-Ford, ASP, Floyd-Warshall 算法分析</title><description>&lt;p&gt;图论中，用来求最短路的方法有很多，适用范围和时间复杂度也各不相同。&lt;/p&gt;&lt;p&gt;本文主要介绍的算法的代码主要来源如下：&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Dijkstra: &lt;em&gt;Algorithms&lt;/em&gt;（《算法概论》）Sanjoy Dasgupta, Christos Papadimitriou, Umesh Vazirani;《算法竞赛入门经典—训练指南》刘汝佳、陈峰。&lt;/li&gt;&lt;li&gt;SPFA (Shortest Path Faster Algorithm): &lt;em&gt;Data Structure and Algorithms Analysis in C&lt;/em&gt;, 2nd ed.（《数据结构与算法分析》）Mark Allen Weiss.&lt;/li&gt;&lt;li&gt;Bellman-Ford: &lt;em&gt;Algorithms&lt;/em&gt;（《算法概论》）Sanjoy Dasgupta, Christos Papadimitriou, Umesh Vazirani.&lt;/li&gt;&lt;li&gt;ASP (Acyclic Shortest Paths): &lt;em&gt;Introduction to Algorithms - A Creative Approach&lt;/em&gt;（《算法引论—一种创造性方法》）Udi Manber.&lt;/li&gt;&lt;li&gt;Floyd-Warshall: &lt;em&gt;Data Structure and Algorithms Analysis in C&lt;/em&gt;, 2nd ed.（《数据结构与算法分析》）Mark Allen Weiss.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;它们的使用限制和运行时间如下：&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Dijkstra: 不含负权。运行时间依赖于优先队列的实现，如 $O\left(\left(\mid V\mid+\mid E\mid\right)\log\mid V\mid\right)$&lt;/li&gt;&lt;li&gt;SPFA: 无限制。运行时间$O\left(k\cdot\mid E\mid\right)\ \left(k \ll \mid V\mid\right)$&lt;/li&gt;&lt;li&gt;Bellman-Ford：无限制。运行时间$O\left(\mid V\mid\cdot\mid E\mid\right)$&lt;/li&gt;&lt;li&gt;ASP: 无圈。运行时间$O\left(\mid V\mid+\mid E\mid\right)$&lt;/li&gt;&lt;li&gt;Floyd-Warshall: 无限制。运行时间$O\left(\mid V\mid^3\right)$&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;其中 1~4 均为单源最短路径 (Single Source Shortest Paths) 算法； 5 为全源最短路径 (All Pairs Shortest Paths) 算法。顺便说一句，为什么没有点对点的最短路径？如果我们只需要一个起点和一个终点，不是比计算一个起点任意终点更节省时间么？答案还真不是，目前还没有发现比算从源点到所有点更快的算法。&lt;/p&gt;&lt;h2 id=&quot;section&quot;&gt;图的表示&lt;/h2&gt;&lt;p&gt;本文中，前四个算法的图都采用邻接表表示法，如下：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot; data-lang=&quot;cpp&quot;&gt;&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Edge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;weight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Edge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;weight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num_nodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num_edges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Edge&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_nodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 每个节点出发的边编号&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_nodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 当前节点单源最短路中的上一条边&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_nodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 单源最短路径长&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;dijkstra-&quot;&gt;Dijkstra 方法&lt;/h2&gt;&lt;p&gt;Dijkstra 方法依据其优先队列的实现不同，可以写成几种时间复杂度不同的算法。它是图论－最短路中最经典、常见的算法。关于这个方法，网上有许多分析，但是我最喜欢的还是《算法概论》中的讲解。为了理解 Dijkstra 方法，首先回顾一下无权最短路的算法。无权最短路算法基于 BFS，每次从源点向外扩展一层，并且给扩展到的顶点标明距离，这个距离就是最短路的长。我们完全可以仿照这个思路，把带权图最短路问题规约到无权图最短路问题——只要把长度大于 1 的边填充进一些「虚顶点」即可。如下图所示。&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;//img.renfei.org/2013/08/11.png&quot; alt=&quot;Dijkstra&quot; /&gt;&lt;/p&gt;&lt;p&gt;这个办法虽然可行，但是显然效率很低。不过，Dijkstra 方法$EC, EB, ED$分别出发，经过一系列「虚节点」，依次到达$D, B, C$ 。为了不在虚节点处浪费时间，出发之前，我们设定三个闹钟，时间分别为$4, 3, 2$提醒我们预计在这些时刻会有重要的事情发生（经过实际节点）。更一般地说，假设现在我们处理到了某个顶点$u$，和$u$相邻接的顶点为$v_1, v_2, \ldots, v_n$，它们和$u$的距离为$d_1, d_2, \ldots, d_n$。我们为$v_1, v_2, \ldots, v_n$各设定一个闹钟。如果还没有设定闹钟，那么设定为$d$ ；如果设定的时间比$d$晚，那么重新设定为$d$（此时我们沿着这条路比之前的某一条路会提前赶到）。每次闹钟响起，都说明可能经过了实际节点，我们都会更新这些信息，直到不存在任何闹钟。综上所述，也就是随着 BFS 的进行，我们一旦发现更近的路径，就立即更新路径长，直到处理完最后（最远）的一个顶点。由此可见，由于上述「虚顶点」并非我们关心的实际顶点，因此 Dijkstra 方法的处理方式为：直接跳过了它们。&lt;/p&gt;&lt;p&gt;还需要解决的一个问题，就是闹钟的管理。闹钟一定是从早到晚按顺序响起的，然而我们设闹钟的顺序却不一定按照时间升序，因此需要一个优先队列来管理。Dijkstra 方法实现的效率严重依赖于优先队列的实现。一个使用标准库容器适配器 &lt;code&gt;priority_queue&lt;/code&gt; 的算法版本如下：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot; data-lang=&quot;cpp&quot;&gt;&lt;span class=&quot;k&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HeapNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Dijkstra&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;priority_queue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HeapNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HeapNode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;greater&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HeapNode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_nodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__inf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;make_pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;N&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;second&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Edge&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]];&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;weight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;weight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;make_pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;bellman-ford&quot;&gt;Bellman-Ford：一个简单的想法&lt;/h2&gt;&lt;p&gt;Dijkstra 方法的本质是进行一系列如下的&lt;strong&gt;更新操作&lt;/strong&gt;：&lt;/p&gt;&lt;script type=&quot;math/tex; mode=display&quot;&gt;d(v)=\min\{d(v),\ d(u)+l(u,v)\}&lt;/script&gt;&lt;p&gt;然而，如果边权含有负值，那么 Dijkstra 方法将不再适用。原因解释如下。&lt;/p&gt;&lt;p&gt;假设最终的最短路径为：&lt;/p&gt;&lt;script type=&quot;math/tex; mode=display&quot;&gt;s \rightarrow u_1 \rightarrow u_2 \rightarrow u_3 \rightarrow \ldots \rightarrow u_k \rightarrow t&lt;/script&gt;&lt;p&gt;不难看出，如果按照 $ (s,\ u_1),\ (u_1,\ u_2),\ \ldots ,(u_k,\ t) $ 的顺序执行上述更新操作，最终$ t $的最短路径一定是正确的。而且，只要保证上述更新操作全部按顺序执行即可，并不要求上述更新操作是连续进行的。Dijkstra 算法所运行的更新序列是经过选择的，而选择基于这一假设：$ s\rightarrow t $的最短路一定&lt;strong&gt;不会经过&lt;/strong&gt;和$s$距离大于$ l(s,\ t) $的点。对于正权图这一假设是显然的，对于负权图这一假设是错误的。&lt;/p&gt;&lt;p&gt;因此，为了求出负权图的最短路径，我们需要保证一个合理的更新序列。但是，我们并不知道最终的最短路径！因此一个简单的想法就是：更新所有的边，每条边都更新$\mid V\mid -1$次。由于多余的更新操作总是无害的，因此算法（几乎）可以正确运行。等等，为什么是$\mid V\mid -1$次？这是由于，任何含有$\mid V\mid$个顶点的图两个点之间的最短路径最多含有$\mid V\mid -1$条边。这意味着最短路不会包含环。理由是，如果是负环，最短路不存在；如果是正环，去掉后变短；如果是零环，去掉后不变。&lt;/p&gt;&lt;p&gt;算法实现中唯一一个需要注意的问题就是&lt;strong&gt;负值圈&lt;/strong&gt; (negative-cost cycle)。负值圈指的是，权值总和为负的圈。如果存在这种圈，我们可以在里面滞留任意长而不断减小最短路径长，因此这种情况下最短路径可能是不存在的，可能使程序陷入无限循环。好在，本文介绍的几种算法都可以判断负值圈是否存在。对于 Bellman-Ford 算法来说，判断负值圈存在的方法是：在$\mid V\mid -1$次循环之后再执行一次循环，如果还有更新操作发生，则说明存在负值圈。&lt;/p&gt;&lt;p&gt;Bellman-Ford 算法的代码如下：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot; data-lang=&quot;cpp&quot;&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Bellman_Ford&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_nodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__inf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_nodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;changed&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_edges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;weight&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__inf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;weight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;changed&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;changed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num_nodes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;changed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 程序应该永远不会执行到这里&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;注记：&lt;/p&gt;&lt;ol&gt;&lt;li&gt;如果某次循环没有更新操作发生，以后也不会有了。我们可以就此结束程序，避免无效的计算。&lt;/li&gt;&lt;li&gt;上述程序中第 11 行的判断：如果去掉这个判断，只要图中存在负值圈函数就会返回 &lt;code&gt;false&lt;/code&gt;。否则，仅在给定源点可以达到负值圈时才返回 &lt;code&gt;false&lt;/code&gt;。&lt;/li&gt;&lt;/ol&gt;&lt;h2 id=&quot;spfa&quot;&gt;SPFA：一个改进的想法&lt;/h2&gt;&lt;p&gt;看来，Bellman-Ford 算法多少有些浪费。这里介绍的 SPFA 可以算作是 Bellman-Ford 的队列改进版。在 Dijkstra 方法中，随着 BFS 的进行，最短路一点一点地「生长」。然而如果存在负权，我们的算法必须允许「绕回去」的情况发生。换句话说，我们需要在某些时候撤销已经形成的最短路。同时，我们还要改变 Bellman-Ford 算法盲目更新的特点，只更新有用的节点。SPFA 中，一开始，我们把源点 $s$放入队列中，然后每次循环让一个顶点$u$出队，找出所有与$u$邻接的顶点$v$，更新最短路，并当$v$不在队列里时将它入队。循环直到队列为空（没有需要更新的顶点）。&lt;/p&gt;&lt;p&gt;可以显示出 SPFA 和 Bellman-Ford 算法相比的一个重大改进的最经典的一个例子，就是图为一条链的情形。&lt;/p&gt;&lt;p&gt;容易看出，如果存在负值圈，这个算法将无限循环下去。因此需要一个机制来确保算法得以中止。由于最短路最长只含有$\mid V\mid -1$条边，因此如果任何一个顶点已经出队$\mid V\mid +1$次，算法停止运行。&lt;/p&gt;&lt;p&gt;SPFA 的代码如下：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot; data-lang=&quot;cpp&quot;&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in_queue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_nodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cnt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_nodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SPFA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;queue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;memset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;in_queue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;in_queue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;memset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cnt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cnt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_nodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__inf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;in_queue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;in_queue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;front&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Edge&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]];&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;weight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;weight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;in_queue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;in_queue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cnt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num_nodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我们已经给出 SPFA 的运行时间为$O\left(k\cdot\mid E\mid\right)\ \left(k \ll \mid V\mid\right)$。实际上，可以&lt;strong&gt;期望&lt;/strong&gt;$k&amp;lt;2$。但是可以构造出使 SPFA 算法变得很慢的针对性数据。&lt;/p&gt;&lt;h2 id=&quot;acyclic-shortest-path&quot;&gt;Acyclic Shortest Path&lt;/h2&gt;&lt;p&gt;如果图是无圈的（acyclic）（或称为有向无环图，DAG），那么情况就变的容易了。我们可以构造一个&lt;strong&gt;拓扑升序序列&lt;/strong&gt;，由拓扑排序的性质，无圈图的任意路径中，顶点都是沿着「拓扑升序序列」排列的，因此我们只需要按照这个序列执行更新操作即可。显然，这样可以在线性时间内解决问题。&lt;/p&gt;&lt;p&gt;实现上，拓扑排序和更新可以一趟完成。这种算法的代码如下：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot; data-lang=&quot;cpp&quot;&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;indegree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_nodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;asp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;queue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_nodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__inf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;indegree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_edges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;indegree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_nodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;indegree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;front&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;weight&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__inf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;weight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;indegree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;floyd-warshall&quot;&gt;Floyd-Warshall&lt;/h2&gt;&lt;p&gt;与前面四种不同，Floyd-Warshall 算法是所谓的「全源最短路径」，也就是任意两点间的最短路径。它并&lt;strong&gt;不是&lt;/strong&gt;对单源最短路径$\mid V\mid$次迭代的一种&lt;strong&gt;渐进改进&lt;/strong&gt;，但是对非常稠密的图却可能更快，因为它的循环更加紧凑。而且，这个算法支持负的权值。&lt;/p&gt;&lt;p&gt;Floyd-Warshall 算法如下：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot; data-lang=&quot;cpp&quot;&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_nodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_nodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 记录路径长&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_nodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_nodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 记录实际路径&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Floyd_Warshall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_nodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_nodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_nodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_nodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_nodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__inf&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__inf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其中 &lt;code&gt;dist&lt;/code&gt; 数组应初始化为邻接矩阵。需要提醒的是， &lt;code&gt;dist[i][i]&lt;/code&gt; 实际上表示「从顶点 i 绕一圈再回来的最短路径」，因此图存在负环当且仅当 dist[i][i]&amp;lt;0。初始化时， &lt;code&gt;dist[i][i]&lt;/code&gt;可以初始化为&lt;code&gt;0&lt;/code&gt;，也可以初始化为$\infty$ 。&lt;/p&gt;&lt;h2 id=&quot;section-1&quot;&gt;显示实际路径&lt;/h2&gt;&lt;p&gt;显示实际路径实际上非常简单。利用前四个算法提供的 &lt;code&gt;int *p&lt;/code&gt;，还原实际路径的一个方法如下：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot; data-lang=&quot;cpp&quot;&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;printpath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;firstcall&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;%d&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;firstcall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;%d -&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;firstcall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot; %d&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;printpath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot; %d -&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;firstcall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot; %d&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;利用 Floyd-Warshall 算法提供的 &lt;code&gt;int **path&lt;/code&gt;，还原实际路径的一个方法如下：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot; data-lang=&quot;cpp&quot;&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;showpath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;%d -&amp;gt; %d:(%2d)%d&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot; -&amp;gt; %d&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description><pubDate>Sun, 04 Aug 2013 00:00:00 +0800</pubDate><link>https://www.renfei.org/blog/weighted-shortest-path.html</link><guid isPermaLink="true">https://www.renfei.org/blog/weighted-shortest-path.html</guid></item><item><title>二分图的最大匹配、完美匹配和匈牙利算法</title><description>&lt;p&gt;这篇文章讲无权二分图（unweighted bipartite graph）的最大匹配（maximum matching）和完美匹配（perfect matching），以及用于求解匹配的匈牙利算法（Hungarian Algorithm）；不讲带权二分图的最佳匹配。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;二分图&lt;/strong&gt;：简单来说，如果图中点可以被分为两组，并且使得所有边都跨越组的边界，则这就是一个二分图。准确地说：把一个图的顶点划分为两个不相交集 $U$ 和$V$ ，使得每一条边都分别连接$U$、$V$中的顶点。如果存在这样的划分，则此图为一个二分图。二分图的一个等价定义是：不含有「含奇数条边的环」的图。图 1 是一个二分图。为了清晰，我们以后都把它画成图 2 的形式。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;匹配&lt;/strong&gt;：在图论中，一个「匹配」（matching）是一个边的集合，其中任意两条边都没有公共顶点。例如，图 3、图 4 中红色的边就是图 2 的匹配。&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;//img.renfei.org/2013/08/1.png&quot; alt=&quot;Bipartite Graph(1)&quot; /&gt;  &lt;img src=&quot;//img.renfei.org/2013/08/2.png&quot; alt=&quot;Bipartite Graph(2)&quot; /&gt;  &lt;img src=&quot;//img.renfei.org/2013/08/3.png&quot; alt=&quot;Matching&quot; /&gt;  &lt;img src=&quot;//img.renfei.org/2013/08/4.png&quot; alt=&quot;Maximum Matching&quot; /&gt;&lt;/p&gt;&lt;p&gt;我们定义&lt;strong&gt;匹配点&lt;/strong&gt;、&lt;strong&gt;匹配边&lt;/strong&gt;、&lt;strong&gt;未匹配点&lt;/strong&gt;、&lt;strong&gt;非匹配边&lt;/strong&gt;，它们的含义非常显然。例如图 3 中 1、4、5、7 为匹配点，其他顶点为未匹配点；1-5、4-7为匹配边，其他边为非匹配边。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;最大匹配&lt;/strong&gt;：一个图所有匹配中，所含匹配边数最多的匹配，称为这个图的最大匹配。图 4 是一个最大匹配，它包含 4 条匹配边。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;完美匹配&lt;/strong&gt;：如果一个图的某个匹配中，所有的顶点都是匹配点，那么它就是一个完美匹配。图 4 是一个完美匹配。显然，完美匹配一定是最大匹配（完美匹配的任何一个点都已经匹配，添加一条新的匹配边一定会与已有的匹配边冲突）。但并非每个图都存在完美匹配。&lt;/p&gt;&lt;p&gt;举例来说：如下图所示，如果在某一对男孩和女孩之间存在相连的边，就意味着他们彼此喜欢。是否可能让所有男孩和女孩两两配对，使得每对儿都互相喜欢呢？图论中，这就是&lt;strong&gt;完美匹配&lt;/strong&gt;问题。如果换一个说法：最多有多少互相喜欢的男孩/女孩可以配对儿？这就是&lt;strong&gt;最大匹配&lt;/strong&gt;问题。&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;//img.renfei.org/2013/08/0.png&quot; alt=&quot;0&quot; /&gt;&lt;/p&gt;&lt;p&gt;基本概念讲完了。求解最大匹配问题的一个算法是&lt;strong&gt;匈牙利算法&lt;/strong&gt;，下面讲的概念都为这个算法服务。&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;//img.renfei.org/2013/08/5.png&quot; alt=&quot;5&quot; /&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;交替路&lt;/strong&gt;：从一个未匹配点出发，依次经过非匹配边、匹配边、非匹配边…形成的路径叫交替路。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;增广路&lt;/strong&gt;：从一个未匹配点出发，走交替路，如果途径另一个未匹配点（出发的点不算），则这条交替路称为增广路（agumenting path）。例如，图 5 中的一条增广路如图 6 所示（图中的匹配点均用红色标出）：&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;//img.renfei.org/2013/08/6.png&quot; alt=&quot;6&quot; /&gt;&lt;/p&gt;&lt;p&gt;增广路有一个重要特点：非匹配边比匹配边多一条。因此，研究增广路的意义是&lt;strong&gt;改进匹配&lt;/strong&gt;。只要把增广路中的匹配边和非匹配边的身份交换即可。由于中间的匹配节点不存在其他相连的匹配边，所以这样做不会破坏匹配的性质。交换后，图中的匹配边数目比原来多了 1 条。&lt;/p&gt;&lt;p&gt;我们可以通过不停地找增广路来增加匹配中的匹配边和匹配点。找不到增广路时，达到最大匹配（这是增广路定理）。匈牙利算法正是这么做的。在给出匈牙利算法 DFS 和 BFS 版本的代码之前，先讲一下匈牙利树。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;匈牙利树&lt;/strong&gt;一般由 BFS 构造（类似于 BFS 树）。从一个未匹配点出发运行 BFS（唯一的限制是，必须走交替路），直到不能再扩展为止。例如，由图 7，可以得到如图 8 的一棵 BFS 树：&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;//img.renfei.org/2013/08/7.png&quot; alt=&quot;7&quot; /&gt;   &lt;img src=&quot;//img.renfei.org/2013/08/8.png&quot; alt=&quot;8&quot; /&gt;    &lt;img src=&quot;//img.renfei.org/2013/08/9.png&quot; alt=&quot;9&quot; /&gt;&lt;/p&gt;&lt;p&gt;这棵树存在一个叶子节点为非匹配点（7 号），但是匈牙利树要求所有叶子节点均为匹配点，因此这不是一棵匈牙利树。如果原图中根本不含 7 号节点，那么从 2 号节点出发就会得到一棵匈牙利树。这种情况如图 9 所示（顺便说一句，图 8 中根节点 2 到非匹配叶子节点 7 显然是一条增广路，沿这条增广路扩充后将得到一个完美匹配）。&lt;/p&gt;&lt;p&gt;下面给出&lt;strong&gt;匈牙利算法&lt;/strong&gt;的 DFS 和 BFS 版本的代码：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot; data-lang=&quot;cpp&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// 顶点、边的编号均从 0 开始&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// 邻接表储存&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Edge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;weight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Edge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;weight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__maxNodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/* G[i] 存储顶点 i 出发的边的编号 */&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Edge&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iterator&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;iterator_t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num_nodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num_left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num_right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num_edges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot; data-lang=&quot;cpp&quot;&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matching&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__maxNodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/* 存储求解结果 */&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;check&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__maxNodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;dfs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;iterator_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;begin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 对 u 的每个邻接点&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;check&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// 要求不在交替路中&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;check&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 放入交替路&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matching&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dfs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matching&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// 如果是未盖点，说明交替路为增广路，则交换路径，并返回成功&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matching&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matching&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 不存在增广路，返回失败&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;hungarian&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ans&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;memset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matching&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matching&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num_left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matching&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;memset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;check&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;check&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dfs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ans&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ans&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot; data-lang=&quot;cpp&quot;&gt;&lt;span class=&quot;n&quot;&gt;queue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__maxNodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Hungarian&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ans&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;memset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matching&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matching&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;memset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;check&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;check&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matching&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 设 i 为路径起点&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;flag&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 尚未找到增广路&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;flag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;front&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;iterator_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ix&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;begin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ix&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;flag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;check&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;check&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matching&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matching&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 此点为匹配点&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matching&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 找到未匹配点，交替路变为增广路&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;flag&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matching&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matching&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matching&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matching&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ans&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ans&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;匈牙利算法的要点如下&lt;/strong&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;从左边第 1 个顶点开始，挑选未匹配点进行搜索，寻找增广路。&lt;/p&gt;&lt;ol&gt;&lt;li&gt;如果经过一个未匹配点，说明寻找成功。更新路径信息，匹配边数 +1，停止搜索。&lt;/li&gt;&lt;li&gt;如果一直没有找到增广路，则不再从这个点开始搜索。事实上，此时搜索后会形成一棵匈牙利树。我们可以永久性地把它从图中删去，而不影响结果。&lt;/li&gt;&lt;/ol&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;由于找到增广路之后需要沿着路径更新匹配，所以我们需要一个结构来记录路径上的点。DFS 版本通过函数调用隐式地使用一个栈，而 BFS 版本使用 &lt;code&gt;prev&lt;/code&gt; 数组。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;性能比较&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;两个版本的时间复杂度均为$O\big(V \cdot E\big)$。DFS 的优点是思路清晰、代码量少，但是性能不如 BFS。我测试了两种算法的性能。对于稀疏图，BFS 版本明显快于 DFS 版本；而对于稠密图两者则不相上下。在完全随机数据 9000 个顶点 4,0000 条边时前者领先后者大约 97.6%，9000 个顶点 100,0000 条边时前者领先后者 8.6%, 而达到 500,0000 条边时 BFS 仅领先 0.85%。&lt;/p&gt;&lt;p&gt;补充定义和定理：&lt;/p&gt;&lt;p&gt;&lt;strong&gt;最大匹配数&lt;/strong&gt;：最大匹配的匹配边的数目&lt;/p&gt;&lt;p&gt;&lt;strong&gt;最小点覆盖数&lt;/strong&gt;：选取最少的点，使任意一条边至少有一个端点被选择&lt;/p&gt;&lt;p&gt;&lt;strong&gt;最大独立数&lt;/strong&gt;：选取最多的点，使任意所选两点均不相连&lt;/p&gt;&lt;p&gt;&lt;strong&gt;最小路径覆盖数&lt;/strong&gt;：对于一个 DAG（有向无环图），选取最少条路径，使得每个顶点属于且仅属于一条路径。路径长可以为 0（即单个点）。&lt;/p&gt;&lt;p&gt;定理1：最大匹配数 = 最小点覆盖数（这是 Konig 定理）&lt;/p&gt;&lt;p&gt;定理2：最大匹配数 = 最大独立数&lt;/p&gt;&lt;p&gt;定理3：最小路径覆盖数 = 顶点数 - 最大匹配数&lt;/p&gt;</description><pubDate>Thu, 01 Aug 2013 00:00:00 +0800</pubDate><link>https://www.renfei.org/blog/bipartite-matching.html</link><guid isPermaLink="true">https://www.renfei.org/blog/bipartite-matching.html</guid></item><item><title>简单 PHP + MySQL 数据库动态网站制作</title><description>&lt;p&gt;在这篇文章中，我尽量用最浅显易懂的语言来说明使用 PHP, MySQL 制作一个动态网站的基本技术。阅读本文需要简单的 HTML 基础知识和（任一编程语言的）编程基础知识（例如变量、值、循环、语句块的概念等）。&lt;/p&gt;&lt;h2 id=&quot;php-&quot;&gt;PHP 基础&lt;/h2&gt;&lt;h3 id=&quot;section&quot;&gt;概述&lt;/h3&gt;&lt;p&gt;PHP 是一种解释性语言，可用于对网页进行&lt;strong&gt;预处理&lt;/strong&gt;。PHP 脚本在&lt;strong&gt;服务器端&lt;/strong&gt;运行，其运行结果是一个可用来显示的网页。尽管可以完成许多类似工作，但是 JavaScript 和 PHP 的一大区别就是，JavaScript 是在&lt;strong&gt;浏览器端&lt;/strong&gt;运行的。事实上，浏览器会接收 JavaScript 代码并运行它，所以用户是可以查看 JavaScript 代码的。而 PHP 不会将原始代码交给浏览器， 只会将其运行的结果交给浏览器，所以用 PHP 处理用户登陆、用户权限等问题是安全可靠的。&lt;/p&gt;&lt;h3 id=&quot;php--html&quot;&gt;PHP 与 HTML&lt;/h3&gt;&lt;p&gt;实际编写的时候，通常采用的方式是建立扩展名为 &lt;code&gt;php&lt;/code&gt; 的文件（网页文件本质上是文本文件）。编写 php 代码和编写 html 代码并没有多少区别，而最方便的地方在于，在一个 php 文件中，两种代码是可以混编的。&lt;/p&gt;&lt;p&gt;规则：php 代码需要包含在 &lt;code&gt;&amp;lt;?php ... ?&amp;gt;&lt;/code&gt; 标签中，就像这样：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// code goes here&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;提示：这是一个 php 和 html 混编的较为生动的例子。&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$var&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&amp;lt;html id=&amp;quot;ie6&amp;quot;&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&amp;lt;html id=&amp;quot;ie8&amp;quot;&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;endif&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里的意思是，如果 php 中的变量 &lt;code&gt;$var&lt;/code&gt; 的值为 &lt;code&gt;true&lt;/code&gt;，则放置一个标签，否则放置另一个标签。PHP 的 if 语句可以像上面那样写，也可以写成C/C++风格的：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$var&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// do something&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// do other things&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&quot;php--1&quot;&gt;关于 PHP 中的操作符&lt;/h3&gt;&lt;p&gt;PHP 采用的操作符和 C/C++ 是类似的，例如用 &lt;code&gt;=&lt;/code&gt; 表示赋值，&lt;code&gt;==&lt;/code&gt; 表示相等性比较，以及 &lt;code&gt;&amp;lt;&lt;/code&gt; 和 &lt;code&gt;&amp;gt;&lt;/code&gt; （小于、大于）比较符、&lt;code&gt;!&lt;/code&gt; 取反、&lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;逻辑与、&lt;code&gt;||&lt;/code&gt;逻辑或等。当然，也支持 &lt;code&gt;+-*/&lt;/code&gt; 等数学表达式的运算。&lt;/p&gt;&lt;h3 id=&quot;php--2&quot;&gt;关于 PHP 中的变量&lt;/h3&gt;&lt;p&gt;PHP 中变量的命名一律以符号 &lt;code&gt;$&lt;/code&gt; 开头，可以使用下划线，例如 &lt;code&gt;$is_logged_in&lt;/code&gt; 就是一个表意清晰的变量名。和大多数编程语言不同，PHP 中变量没有类型的概念，而且不用声明就可以直接使用。虽然很爽，但是变量多了的时候也容易混乱，这一点需要特别注意。&lt;/p&gt;&lt;h3 id=&quot;php--3&quot;&gt;关于 PHP 中的语句&lt;/h3&gt;&lt;p&gt;这一点 PHP 和许多其他常见的编程语言很类似，也可以用 &lt;code&gt;if...else&lt;/code&gt; 选择语句（之前已经见过了），PHP 还包括 &lt;code&gt;while&lt;/code&gt; 循环、&lt;code&gt;foreach&lt;/code&gt; 循环等，以后遇到了会详细介绍。&lt;/p&gt;&lt;h2 id=&quot;mysql-&quot;&gt;MySQL 基础&lt;/h2&gt;&lt;p&gt;使用 MySQL 数据库是存储数据的一种方法，MySQL 需要和 PHP 配合来完成对数据库的查询（这里术语“查询”包括写入、更新、读取等）操作。利用 MySQL，你可以创建许多数据库（database），每个数据库可以包含多个表（table），而每个表包含若干字段。为了高效，一般会采取分类维护多个表的方式，而不是把所有数据都储存在同一个表中。&lt;/p&gt;&lt;p&gt;MySQL 需要服务器支持。使用的第一步是建立一个数据库，可以用相应的图形化工具（例如 phpMyAdmin）来建立数据库，也可以在终端直接使用下列 SQL 语句来创建一个名为 database_name 的数据库：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-mysql&quot; data-lang=&quot;mysql&quot;&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DATABASE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;database_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;创建好数据库之后，需要创建表。可以通过下列 SQL 语句来创建一个名为 table_name 的表：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-mysql&quot; data-lang=&quot;mysql&quot;&gt;&lt;span class=&quot;k&quot;&gt;USE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;database_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;table_name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first_name&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;varchar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;last_name&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;varchar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;第一句说明在哪个数据库中添加表，第二个说明添加的表的详情。这里我们在表中添加了两个字段，分别叫做 &lt;code&gt;first_name&lt;/code&gt; 和 &lt;code&gt;last_name&lt;/code&gt;，它们的类型是 &lt;code&gt;varchar(30)&lt;/code&gt;。其中 &lt;code&gt;varchar&lt;/code&gt; 是一种可变长字符类型，而 &lt;code&gt;30&lt;/code&gt; 代表了最大长度。&lt;/p&gt;&lt;p&gt;其他常见的数据类型如下：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-mysql&quot; data-lang=&quot;mysql&quot;&gt;&lt;span class=&quot;kt&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;可变字符&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;CHARACTER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;定长&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;INTEGER&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;整数&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;DECIMAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;小数&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;小数点前后的位数&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;TIMESTAMP&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;日期和时间&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;DATE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;日期&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;你可能已经看出来了，MySQL 的注释符为 &lt;code&gt;--&lt;/code&gt;。你可能觉得没太大用，但是它却是一种稍后要提到的攻击的关键之处。此外，一般字符串都应该使用变长的&lt;code&gt;VARCHAR&lt;/code&gt;类型，而非定长的&lt;code&gt;CHARACTER&lt;/code&gt;类型，因为后者会占据更多的空间，而这是不必要的。&lt;/p&gt;&lt;h2 id=&quot;php--mysql-&quot;&gt;使 PHP 和 MySQL 协作&lt;/h2&gt;&lt;h3 id=&quot;section-1&quot;&gt;第一种方式&lt;/h3&gt;&lt;p&gt;现在你已经创建好了 SQL 数据表，并对 PHP 语言有了一个概览。下面我们直奔主题，学习如何对数据表进行查询。&lt;/p&gt;&lt;p&gt;为了使 PHP 和 MySQL 进行交互，需要为 PHP 提供你的数据库用户名、密码、数据库名和数据表名。当然，最重要的，查询操作的 SQL 语句。我们一一来观察是如何实现的。&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;DB_HOST&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;localhost&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;DB_USER&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;renfei&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;DB_PASS&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;root&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;DB_NAME&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;database_name&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$dbc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;mysqli_connect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;DB_HOST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DB_USER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DB_PASS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DB_NAME&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$query&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;INSERT INTO table_name (column1, column2) VALUES (&amp;#39;value1&amp;#39;, &amp;#39;value2&amp;#39;)&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;mysqli_query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$dbc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;下面来解释一下这一坨代码的工作原理。&lt;/p&gt;&lt;ul&gt;&lt;li&gt;首先 3～6 行为 PHP 中的 &lt;code&gt;define&lt;/code&gt; 语句，作用很明显，把 &lt;code&gt;DB_HOST&lt;/code&gt; 定义为 &lt;code&gt;localhost&lt;/code&gt;，下面的代码中就可以使用 &lt;code&gt;DB_HOST&lt;/code&gt; 来代替 &lt;code&gt;localhost&lt;/code&gt;。这样做的好处在于，如果&lt;code&gt;mysqli_connect&lt;/code&gt;函数在代码中出现多次，修改参数的时候则只需修改 &lt;code&gt;define&lt;/code&gt;语句，非常方便。&lt;/li&gt;&lt;li&gt;然后是一个叫做 &lt;code&gt;mysqli_connect()&lt;/code&gt; 的函数，它需要四个变量，分别是主机名、用户名、密码、数据库名。这个函数执行后的返回值传递给变量 &lt;code&gt;$dbc&lt;/code&gt;，&lt;code&gt;$dbc&lt;/code&gt; 包含了一次数据库连接。注意，这个变量名是任意的，并不强制要求叫做&lt;code&gt;$dbc&lt;/code&gt;。&lt;/li&gt;&lt;li&gt;然后，我们把要对数据库执行的操作对应的 SQL 语句以字符串的形式赋给变量 &lt;code&gt;$query&lt;/code&gt;。这个变量名也是任意的。应该注意到，这里的 SQL 语句是不以分号结尾的。&lt;/li&gt;&lt;li&gt;最后，我们执行 &lt;code&gt;mysqli_query();&lt;/code&gt; 函数，该函数有两个参数，分别是一个数据库连接，和一个 SQL 查询操作。执行该函数后，相应的查询操作被执行。&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;如果把这些代码保存成一个网页，当用户打开网页的时候，如果各项参数正确，它就会完整地运行下去。&lt;/p&gt;&lt;p&gt;这里的 SQL 语句的含义是向叫做 &lt;code&gt;table_name&lt;/code&gt; 的表中插入一行，其中把 &lt;code&gt;colume#&lt;/code&gt; 字段的值相应地设置为 &lt;code&gt;value#&lt;/code&gt;。这里只设定了两个字段的数值（表中还可以有其他字段；没有显式说明的字段则留空或者使用数据表指定的默认值）。该语句的通用形式为：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-mysql&quot; data-lang=&quot;mysql&quot;&gt;&lt;span class=&quot;k&quot;&gt;INSERT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INTO&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;table_name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;column2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;VALUES&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;value1&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;value2&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果你要做的仅仅是执行一个 SQL 语句，那么使用这种模式就可以完成。提醒一下，&lt;code&gt;$dbc&lt;/code&gt; 变量往往是重复使用的。&lt;/p&gt;&lt;p&gt;另一个常用的 SQL 语句就是修改某一行。它的形式为：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-mysql&quot; data-lang=&quot;mysql&quot;&gt;&lt;span class=&quot;k&quot;&gt;UPDATE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_name&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;SET&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;preferred_value1&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;preferred_value2&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;...,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;$id&amp;#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;当然，这个语句应该是写到一行的，不过为了清晰我分开来写。它的含义是，修改名为 &lt;code&gt;table_name&lt;/code&gt; 的表中字段 &lt;code&gt;id&lt;/code&gt; 的值是变量 &lt;code&gt;$id&lt;/code&gt; 的值的所有行，把 &lt;code&gt;column1&lt;/code&gt; 字段的值设为 &lt;code&gt;preferred_value1&lt;/code&gt;，把 &lt;code&gt;column2&lt;/code&gt; 字段的值设为 &lt;code&gt;preferred_value2，依此类推。&lt;/code&gt;这里我们还看到，值既可以用常量表示，也可以用变量表示。&lt;/p&gt;&lt;p&gt;注意：会修改所有符合 &lt;code&gt;WHERE&lt;/code&gt; 子句限定的条件的行（如果省略 &lt;code&gt;WHERE&lt;/code&gt; 子句，就会修改所有行）。&lt;code&gt;WHERE&lt;/code&gt; 子句可以设定多个条件，也可以使用比较运算符。例如：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-mysql&quot; data-lang=&quot;mysql&quot;&gt;&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;age&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gender&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;male&amp;#39;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_admin&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;true&amp;#39;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;$id&amp;#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;（如果你想问 &lt;code&gt;AND&lt;/code&gt; 和 &lt;code&gt;OR&lt;/code&gt; 为什么不是符号 &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; 和 &lt;code&gt;||&lt;/code&gt; 的话，我想提示你，不要把 PHP 语言和 SQL 语言搞混了。这是 SQL 语言，而我只说过 PHP 语言和 C/C++ 有些类似）。&lt;/p&gt;&lt;p&gt;下面介绍其他 SQL 语句。&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-mysql&quot; data-lang=&quot;mysql&quot;&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;删除&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table_name&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;表中的所有行&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;DELETE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;删除&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table_name&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;表中&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;字段为&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;david&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;的所有行&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;DELETE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;email&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;david@example.com&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;删除&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table_name&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;表&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;DROP&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;删除&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table_name&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;表中的&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;score&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;字段&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DROP&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;COLUMN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;score&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;给&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table_name&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;表添加一个叫&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;age&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;的字段，类型为&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;INTEGER&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ADD&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;COLUMN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;age&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;INTEGER&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可见，第一种方式的本质就是编写一条 SQL 语句，然后通过 PHP 来执行它。下面，我们来看第二种方式。&lt;/p&gt;&lt;h3 id=&quot;section-2&quot;&gt;第二种方式&lt;/h3&gt;&lt;p&gt;有时，我们不满足于让服务器去执行一条 SQL 语句。我们会需要从数据库中查询信息，然后把得到的信息储存起来（其实就是储存在变量中）。这样，我们需要一些额外的工作。先看一坨代码：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$dbc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;mysqli_connect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;DB_HOST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DB_USER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DB_PASS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DB_NAME&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$query&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;SELECT * FROM table_name WHERE problem_id=&amp;#39;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$id&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;#39;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;mysqli_query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$dbc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$row&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;mysqli_fetch_array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$problem_title&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;problem_title&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里我们省略了 &lt;code&gt;define&lt;/code&gt; 语句。&lt;/p&gt;&lt;p&gt;这一坨代码和上一坨的主要区别是，我们使用了 &lt;code&gt;mysqli_query()&lt;/code&gt; 函数的&lt;strong&gt;返回值&lt;/strong&gt;，把它保存到 &lt;code&gt;$result&lt;/code&gt; 变量中。这个 &lt;code&gt;$result&lt;/code&gt; 变量里边保存的即为执行 &lt;code&gt;SELECT&lt;/code&gt; 语句的返回结果。&lt;/p&gt;&lt;p&gt;解释一下 &lt;code&gt;SELECT&lt;/code&gt; 语句，它的作用是选取 &lt;code&gt;table_name&lt;/code&gt; 表中符合 &lt;code&gt;WHERE&lt;/code&gt; 子句条件的所有行。上面的语句会选定每一行的&lt;strong&gt;所有&lt;/strong&gt;字段（通配符说明了这一点），并且把这些信息全部储存到变量 &lt;code&gt;$result&lt;/code&gt;中。&lt;/p&gt;&lt;p&gt;然后，用变量 &lt;code&gt;$row&lt;/code&gt; 储存 &lt;code&gt;mysqli_fetch_array()&lt;/code&gt; 函数的返回值。&lt;code&gt;$row&lt;/code&gt; 这个变量非常神奇，&lt;code&gt;$row[&#39;column_name&#39;]&lt;/code&gt; 这个事儿包含的内容正是刚才选定的行的 &lt;code&gt;column_name&lt;/code&gt; 字段的值（事实上，&lt;code&gt;$row&lt;/code&gt; 正是一个数组）。这里，我们把它赋给了 &lt;code&gt;$problem_title&lt;/code&gt; 变量。&lt;/p&gt;&lt;p&gt;到这里你应该问一个问题：如果满足 &lt;code&gt;WHERE&lt;/code&gt; 子句条件的有&lt;strong&gt;不止一行&lt;/strong&gt;的话怎么办？要解答这个问题，需要稍微细致的讲解一下 &lt;code&gt;$row&lt;/code&gt; 这个事儿。如果满足条件的只有一行，那么使用 &lt;code&gt;$row = mysqli_fetch_array($result)&lt;/code&gt; 自然会把这唯一的一行信息储存到 &lt;code&gt;$row&lt;/code&gt; 中。如果有很多行，那么&lt;strong&gt;第一次&lt;/strong&gt;使用 &lt;code&gt;$row = mysqli_fetch_array($result)&lt;/code&gt; 会把第一行的信息储存到 &lt;code&gt;$row&lt;/code&gt; 中，而&lt;strong&gt;第二次&lt;/strong&gt;使用 &lt;code&gt;$row = mysqli_fetch_array($result)&lt;/code&gt; 会把第二行的信息储存到 &lt;code&gt;$row&lt;/code&gt; 中。如果这时没有下一行了，再次调用的话 &lt;code&gt;$row&lt;/code&gt; 会储存逻辑假（&lt;code&gt;false&lt;/code&gt; 或 &lt;code&gt;0&lt;/code&gt;）。类似，如果符合 &lt;code&gt;WHERE&lt;/code&gt; 子句条件的一行都没有，那么执行后 &lt;code&gt;$row&lt;/code&gt; 直接存储逻辑假。&lt;/p&gt;&lt;p&gt;最后补充一点刚才没有提到的。如果不需要所有字段的数据，可以只选择需要的字段。方法是把原来 SQL 语句中的通配符换成字段名称。例如：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;x&quot;&gt;SELECT problem_name, problem_type FROM table_name WHERE problem_id=&amp;#39;$id&amp;#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&quot;while--php-&quot;&gt;while 循环在 PHP 中的应用举例&lt;/h3&gt;&lt;p&gt;如果我们要把一个数据库的许多行信息都展示在网页中，那么需要用到 &lt;code&gt;while&lt;/code&gt; 循环和上面的第二种方式。代码如下：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$dbc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;mysqli_connect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;DB_HOST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DB_USER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DB_PASS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DB_NAME&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$query&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;SELECT user_id FROM database_name ORDER BY user_id ASC&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;mysqli_query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$dbc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$row&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;mysqli_fetch_array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$user_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;user_id&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&amp;lt;tr&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&amp;lt;td&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$user_id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&amp;lt;/tr&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果有一定编程基础的话上面的代码很容易看懂。上面新出现了三种用法，说明如下：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;SELECT&lt;/code&gt; 语句可以附加一个 &lt;code&gt;ORDER BY&lt;/code&gt; 子句，用来控制顺序。例如这里是按照字段 user_id 升序排列。下面的例子会先按照 user_rank 降序排列，user_rank 相同时按照 user_id 升序排列：&lt;/li&gt;&lt;/ul&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-mysql&quot; data-lang=&quot;mysql&quot;&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user_rank&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user_rank&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DESC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user_id&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ASC&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt;关于 PHP 中的 &lt;code&gt;echo&lt;/code&gt; 语句，它可以用来生成文本，类似于 C 中的 &lt;code&gt;printf()&lt;/code&gt; 函数。这里利用它直接生成 HTML 代码。它的用法参考例子就可以了。&lt;/li&gt;&lt;li&gt;关于符号 &lt;code&gt;.&lt;/code&gt; 的用法，它的作用是连接字符串（和变量），往往和 &lt;code&gt;echo&lt;/code&gt; 配合使用，用法参考示例。&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;section-3&quot;&gt;从表单获取信息&lt;/h2&gt;&lt;h3 id=&quot;section-4&quot;&gt;概述&lt;/h3&gt;&lt;p&gt;这一部分我们演示如何构建一个表单，使用户填写这个表单并把内容储存到数据库。这一技术是用户注册系统和用户互动的基础。&lt;/p&gt;&lt;p&gt;要实现这个功能，需要 HTML 和 PHP 配合完成。HTML 负责表单，而 PHP 负责获取信息并使用 SQL 查询储存信息。首先来看 HTML 部分（就是普通的表单）：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;form&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;method=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;post&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;action=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;&amp;lt;?php echo $_SERVER[&amp;#39;PHP_SELF&amp;#39;]; ?&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;label&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;for=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;username&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;用户名:&lt;span class=&quot;nt&quot;&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;text&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;username&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;username&amp;quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&amp;lt;br/&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;label&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;for=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;info&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;信息:&lt;span class=&quot;nt&quot;&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;text&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;info&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;info&amp;quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&amp;lt;br/&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;submit&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;Submit&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;submit&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;属于 HTML 部分的不再解释了，说一说新鲜的。这里的 action 属性后面的 &lt;code&gt;$_SERVER[&#39;PHP_SELF&#39;]&lt;/code&gt;（严格地说，&lt;code&gt;$_SERVER&lt;/code&gt;），是 PHP 的一个超级全局变量，内容是当前页面的相对路径，例如 &lt;code&gt;signup.php&lt;/code&gt;。这个 action 属性的含义是指定用户填写的信息在哪里被处理，这里是在当前页面处理。一般的做法都是将负责处理这部分信息的 PHP 代码和 HTML 代码放在同一页面内。&lt;/p&gt;&lt;p&gt;下面来看一下相应的 PHP 处理部分的代码：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$dbc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;mysqli_connect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;DB_HOST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DB_USER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DB_PASS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DB_NAME&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;isset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_POST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;submit&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_POST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;username&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$info&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_POST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;info&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$query&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;INSERT INTO table_name (tb_user, tb_info) VALUES (&amp;#39;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$user&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;#39;, &amp;#39;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$info&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;#39;)&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;mysqli_query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$dbc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&amp;lt;p&amp;gt;提交成功&amp;lt;/p&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;mysqli_close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$dbc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;首先仍然是建立数据库连接。当用户点击 sumbit 按钮后，表单的内容会被储存在 PHP 中 &lt;code&gt;$_POST&lt;/code&gt; 超级全局变量内，这个超级全局变量仍然是一个数组。&lt;code&gt;isset()&lt;/code&gt; 函数用来检查变量是否被设置，只有用户点击 submit 后 &lt;code&gt;isset($_POST[&#39;submit&#39;])&lt;/code&gt; 才返回真，所以不用担心，首次加载表单（那时用户还没有填写任何内容）是不会执行这部分 PHP 代码的，只有用户提交之后才会执行。用户填写的具体内容可以用 &lt;code&gt;$_POST[&#39;name&#39;]&lt;/code&gt; 来获取。这里的 name 对应的是 HTML 中 name 属性的内容。这一段程序把用户填写的内容赋给变量，然后执行插入到数据库的操作。&lt;/p&gt;&lt;p&gt;这里新出现了一个内容，就是 &lt;code&gt;mysqli_close()&lt;/code&gt; 函数，它的作用是关闭数据库连接。当我们不再需要这个连接的时候，及时关闭是一个好主意。&lt;/p&gt;&lt;p&gt;需要注意的是，这仅仅是最简单的代码，而且实际上是不完善的。如果要真正投入使用，我们需要使它更健壮一些。下面逐一讨论这些内容。&lt;/p&gt;&lt;h3 id=&quot;section-5&quot;&gt;检查用户输入是否合法&lt;/h3&gt;&lt;p&gt;如果用户根本没有填写表单，就直接点击提交按钮，会发生什么？在上面的实例中，PHP 依然会乖乖地把空内容插入，而这显然是垃圾信息，不是我们需要的。所以，需要在插入前检查被插入的变量是否为空。例如：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// 插入操作&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里出现了 &lt;code&gt;empty()&lt;/code&gt; 函数，用于检查内容是否为空。注意这里使用 &lt;code&gt;isset()&lt;/code&gt; 是无效的，因为 &lt;code&gt;isset()&lt;/code&gt; 检查的是是否“被设置”，而被设置为空也属于被设置。&lt;/p&gt;&lt;h3 id=&quot;span-idi-7span&quot;&gt;&lt;span id=&quot;i-7&quot;&gt;错误提示&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;用户输入有误时，上面的改进除了不执行SQL查询，并没有多少直观上的变化。用户不会收到任何信息表明他们的填写是不合适的。所以我们要在这时产生一些提示，引导用户正确填写表单。&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// 插入操作&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;请填写全部内容后再提交&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&quot;sql-&quot;&gt;防范 SQL 注入攻击&lt;/h3&gt;&lt;p&gt;我们执行的 SQL语句中包含变量，执行的时候会直接把变量内容替换进去。而如果攻击者在输入框中输入一些危险的字符（通常包含 SQL 注释符 &lt;code&gt;--&lt;/code&gt;，以及其他预先精心设置的内容），就可能导致该次 SQL 查询完全被改写成攻击者需要的意思。为了防范这种攻击，我们需要对可能存在的危险字符进行过滤和转义，较为便捷的方法是使用两个函数。改进后的部分如下：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;mysqli_real_escape_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$dbc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_POST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;username&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]));&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$info&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;mysqli_real_escape_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$dbc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_POST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;info&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]));&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&quot;section-6&quot;&gt;粘性表单&lt;/h3&gt;&lt;p&gt;如果用户第一次填写失败，他们希望能保留已经填写好的内容，只做些修改就好了。这需要使用粘性表单技术。要实现，只需要稍稍改动 HTML 表单部分的代码：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;label&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;for=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;username&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;用户名:&lt;span class=&quot;nt&quot;&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;text&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;username&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;user&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;&amp;lt;?php if (!empty($user)) echo $user; ?&amp;gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;显而易见，如果用户填写后因为某些原因没有提交而是回到了这个表单，并且之前填写了 user 字段的内容，那么此时 &lt;code&gt;$user&lt;/code&gt; 变量已经被赋值了。那么就会在 HTML 表单显示这些内容，避免用户再次输入。&lt;/p&gt;&lt;h2 id=&quot;section-7&quot;&gt;构造一个注册页面&lt;/h2&gt;&lt;p&gt;虽然上面说了很多，但是仅仅满足了我们最基本的输入要求。许多时候我们需要更为复杂的功能。举例来说，要写一个注册页面，必须检查用户名是否重复，还要对密码采取某种技术加密以保证安全。&lt;/p&gt;&lt;h3 id=&quot;section-8&quot;&gt;检查用户是否重复&lt;/h3&gt;&lt;p&gt;基本原理就是，根据需要判重的字段（例如用户名）去数据库搜索。如果发现结果则用户名重复，如果没有找到则允许注册。需要一个新函数 &lt;code&gt;mysqli_num_rows()&lt;/code&gt;，返回 &lt;code&gt;SELECT&lt;/code&gt; 语句得到的行数，根据其是否等于 0 进行判断。&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$dbc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;mysqli_connect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;DB_HOST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DB_USER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DB_PASS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DB_NAME&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$query&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;SELECT * FROM table_name WHERE user_name = &amp;#39;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$user&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;#39;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;mysqli_query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$dbc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;mysqli_num_rows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// 把内容插入数据库&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;注册成功&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;mysqli_close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$dbc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;用户名已被占用，请重新选择用户名&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;把 &lt;code&gt;$user&lt;/code&gt; 清空是为了配合粘性表单。&lt;/p&gt;&lt;p&gt;需要说明的是 &lt;code&gt;exit()&lt;/code&gt;; 函数，它会立刻终止 PHP 的运行。因为用户已经注册成功，没有必要执行后面的任何代码，所以使用这个函数。写自己的程序的时候可以亲自试验是否需要这一行、PHP 和 HTML 在 php 中的顺序不同有何影响。我通常的做法是把 PHP 代码放在前面，HTML 代码放在后面。&lt;/p&gt;&lt;h3 id=&quot;section-9&quot;&gt;对密码进行加密存储&lt;/h3&gt;&lt;p&gt;明文存储密码是对用户很不负责的，不仅数据库管理员可以看到密码，一旦数据库泄漏，密码就会被公开。所以，我们应该加密存储用户密码。在 PHP 中，可以使用 &lt;code&gt;sha1()&lt;/code&gt; 函数进行加密（sha 即 secure hash algorithm 的首字母缩写），它是一种不可逆的加密，加密后会生成定长的一段字符串，并且是无法由这段字符串还原原密码的。&lt;/p&gt;&lt;p&gt;加密的原理是，用户输入密码后，利用 PHP 把 hash 过的密码储存在数据库中。用户登陆的时候，把用户输入的密码进行 hash 运算，之后和数据库中的进行比对。&lt;/p&gt;&lt;p&gt;使用方法如下：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;x&quot;&gt;sha1($password)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;cookie&quot;&gt;识别用户登录：Cookie&lt;/h2&gt;&lt;p&gt;只注册没有用，必须添加登录功能。登录功能可以使用 Cookie 来实现。这里假定你已经了解 Cookie 的基础知识，只说如何实现。&lt;/p&gt;&lt;h3 id=&quot;cookie-1&quot;&gt;设置 Cookie&lt;/h3&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;setcookie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;user_type&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$user_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;60&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;60&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;24&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;上面的代码用来设置 Cookie，其中函数的第一个参数为 Cookie 名称，第二个参数为数值（这里用一个变量传递），第三个参数为过期时间，单位秒。示例为一个月。&lt;/p&gt;&lt;p&gt;可以用设置多个 Cookie 来存储许多内容，例如用户 ID、用户组（管理员还是普通用户）等。&lt;/p&gt;&lt;h3 id=&quot;cookie-2&quot;&gt;验证 Cookie&lt;/h3&gt;&lt;p&gt;用户登陆后，我们可以设置一个 Cookie 来存储登录信息（即哪个用户登陆的），然后通过检查这个 Cookie 来设定相应功能。&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;isset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_COOKIE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;user_type&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$user_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_COOKIE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;user_type&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&quot;cookie-3&quot;&gt;删除 Cookie&lt;/h3&gt;&lt;p&gt;要删除 Cookie，只需要把过期时间设定在过去。&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;setcookie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;user_type&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3600&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;不要问我为什么设定在过去一个小时，设定几个小时都没问题。&lt;/p&gt;&lt;h3 id=&quot;cookie-&quot;&gt;Cookie 的安全性&lt;/h3&gt;&lt;p&gt;设置 Cookie 有其潜在的危险。由于 Cookie 是保存在用户本地的，所以用户完全可以通过篡改 Cookie 来达到他们的目的。所以，把 Cookie 的值设置得“通俗易懂”不是一个好主意。例如，我们要用 Cookie 来保存登陆的用户名，如果单纯把这个用户名存入 Cookie，那么攻击者会很容易通过修改成他人的用户名来伪造 Cookie 登陆。所以，我们需要其他的手段来防止这一点。&lt;/p&gt;&lt;p&gt;我的做法是，用户注册的时候，把用户名按一定手段进行变换，然后使用 &lt;code&gt;SHA()&lt;/code&gt; 函数加密生成一个用户密钥，然后把这个密钥储存进数据库。登陆时，再把这个密钥存储到 Cookie 中，通过检查 Cookie 中的密钥和数据库中用户密钥的匹配情况判定是哪位用户登录。这样，只要你的用户名变换方法不泄露，攻击者就很难按他们的想法伪造 Cookie。&lt;/p&gt;&lt;h2 id=&quot;get-&quot;&gt;使用 GET 方法&lt;/h2&gt;&lt;p&gt;在网页间传递信息除了刚才介绍的 &lt;code&gt;POST&lt;/code&gt; 方法外，还有 &lt;code&gt;GET&lt;/code&gt; 方法。&lt;code&gt;GET&lt;/code&gt; 方法是通过 URL 来完成信息传递的。例如，构造下列网址：&lt;/p&gt;&lt;pre&gt;&lt;code&gt;http://www.renfei.org/index.php?id=2&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;网址最后有 &lt;code&gt;?id=2&lt;/code&gt; 标记。这个信息会储存在&lt;code&gt;$_GET[&#39;id&#39;]&lt;/code&gt; 这个超级全局变量中，并且可以在 PHP 中使用：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;isset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_GET&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;id&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_GET&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;id&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// code goes there&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个例子中我们把 &lt;code&gt;2&lt;/code&gt; 赋给了变量 &lt;code&gt;$id&lt;/code&gt;。当然，也可以构造这样的网址：&lt;/p&gt;&lt;pre&gt;&lt;code&gt;http://www.renfei.org/index.php?id=2&amp;amp;message=10&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;除了多一个可以使用的 &lt;code&gt;$_GET[&#39;message&#39;]&lt;/code&gt; 以外没有任何不同。&lt;/p&gt;&lt;p&gt;这个特性的用处之一就是可以根据网址的不同，配合数据库查询，返回不同的网页内容。例如我做的在线问答系统，就是根据 &lt;code&gt;problem_id&lt;/code&gt; 来返回不同题目的。&lt;/p&gt;&lt;p&gt;注意，由于 &lt;code&gt;GET&lt;/code&gt; 方法的数值是不可靠的（用户可以手动构造 URL 来传递他们想要的参数），所以应该仅仅用它来做一些无关痛痒的事情（例如显示不同的页面内容）。这里我并没有强调 &lt;code&gt;GET&lt;/code&gt; 方法的数值是“透明”的：虽然 &lt;code&gt;POST&lt;/code&gt; 方法的数值不会显示在 URL 中，但是它还是会通过 HTTP Header 发送到服务器，用许多插件和小工具都可以查看 HTTP Header 信息。&lt;/p&gt;&lt;p&gt;另外，如果你的表单是用来上传文件的，那么估计你会更喜欢 &lt;code&gt;POST&lt;/code&gt; 方法：因为  &lt;code&gt;GET&lt;/code&gt; 方法得到的 URL 可能会很长，甚至超过浏览器的限制！&lt;/p&gt;&lt;h2 id=&quot;section-10&quot;&gt;使用模板&lt;/h2&gt;&lt;p&gt;最后一部分，来讲一下使用模板构造一个网站。&lt;/p&gt;&lt;p&gt;事实上，网站的每个页面中，有许多部分是完全相同的，例如数据库连接常量（就是那些&lt;code&gt;define&lt;/code&gt;语句）以及每一页的 header 和 footer 部分等。这样，我们没必要在每一页内写相同的代码。除了麻烦和浪费空间以外，还有一点很重要的原因，就是修改的时候工作量很大。&lt;/p&gt;&lt;p&gt;PHP 中 &lt;code&gt;require_once&lt;/code&gt; 语句作用就是把其他文件的内容插入此处。例如，我们可以创建一个&lt;code&gt;define.php&lt;/code&gt;，把&lt;code&gt;define&lt;/code&gt;语句全部写到里面，并在每个页面顶部添加如下语句：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;require_once&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;define.php&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这样一来，会把 &lt;code&gt;define.php&lt;/code&gt; 中的内容插入当前位置。同理，我们可以建立一个 &lt;code&gt;header.php&lt;/code&gt; 和 &lt;code&gt;footer.php&lt;/code&gt;，写好页面的头部、底部之后在每个其他页面导入就可以了。&lt;/p&gt;&lt;h2 id=&quot;php&quot;&gt;PHP的错误处理&lt;/h2&gt;&lt;h3 id=&quot;section-11&quot;&gt;分级的错误信息&lt;/h3&gt;&lt;p&gt;最后来讲一下 PHP 的错误处理机制。如果你写了有错误的 PHP 代码，那么运行的时候系统会自动生成一些错误提示信息并且打印到屏幕上，以提醒用户修复。通常，这些错误信息是分级的。首先，是 &lt;code&gt;notice&lt;/code&gt;。如果屏幕出现了 &lt;code&gt;notice: (...)&lt;/code&gt; 的提示说明你有需要修复的小问题（你没有完全按照规则进行），不过问题不大，代码还是会继续执行完毕。而 &lt;code&gt;warning&lt;/code&gt; 则更严重一些，如果出现 &lt;code&gt;warning&lt;/code&gt;，你可能需要思考一下你是否真的知道自己在做什么，并作出修改。但是，程序仍然会运行。如果出现了 &lt;code&gt;error&lt;/code&gt;，那么 PHP 是在跟你说：你是个白痴；这种代码无法执行，程序的运行会中止。&lt;/p&gt;&lt;p&gt;在写 PHP 程序的时候，我们需要这些错误提示来帮助我们改正错误，但是当产品发布的时候，开发人员往往倾向于隐藏错误提示：用户收到这些信息是很让人恼火的，而且，让他人知道你的代码有什么漏洞总归不是一个好主意，因为这可能被某些图谋不轨的攻击者加以利用。&lt;/p&gt;&lt;h3 id=&quot;suppression-operator&quot;&gt;Suppression Operator&lt;/h3&gt;&lt;p&gt;有时，为了代码的简洁性考虑我们可能会故意犯一些无关痛痒的小错误。例如，如果 &lt;code&gt;$_GET&lt;/code&gt; 中的某一个元素不一定总会被提交到 PHP，那么理论上应该使用 &lt;code&gt;isset()&lt;/code&gt; 函数来进行检测。但是，如果你觉得到处使用这个函数太麻烦了，可以省略 &lt;code&gt;isset()&lt;/code&gt; 函数而直接使用这个元素，只不过如果它没有被设置的话会返回一个 &lt;code&gt;notice&lt;/code&gt; 错误信息（类似于 C/C++ 中的变量未声明）。这时，为了忽略这一条信息，可以使用错误抑制操作符 &lt;code&gt;@&lt;/code&gt;。例如：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_GET&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;opt&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// code goes here...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;section-12&quot;&gt;其他提示&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;尽管没有特别强调，但是有几个函数是需要灵活掌握和使用的，例如 &lt;code&gt;exit()&lt;/code&gt;。它可以立刻结束 PHP 程序的运行。例如，有的页面需要一定用户权限才能访问，则可以把验证权限的代码放在页面顶端，如果验证失败则显示错误信息并调用 &lt;code&gt;exit()&lt;/code&gt; 函数。&lt;/li&gt;&lt;li&gt;当一个 SQL 连接的使命完成后，不要忘了用 &lt;code&gt;mysqli_close()&lt;/code&gt; 关闭它。&lt;/li&gt;&lt;li&gt;设计 SQL 数据库的结构是一件非常重要的事情，设计的原则是高效且便于查询。一旦你的数据库充满各种信息，再想更改它的结构就会变得有些困难。&lt;/li&gt;&lt;li&gt;SQL 的知识这里介绍得不多。它有许多特性，比如默认值、主键等。默认值的意思是如果不设定，那么该字段采用默认值；主键则规定该字段每行是不能重复的。默认值除了固定字符以外，还可以设定为时间，甚至自增。例如，要建立一个用户数据库，为每个用户分配一个唯一 ID，则可以把数据库中的 ID 字段设为 AUTO INCREMENT，这样每次不用手工维护这个字段，只要新增一行，这个字段的数值就增 1（默认从 1 开始），很方便。 一般会把这种 ID 字段设为主键。&lt;/li&gt;&lt;li&gt;本来打算简要介绍一下 PHP 和 MySQL 的，但是一写就是 7000 字。即使如此，本文介绍的所有特性也仅仅是构建一个动态网站最基本的知识，而且许多非重要的知识并没有介绍。你应该通过书籍更深入地学习。&lt;/li&gt;&lt;li&gt;额外推荐 PHP 的文档，对新手非常友好，值得一看：&lt;a href=&quot;http://us3.php.net/manual/en/&quot;&gt;http://us3.php.net/manual/en/&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;</description><pubDate>Mon, 31 Dec 2012 00:00:00 +0800</pubDate><link>https://www.renfei.org/blog/build-dynamic-website-with-php-mysql.html</link><guid isPermaLink="true">https://www.renfei.org/blog/build-dynamic-website-with-php-mysql.html</guid></item><item><title>C++ string 字符串函数详解</title><description>&lt;h2 id=&quot;section&quot;&gt;运算符重载&lt;/h2&gt;&lt;ol&gt;&lt;li&gt;+ 和 +=：连接字符串&lt;/li&gt;&lt;li&gt;=：字符串赋值&lt;/li&gt;&lt;li&gt;&amp;gt;、&amp;gt;=、&amp;lt; 和 &amp;lt;=：字符串比较（例如a &amp;lt; b, aa &amp;lt; ab）&lt;/li&gt;&lt;li&gt;==、!=：比较字符串&lt;/li&gt;&lt;li&gt;&amp;lt;&amp;lt;、&amp;gt;&amp;gt;：输出、输入字符串&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;注意：使用重载的运算符 + 时，必须保证前两个操作数至少有一个为 string 类型。例如，下面的写法是不合法的：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot; data-lang=&quot;cpp&quot;&gt;&lt;span class=&quot;cp&quot;&gt;#include &amp;lt;iostream&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;#include &amp;lt;string&amp;gt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;cat&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;apple&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;boy&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// illegal!&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;section-1&quot;&gt;查找&lt;/h2&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot; data-lang=&quot;cpp&quot;&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cin&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;ab&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;//返回字符串 ab 在 str 的位置&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;ab&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;//在 str[2]~str[n-1] 范围内查找并返回字符串 ab 在 str 的位置&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rfind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;ab&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;//在 str[0]~str[2] 范围内查找并返回字符串 ab 在 str 的位置&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;//first 系列函数&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;find_first_of&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;apple&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;//返回 apple 中任何一个字符首次在 str 中出现的位置&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;find_first_of&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;apple&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;//返回 apple 中任何一个字符首次在 str[2]~str[n-1] 范围中出现的位置&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;find_first_not_of&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;apple&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;//返回除 apple 以外的任何一个字符在 str 中首次出现的位置&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;find_first_not_of&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;apple&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;//返回除 apple 以外的任何一个字符在 str[2]~str[n-1] 范围中首次出现的位置&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;//last 系列函数&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;find_last_of&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;apple&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;//返回 apple 中任何一个字符最后一次在 str 中出现的位置&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;find_last_of&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;apple&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;//返回 apple 中任何一个字符最后一次在 str[0]~str[2] 范围中出现的位置&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;find_last_not_of&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;apple&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;//返回除 apple 以外的任何一个字符在 str 中最后一次出现的位置&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;find_last_not_of&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;apple&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;//返回除 apple 以外的任何一个字符在 str[0]~str[2] 范围中最后一次出现的位置&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;//以上函数如果没有找到，均返回string::npos&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;npos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;section-2&quot;&gt;子串&lt;/h2&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot; data-lang=&quot;cpp&quot;&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;substr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//返回 [3] 及以后的子串&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;substr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//返回 str[2]~str[2+(4-1)] 子串(即从[2]开始4个字符组成的字符串)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;section-3&quot;&gt;替换&lt;/h2&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot; data-lang=&quot;cpp&quot;&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;sz&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;//返回把 [2]~[2+(4-1)] 的内容替换为 &amp;quot;sz&amp;quot; 后的新字符串&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;abcd&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;//返回把 [2]~[2+(4-1)] 的内容替换为 &amp;quot;abcd&amp;quot; 的前3个字符后的新字符串&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;section-4&quot;&gt;插入&lt;/h2&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot; data-lang=&quot;cpp&quot;&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;sz&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;//从 [2] 位置开始添加字符串 &amp;quot;sz&amp;quot;，并返回形成的新字符串&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;abcd&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;//从 [2] 位置开始添加字符串 &amp;quot;abcd&amp;quot; 的前 3 个字符，并返回形成的新字符串&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;abcd&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;//从 [2] 位置开始添加字符串 &amp;quot;abcd&amp;quot; 的前 [2]~[2+(3-1)] 个字符，并返回形成的新字符串&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;section-5&quot;&gt;追加&lt;/h2&gt;&lt;p&gt;除了用重载的 &lt;code&gt;+&lt;/code&gt; 操作符，还可以使用函数来完成。&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot; data-lang=&quot;cpp&quot;&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;push_back&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sc&quot;&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;//在 str 末尾添加字符&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;abc&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;//在 str 末尾添加字符串&amp;quot;abc&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;section-6&quot;&gt;删除&lt;/h2&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot; data-lang=&quot;cpp&quot;&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;erase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;//删除 [3] 及以后的字符，并返回新字符串&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;erase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;//删除从 [3] 开始的 5 个字符，并返回新字符串&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;section-7&quot;&gt;交换&lt;/h2&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot; data-lang=&quot;cpp&quot;&gt;&lt;span class=&quot;n&quot;&gt;str1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;swap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;//把 str1 与 str2 交换&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;section-8&quot;&gt;其他&lt;/h2&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot; data-lang=&quot;cpp&quot;&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;//返回字符串长度&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;//返回字符串长度&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;//检查 str 是否为空，为空返回 1，否则返回 0&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;//存取 str 第 n + 1 个字符&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;at&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;//存取 str 第 n + 1 个字符（如果溢出会抛出异常）&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;section-9&quot;&gt;实例&lt;/h2&gt;&lt;h3 id=&quot;section-10&quot;&gt;查找给定字符串并把相应子串替换为另一给定字符串&lt;/h3&gt;&lt;p&gt;string 并没有提供这样的函数，所以我们自己来实现。由于给定字符串可能出现多次，所以需要用到 &lt;code&gt;find()&lt;/code&gt; 成员函数的第二个参数，每次查找之后，从找到位置往后继续搜索。直接看代码（这个函数返回替换的次数，如果返回值是 0 说明没有替换）：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot; data-lang=&quot;cpp&quot;&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;str_replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;counter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size_type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;npos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&quot;section-11&quot;&gt;从给定字符串中删除一给定字串&lt;/h3&gt;&lt;p&gt;方法和上面相似，内部使用 &lt;code&gt;erase()&lt;/code&gt; 完成。代码：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot; data-lang=&quot;cpp&quot;&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;str_erase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;counter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size_type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;npos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;erase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&quot;section-12&quot;&gt;给定一字符串和一字符集，从字符串剔除字符集中的任意字符&lt;/h3&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot; data-lang=&quot;cpp&quot;&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;str_wash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;counter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size_type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;find_first_of&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;npos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;erase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description><pubDate>Sat, 08 Dec 2012 00:00:00 +0800</pubDate><link>https://www.renfei.org/blog/introduction-to-cpp-string.html</link><guid isPermaLink="true">https://www.renfei.org/blog/introduction-to-cpp-string.html</guid></item><item><title>北京王府井新 Apple Store 开业</title><description>&lt;p&gt;据说是亚洲最大的一家苹果店，10 月 20 日早晨 9 点正式开业。&lt;/p&gt;&lt;p&gt;排队的人不算特别多，而且Apple已经提前准备好了这种栏杆。到的时候有一个醒目的“队尾”标志，一看就知道从哪里开始排，还是比较人性化的。&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;//img.renfei.org/2012/12/psb.jpeg&quot; alt=&quot;&quot; title=&quot;Apple Store&quot; /&gt;&lt;/p&gt;&lt;p&gt;正门&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;//img.renfei.org/2012/12/psb-1.jpeg&quot; alt=&quot;&quot; title=&quot;Apple Store&quot; /&gt;&lt;/p&gt;&lt;p&gt;排队的地方到处都是保安&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;//img.renfei.org/2012/12/psb-2.jpeg&quot; alt=&quot;&quot; title=&quot;Apple Store&quot; /&gt;&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;//img.renfei.org/2012/12/psb-3.jpeg&quot; alt=&quot;&quot; title=&quot;Apple Store&quot; /&gt;&lt;/p&gt;&lt;p&gt;这个大叔…神似乔布斯=_=&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;//img.renfei.org/2012/12/psb-4.jpeg&quot; alt=&quot;&quot; title=&quot;Apple Store&quot; /&gt;&lt;/p&gt;&lt;p&gt;不得不提一下穿蓝色工作服的 Apple Store 工作人员，非常有激情。每次进去一拨人都会有工作人员列队欢迎，一边鼓掌一边喊，有时喊“早上好”，有时喊“欢迎你”，有时喊“iPhone”&lt;/p&gt;&lt;p&gt;这张图是从视频截下来的&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;//img.renfei.org/2012/12/psb.png&quot; alt=&quot;&quot; title=&quot;Apple Store Stuff&quot; /&gt;&lt;/p&gt;&lt;p&gt;内景&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;//img.renfei.org/2012/12/psb-5.jpeg&quot; alt=&quot;&quot; title=&quot;Apple Store&quot; /&gt;&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;//img.renfei.org/2012/12/psb-6.jpeg&quot; alt=&quot;&quot; title=&quot;Apple Store&quot; /&gt;&lt;/p&gt;&lt;p&gt;传说中的防走光楼梯&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;//img.renfei.org/2012/12/psb-7.jpeg&quot; alt=&quot;&quot; title=&quot;psb-7&quot; /&gt;&lt;/p&gt;</description><pubDate>Sat, 20 Oct 2012 00:00:00 +0800</pubDate><link>https://www.renfei.org/blog/wangfujing-new-apple-store.html</link><guid isPermaLink="true">https://www.renfei.org/blog/wangfujing-new-apple-store.html</guid></item><item><title>分享与推荐：我常用的 Safari 扩展</title><description>&lt;p&gt;&lt;img src=&quot;//img.renfei.org/2012/08/safari-extensions.png&quot; alt=&quot;safari扩展推荐&quot; title=&quot;safari-extensions&quot; /&gt;&lt;/p&gt;&lt;p&gt;我目前一共使用 7 个 Safari 扩展，觉得十分有用，推荐给大家。&lt;/p&gt;&lt;h2 id=&quot;duplicate-tab-button-20&quot;&gt;Duplicate Tab Button 2.0&lt;/h2&gt;&lt;p&gt;这个插件的作用是为当前标签建立一个副本，可以选择新标签的位置、是否自动激活新标签等，有时比较管用。安装后会在工具栏出现一个按钮。&lt;/p&gt;&lt;h2 id=&quot;ad-block&quot;&gt;Ad-Block&lt;/h2&gt;&lt;p&gt;著名的过滤广告插件，FireFox、Chrome 都有同名插件，非常好用。可以订阅中国专用过滤菜单，从而过滤掉绝大部分广告。也支持设定自定义过滤规则（比如我就用它过滤微博边栏的一些无聊新闻）。&lt;/p&gt;&lt;h2 id=&quot;gdirectlinks&quot;&gt;gDirectLinks&lt;/h2&gt;&lt;p&gt;它的作用是禁用 Google 搜索结果的跳转机制，安装启用后搜索结果的链接都会直接链向目标页面，有时出现连接被重置时，可以使得当次搜索结果还能用⋯⋯另外也可以设置图片搜索直接链向原图。&lt;/p&gt;&lt;h2 id=&quot;ultimate-status-bar&quot;&gt;Ultimate Status Bar&lt;/h2&gt;&lt;p&gt;它的作用是给Safari提供一个状态栏，来显示链接地址等。有许多样式和设置项可供定制。我选择的皮肤为“Smooth”，Zoom Level 在大约 29% 的位置，语言设置为英语，其显示效果如下，相当精致。&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;//img.renfei.org/2012/08/屏幕快照-2012-08-11-下午7.12.01.png&quot; alt=&quot;Ultimate Status Bar&quot; title=&quot;Ultimate Status Bar&quot; /&gt;&lt;/p&gt;&lt;h2 id=&quot;keyword-search-12&quot;&gt;Keyword Search 1.2&lt;/h2&gt;&lt;p&gt;地址栏搜索增强插件，可以根据你的关键字来切换搜索引擎。它的设置界面如下：&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;//img.renfei.org/2012/08/屏幕快照-2012-08-11-下午7.16.00.png&quot; alt=&quot;safari扩展keyword search&quot; title=&quot;safari扩展keyword search&quot; /&gt;&lt;/p&gt;&lt;p&gt;如果按照我的设置，那么想搜索中文 Wikipedia 的内容，只需在搜索栏输入&lt;code&gt;wiki 关键字&lt;/code&gt;即可；想使用百度搜索，只需在搜索栏输入&lt;code&gt;baidu 关键字&lt;/code&gt;即可。如果的输入不是以&lt;code&gt;wiki&lt;/code&gt;或&lt;code&gt;baidu&lt;/code&gt;开头，那么还会调用系统默认的搜索引擎（Google）。&lt;/p&gt;&lt;h2 id=&quot;shortly&quot;&gt;Shortly&lt;/h2&gt;&lt;p&gt;&lt;img src=&quot;//img.renfei.org/2012/08/屏幕快照-2012-08-11-下午7.22.03.png&quot; alt=&quot;safari扩展shortly&quot; title=&quot;safari扩展shortly&quot; /&gt;&lt;/p&gt;&lt;p&gt;这是一个网址缩短工具，可以选择预置的 goo.gl、TinyURL 或 bit.ly 服务。安装后会在工具栏出现一个按钮，点按后片刻会显示当前网址缩短后的短网址，相当方便。&lt;/p&gt;&lt;h2 id=&quot;ease-link&quot;&gt;Ease link&lt;/h2&gt;&lt;p&gt;可以把迅雷、QQ 旋风、Flashget、RayFile、纳米盘和 QQ 的专用链还原成通用的链接，支持自动下载。&lt;a href=&quot;http://code.google.com/p/easelink/wiki/User_Guide_Safari?wl=en-US&quot; title=&quot;Ease Link: User_Guide_Safari&quot;&gt;详细介绍见这里&lt;/a&gt;。&lt;/p&gt;</description><pubDate>Sat, 11 Aug 2012 00:00:00 +0800</pubDate><link>https://www.renfei.org/blog/my-safari-6-extension-recommendation.html</link><guid isPermaLink="true">https://www.renfei.org/blog/my-safari-6-extension-recommendation.html</guid></item><item><title>Mac OS X Terminal 101：终端使用初级教程</title><description>&lt;p&gt;最近学习苹果认证的《Mac OS X Support Essentials》教程，看到 Command Line 一节有很多实用的知识，下面选取一部分翻译 + 笔记，整理成此文。&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;你可以整天驾驶汽车而不用知道如何修理它们，但是如果你希望当一个维护员，你就需要知道事情是如何运作的。同样的事情也发生在了 Mac OS X 上；你可以一直使用 Mac 而不用知道如何修理它，但是如果你想对系统做一些维护或解决一些问题，那么你需要知道如何使用 command-line。&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;section&quot;&gt;为什么要使用命令行/如何开启命令行？&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;许多功能在图形界面不提供，只有通过命令行来实现。&lt;/li&gt;&lt;li&gt;Finder会隐藏许多你不太会需要的文件，然而 command line 会允许你访问所有文件。&lt;/li&gt;&lt;li&gt;通过 command line 可以远程访问你的 Mac（利用 SSH）。&lt;/li&gt;&lt;li&gt;administrators 用户可以通过 &lt;code&gt;sudo&lt;/code&gt; 命令获得 root 用户权限。&lt;/li&gt;&lt;li&gt;通过 command-line script 可以使工作更高效。&lt;/li&gt;&lt;li&gt;Terminal（终端）程序可以在“实用工具”里找到。&lt;/li&gt;&lt;li&gt;如果你开启手动输入用户名登陆模式，登陆时在用户名处输入 &lt;code&gt;&amp;gt;console&lt;/code&gt; 可以直接进入命令行界面。随后你仍然需要登录到一个账户。&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;command-line&quot;&gt;初识Command Line&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;许多命令会花费一些时间来执行，然而这中间不会给出任何提示或者进度条。一般结束后会出现一个“用户名$”的标记。如果没有出现，那么说明最后一条命令正在执行。&lt;/li&gt;&lt;li&gt;一条命令包括 Command Name、Options、Arguments、Extras 四个部分，但是后三个部分有时是可选的。Options 部分用&lt;code&gt;-&lt;/code&gt;作为前导符。其中许多命令的 Options 部分只包含单个字母，这时可以合并。例如，&lt;code&gt;ls -lA&lt;/code&gt;和&lt;code&gt;ls -l -A&lt;/code&gt;是等效的。Arguments 部分用来细化这个命令或指定这个命令具体的实施对象，Extras 部分则用来进一步实现其他功能。&lt;/li&gt;&lt;li&gt;&lt;p&gt;举例：下列命令包含前三个部分，用于删除 Junk 这个程序。&lt;/p&gt;&lt;p&gt;&lt;code&gt;michelle$ rm -R /Applications/Junk.app&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;如果你输入了一些错误的命令，系统会返回一些错误信息。但是系统却不会阻止你做傻事（例如删除整个用户文件夹）。&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;man-&quot;&gt;关于 man 命令&lt;/h2&gt;&lt;p&gt;虽然有上千条命令，每条命令还有许多可选参数和具体的使用方式，但是你却不需要记住这些命令。你只需要记住一个：&lt;code&gt;man&lt;/code&gt;&lt;/p&gt;&lt;p&gt;大多数命令都会包含一个使用指南，会告诉你任何你需要知道的关于这个命令的所有细节，在命令行中输入 &lt;code&gt;man command-name&lt;/code&gt; 即可获取。例如，你想知道&lt;code&gt;ls&lt;/code&gt;这个命令怎么使用，输入&lt;code&gt;man ls&lt;/code&gt;即可进入使用指南页面。&lt;/p&gt;&lt;p&gt;使用指南往往很长，所以你可以使用&lt;span class=&quot;keybtn arrow&quot;&gt;▲&lt;/span&gt;（上箭头）或&lt;span class=&quot;keybtn arrow&quot;&gt;▼&lt;/span&gt;（下箭头）来上下移动，使用&lt;span class=&quot;keybtn space&quot;&gt;　&lt;/span&gt;来翻页，输入&lt;code&gt;/&lt;/code&gt;和关键字来按照关键字搜索，按&lt;span class=&quot;keybtn char&quot;&gt;Q&lt;/span&gt;来退出使用指南页面。&lt;/p&gt;&lt;p&gt;那么——如果你连命令名称都不知道怎么办呢？输入&lt;code&gt;man -k&lt;/code&gt;和关键字来对整个使用指南数据库进行搜索。&lt;/p&gt;&lt;h2 id=&quot;section-1&quot;&gt;命令行，文件和路径&lt;/h2&gt;&lt;blockquote&gt;&lt;p&gt;如果知道如何使用命令是掌握 command line 的第一步，那么第二步就是学习如何在 command line 中使用文件路径。如果你掌握了文件路径，你将会发现这比使用 Finder 更加快捷。&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;command line 工具是大小写敏感的，并且对于文件名，必须包括扩展名。例如，你想找iTunes这个程序，输入&lt;code&gt;itunes&lt;/code&gt;是无效的，必须输入&lt;code&gt;iTunes.app&lt;/code&gt;。&lt;/li&gt;&lt;li&gt;Mac OS传统上喜欢使用“文件夹”（folders）这个名称，但是在 command line 中，主要使用“目录”（directory）这个词。这和 UNIX 是一致的。&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&quot;section-2&quot;&gt;两种路径：绝对路径和相对路径&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;绝对路径：完整描述一个文件的位置，总是以斜杠（&lt;code&gt;/&lt;/code&gt;）（forward slash）开头。例如&lt;code&gt;/Users/michelle/Public/Drop Box&lt;/code&gt;。&lt;/li&gt;&lt;li&gt;相对路径：只描述一部分位置信息，它和你在 command line 目前的目录有关。当你打开新的 Terminal 程序时，command line 会话的目录应该是你的 home folder。这时上面例子文件夹的相对路径写作&lt;code&gt;Public/Drop Box&lt;/code&gt;。显然它从当前目录开始。和html类似，你也可以使用两个点（“&lt;code&gt;..&lt;/code&gt;”）来代表父目录，这样你就可以用相对路径表示上级或同级目录了。例如你可以输入&lt;code&gt;cd ..&lt;/code&gt;甚至&lt;code&gt;cd ../..&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&quot;section-3&quot;&gt;切换到其他路径和目录&lt;/h3&gt;&lt;p&gt;如果你想将当前 command line 会话切换到其他目录，需要用到三个命令：&lt;code&gt;pwd&lt;/code&gt;，&lt;code&gt;ls&lt;/code&gt;和&lt;code&gt;cd&lt;/code&gt;。&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;code&gt;pwd&lt;/code&gt;的含义是“print working directory”，会显示当前目录的绝对路径。&lt;/li&gt;&lt;li&gt;&lt;code&gt;ls&lt;/code&gt;的含义是“list directory contents”，它会列出当前目录的内容。这个命令还有其他参数可选。&lt;/li&gt;&lt;li&gt;&lt;code&gt;cd&lt;/code&gt;的含义是“change directory”，它会改变当前目录到你指定的目录。如果你不指定，则会返回你的 home folder。&lt;/li&gt;&lt;/ol&gt;&lt;h3 id=&quot;section-4&quot;&gt;处理特殊字符&lt;/h3&gt;&lt;p&gt;如果目录中有特殊字符（空格，括号，引号，&lt;code&gt;[]&lt;/code&gt;，&lt;code&gt;!&lt;/code&gt;，&lt;code&gt;$&lt;/code&gt;，&lt;code&gt;&amp;amp;&lt;/code&gt;，&lt;code&gt;*&lt;/code&gt;，&lt;code&gt;;&lt;/code&gt;，&lt;code&gt;|&lt;/code&gt;，&lt;code&gt;\&lt;/code&gt;），那么直接输入空格会造成系统识别困难，必须使用特殊的语法来表示这些字符。例如上例中，空格前添加反斜杠“&lt;code&gt;\&lt;/code&gt;”（back slash）即可：&lt;code&gt;cd Punlic/Drop\ Box/&lt;/code&gt;。除了反斜杠，也可以用引号的方法：&lt;code&gt;cd &quot;Public/Drop Box&quot;。&lt;/code&gt;&lt;/p&gt;&lt;p&gt;——如果不想手动输入，也可以把文件从 Finder 拖到 Terminal 窗口来创建绝对路径，这会方便一些，因为上面提到的所有特殊字符在拖动后都会自动变成系统可识别的表示方法。其实，更有效率的解决方案是使用 Tab Complete 功能。&lt;/p&gt;&lt;p&gt;Tab Complete 是 command line 中最能给你节省时间的特性之一，利用它的自动完成文件、目录名称功能还可以防止你输入错误。使用&lt;code&gt;cd&lt;/code&gt;进入你的 home folder，使用&lt;code&gt;cd P&lt;/code&gt;命令，然后按下&lt;span class=&quot;keybtn tab&quot;&gt;tab&lt;/span&gt;按键。你可能会听到错误音，因为你的 home folder 内有多个 P 开头的文件夹。再按一次&lt;span class=&quot;keybtn tab&quot;&gt;tab&lt;/span&gt;，Terminal 将会为你列出 P 开头的两个文件夹：Public 和 Pictures。按&lt;span class=&quot;keybtn char&quot;&gt;U&lt;/span&gt;，再按&lt;span class=&quot;keybtn tab&quot;&gt;tab&lt;/span&gt;，Terminal 则会自动为你补全&lt;code&gt;Public/&lt;/code&gt;。Tab complete 同样会处理那些特殊字符。注意，这会在末尾保留&lt;code&gt;/&lt;/code&gt;符号，大部分时候这没问题，但如果出错，移除多余的&lt;code&gt;/&lt;/code&gt;试一试。&lt;/p&gt;&lt;p&gt;另外，鄂化符&lt;code&gt;~&lt;/code&gt;（tilde）在command line 中可以代表当前用户的 home folder。例如&lt;code&gt;~/Public/Drop\ Box/&lt;/code&gt;是合法的。&lt;/p&gt;&lt;h3 id=&quot;section-5&quot;&gt;查看隐藏文件&lt;/h3&gt;&lt;p&gt;为了简化工作，command line 和 Finder 都会隐藏许多文件和文件夹，这些内容通常是系统需要的。不借助第三方工具让 Finder 显示隐藏文件比较困难，但是在 command line 中却非常简单。首先，许多隐藏文件的隐藏是通过隐藏属性在 Finder 中隐藏的，而 command line 会忽略这些属性，所以这些文件会在 command line 中显示。另外，&lt;code&gt;ls&lt;/code&gt;命令会隐藏文件名以&lt;code&gt;.&lt;/code&gt;开头的文件，但是这些文件却可以被显示出来，方法是利用&lt;code&gt;-a&lt;/code&gt;选项。例如：&lt;/p&gt;&lt;pre&gt;&lt;code&gt;michelle$ ls -la&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;我们还添加了&lt;code&gt;-l&lt;/code&gt;选项，目的是控制输出格式。如果你注意输出内容的话，会发现还包括&lt;code&gt;.&lt;/code&gt;和&lt;code&gt;..&lt;/code&gt;两项，它们分别表示当前文件夹和父文件夹（如图）。如果你不想显示这两项，只需要把&lt;code&gt;-a&lt;/code&gt;改成&lt;code&gt;-A&lt;/code&gt;即可。&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;//img.renfei.org/2012/07/terminal729.png&quot;&gt;&lt;img src=&quot;//img.renfei.org/2012/07/terminal729.png&quot; alt=&quot;Terminal ls -la命令&quot; title=&quot;Terminal ls -la命令&quot; /&gt;&lt;/a&gt;&lt;/p&gt;&lt;h3 id=&quot;section-6&quot;&gt;前往其他卷&lt;/h3&gt;&lt;p&gt;在 command line 中，系统卷（也称为 root volume）是由开始的一个正斜杠表示的。然而也许听起来不可思议，在 command line 中其他卷看起来就在文件系统中一个叫做 Volumes 的文件夹中。下面的命令清晰地显示出这种逻辑关系：我从我的 home folder 出发，最终前往一个叫 Time Machine 的卷，该卷是外接在 Mac 上的。&lt;/p&gt;&lt;pre&gt;&lt;code&gt;bogon:~ renfei$ pwd/Users/renfeibogon:~ renfei$ cd /Volumes/bogon:Volumes renfei$ pwd/Volumesbogon:Volumes renfei$ lsMacintosh SSD &amp;amp;nbsp; &amp;amp;nbsp; &amp;amp;nbsp;Time Machinebogon:Volumes renfei$ cd Time\ Machine/bogon:Time Machine renfei$ pwd/Volumes/Time Machine&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;command-line-1&quot;&gt;用Command-Line管理文件&lt;/h2&gt;&lt;h3 id=&quot;section-7&quot;&gt;检视文件&lt;/h3&gt;&lt;p&gt;有许多基础命令用来定位、检视文件和文件夹，包括&lt;code&gt;cat&lt;/code&gt;, &lt;code&gt;less&lt;/code&gt;, &lt;code&gt;which&lt;/code&gt;, &lt;code&gt;file&lt;/code&gt;以及&lt;code&gt;find&lt;/code&gt;。别忘了，你可以利用&lt;code&gt;man&lt;/code&gt;命令来查阅每个命令的使用指南。&lt;/p&gt;&lt;h4 id=&quot;cat&quot;&gt;cat&lt;/h4&gt;&lt;p&gt;&lt;code&gt;cat&lt;/code&gt;是“concatenate”的意思，会按顺序读取文件并输出到 Terminal 窗口，语法为&lt;code&gt;cat&lt;/code&gt;后接你需要查看的文件的路径。&lt;code&gt;cat&lt;/code&gt;命令也可以用&lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt;来增加文本文件的内容，例如命令&lt;code&gt;cat ../textOne.txt &amp;gt;&amp;gt; textTwo.txt&lt;/code&gt;会把 textOne.txt 的内容添加到 textTwo.txt 的结尾。这个&lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt;就属于上一篇提到的“Extras”。&lt;/p&gt;&lt;h4 id=&quot;less&quot;&gt;less&lt;/h4&gt;&lt;p&gt;这个命令更适合用来查看长文本文件，因为它会允许你查找文本。语法为 &lt;code&gt;less&lt;/code&gt;后接文件路径，和&lt;code&gt;cat&lt;/code&gt;一样。用&lt;code&gt;less&lt;/code&gt;命令打开的文件其实和你查看命令使用指南的时候使用的是一个查看器，所以操作是相同的，同样可以使用&lt;span class=&quot;keybtn arrow&quot;&gt;▲&lt;/span&gt;（上箭头）或&lt;span class=&quot;keybtn arrow&quot;&gt;▼&lt;/span&gt;（下箭头）来上下移动文本，使用&lt;span class=&quot;keybtn space&quot;&gt;　&lt;/span&gt;来翻页，输入&lt;code&gt;/&lt;/code&gt;和关键字来按照关键字搜索，按&lt;span class=&quot;keybtn char&quot;&gt;Q&lt;/span&gt;来退出使用指南页面。除此之外，按&lt;span class=&quot;keybtn char&quot;&gt;V&lt;/span&gt;键来使用&lt;code&gt;vi&lt;/code&gt;文本编辑器。&lt;/p&gt;&lt;h4 id=&quot;which&quot;&gt;which&lt;/h4&gt;&lt;p&gt;这个命令会定位某个命令的文件路径。换言之，它会告诉你你执行某个具体命令的时候，在使用哪个文件。语法为&lt;code&gt;which&lt;/code&gt;后接某个命令。如图：&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;//img.renfei.org/2012/07/屏幕快照-2012-07-29-13.53.59.png&quot; alt=&quot;终端 which 命令&quot; title=&quot;终端 which 命令&quot; /&gt;&lt;/p&gt;&lt;h4 id=&quot;file&quot;&gt;file&lt;/h4&gt;&lt;p&gt;这个命令会尝试根据文件的内容输出文件类型。如果一个文件缺失了扩展名，那么这个命令可能会非常有用。语法为&lt;code&gt;file&lt;/code&gt;后接文件路径。如图，此例为一个 PNG 文件，还给出了它的尺寸、颜色数等信息。&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;//img.renfei.org/2012/07/屏幕快照-2012-07-29-14.04.51.png&quot; alt=&quot;终端 file命令&quot; title=&quot;终端 file命令&quot; /&gt;&lt;/p&gt;&lt;h4 id=&quot;find&quot;&gt;find&lt;/h4&gt;&lt;p&gt;这个命令用来根据搜索关键词定位文件路径。 &lt;code&gt;find&lt;/code&gt;命令不使用 Spotlight 搜索服务，但是它允许你设置非常具体的搜索条件，以及通配符（稍后介绍）。语法为&lt;code&gt;find&lt;/code&gt;后接搜索的起始路径，后接定义搜索的选项，后接搜索内容（包含在引号里）。例如：&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;//img.renfei.org/2012/07/屏幕快照-2012-07-29-14.12.49.png&quot; alt=&quot;Terminal Find 命令&quot; title=&quot;Terminal Find 命令&quot; /&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;如果你要搜索根目录，也许你想使用&lt;code&gt;-x&lt;/code&gt;选项来避免搜索 /Volumes 文件夹。&lt;/li&gt;&lt;li&gt;如果想使用 Soptlight 搜索服务，使用&lt;code&gt;mdfind&lt;/code&gt;命令后接搜索关键词即可。&lt;/li&gt;&lt;/ol&gt;&lt;h3 id=&quot;wildcard-characters&quot;&gt;使用通配符（Wildcard Characters）&lt;/h3&gt;&lt;p&gt;下面是常用的通配符：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;星号（＊，Asterisk）——代表任何长度的任何字符。例如&lt;code&gt;*.tiff&lt;/code&gt;代表所有格式为tiff的文件。&lt;/li&gt;&lt;li&gt;问号（?，Question mark）——代表任何单个字符。例如&lt;code&gt;b?ok&lt;/code&gt;匹配 book 但是不匹配 brook。&lt;/li&gt;&lt;li&gt;方括号（[]，Square brackets）——定义一定范围的字符，例如&lt;code&gt;[Dd]ocument&lt;/code&gt;匹配 Document 以及 document；&lt;code&gt;doc[1-9]&lt;/code&gt;匹配doc1, doc2, …, doc9。&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;配合使用上面三种通配符可以大大提高效率。&lt;/p&gt;&lt;h3 id=&quot;section-8&quot;&gt;使用递归命令&lt;/h3&gt;&lt;p&gt;简单来说，递归命令可以允许命令不执行于一个特定文件，而是指定的路径下的所有文件。大多数命令包含一个&lt;code&gt;-r&lt;/code&gt;或者&lt;code&gt;-R&lt;/code&gt;选项，来设定你想递归地执行这个命令。例如下面的例子，展示了添加&lt;code&gt;-R&lt;/code&gt;后&lt;code&gt;ls&lt;/code&gt;命令的执行方式：&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;//img.renfei.org/2012/07/屏幕快照-2012-07-29-14.39.00.png&quot; alt=&quot;Terminal -R选项&quot; title=&quot;Terminal -R选项&quot; /&gt;&lt;/p&gt;&lt;h3 id=&quot;section-9&quot;&gt;编辑文件和文件夹&lt;/h3&gt;&lt;p&gt;有许多基础的命令用来编辑文件和文件夹，包括&lt;code&gt;mkdir&lt;/code&gt;, &lt;code&gt;cp&lt;/code&gt;, &lt;code&gt;mv&lt;/code&gt;, &lt;code&gt;rm&lt;/code&gt;, &lt;code&gt;rmdir&lt;/code&gt;以及&lt;code&gt;vi&lt;/code&gt;。下面我们来简要地介绍一下这些命令。&lt;/p&gt;&lt;h4 id=&quot;mkdir&quot;&gt;mkdir&lt;/h4&gt;&lt;p&gt;“make diretory”的缩写，用来创建文件夹，语法为&lt;code&gt;mkdir&lt;/code&gt;后接新文件夹的目录。可以用&lt;code&gt;-p&lt;/code&gt;选项，来一起创建路径中不存在的文件夹（这样你就不用挨层创建了）。&lt;/p&gt;&lt;h4 id=&quot;cp&quot;&gt;cp&lt;/h4&gt;&lt;p&gt;“copy”的缩写，用来把文件从一处复制到另一处。语法为&lt;code&gt;cp&lt;/code&gt;后接原始路径，后接目标路径。如果你想复制整个文件夹和所有内容，需要添加&lt;code&gt;-R&lt;/code&gt;选项。如果指定的目标路径不含文件名，则 cp 命令会按原名复制。如果指定的目标路径包括文件名，则会复制为你指定的文件名。如果仅指定新文件名，则会在原处以新名称创建文件副本。注意，系统会自动替换同名文件而不出现提示。&lt;/p&gt;&lt;h4 id=&quot;mv&quot;&gt;mv&lt;/h4&gt;&lt;p&gt;“move”的缩写，用来移动文件。语法为&lt;code&gt;mv&lt;/code&gt;后接原路径，后接新路径。mv 的指定路径规则和 cp 是一样的（没错，如果仅指定新文件名，它就成了重命名命令）。&lt;/p&gt;&lt;h4 id=&quot;rm&quot;&gt;rm&lt;/h4&gt;&lt;p&gt;“remove”的缩写，会永久删除文件。注意，command-line中没有废纸篓。语法为&lt;code&gt;rm&lt;/code&gt;后接文件路径。然而，使用 rm 命令删除的文件有可能可以通过数据恢复工具恢复。如果希望安全删除文件，可以使用&lt;code&gt;srm&lt;/code&gt;命令。&lt;/p&gt;&lt;h4 id=&quot;rmdirrm--r&quot;&gt;rmdir和rm -R&lt;/h4&gt;&lt;p&gt;rmdir是“remove directory”的缩写，这个命令会永久删除文件夹。再强调一遍，CLI 中木有废纸篓。语法为&lt;code&gt;rmdir&lt;/code&gt;后接希望删除目录的路径。然而，rmdir 命令无法删除含有任何其他文件的文件夹，所以大多数情形下&lt;code&gt;rmdir&lt;/code&gt;命令是不适用的。不过，你可以利用&lt;code&gt;rm&lt;/code&gt;添加&lt;code&gt;-R&lt;/code&gt;选项来删除文件夹及包含的所有文件。&lt;/p&gt;&lt;h4 id=&quot;vi&quot;&gt;vi&lt;/h4&gt;&lt;p&gt;代表“visual”（视觉的），然而这个名称相当具有讽刺意味：vi可能是可视化效果最差的文本编辑器了。然而，vi 是 command line 中最常见的文本编辑器。用vi打开文本文件，只需要输入&lt;code&gt;vi&lt;/code&gt;后接文件路径即可。Mac OS X 还提供了&lt;code&gt;nano&lt;/code&gt;，一个更加现代的文本编辑器。它也更加方便，例如在底部包含了一个作弊小条（=_=），上面有常用的快捷键列表（你就不用背下来它们了）。然而，vi却有时是默认的文本编辑器，所以掌握vi是很有用的。&lt;/p&gt;&lt;p&gt;和&lt;code&gt;less&lt;/code&gt;命令类似，&lt;code&gt;vi&lt;/code&gt;命令会占用整个 Terminal 空间来显示文件内容。打开后，在“command模式”，vi 会等你输入一些预定义字符来告诉 vi 你想做什么。你也可以使用键盘上的箭头键单纯地浏览文件。你想编辑时，按&lt;span class=&quot;keybtn char&quot;&gt;A&lt;/span&gt;开始（会进入编辑模式）。文字会插入到光标处。如果你想保存，需要先退出编辑模式进入 command 模式。方法是按下&lt;span class=&quot;keybtn esc&quot;&gt;esc&lt;/span&gt;键。回到 command 模式后，按住&lt;span class=&quot;keybtn shift&quot;&gt;shift&lt;/span&gt;同时按两次&lt;span class=&quot;keybtn char&quot;&gt;Z&lt;/span&gt;来保存并退出。如果你不想保存，在 command 模式输入&lt;code&gt;:quit!&lt;/code&gt;并按&lt;span class=&quot;keybtn enter-return&quot;&gt;&lt;span class=&quot;enter&quot;&gt;enter&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;return&quot;&gt;return&lt;/span&gt;&lt;/span&gt;直接退出。&lt;/p&gt;&lt;h2 id=&quot;command-line-2&quot;&gt;用Command-Line管理系统&lt;/h2&gt;&lt;h3 id=&quot;su&quot;&gt;使用su来切换用户&lt;/h3&gt;&lt;p&gt;&lt;code&gt;su&lt;/code&gt;命令代表“substitute user identity”，允许你在命令行中轻松切换到另一个用户账户。语法为&lt;code&gt;su&lt;/code&gt;后接用户的短名称。然后会要求你输入密码（但是输入的时候不会显示）。执行完毕后，命令的前缀会改变，表示你拥有其他用户的权利。你可以利用&lt;code&gt;who -m&lt;/code&gt;命令来验证当前登陆的身份。切换后，你会一直保持该用户身份，直至退出 Terminal 或者输入&lt;code&gt;exit&lt;/code&gt;命令。&lt;/p&gt;&lt;h3 id=&quot;sudo&quot;&gt;关于sudo的使用&lt;/h3&gt;&lt;h4 id=&quot;sudo-1&quot;&gt;sudo概述&lt;/h4&gt;&lt;p&gt;更强大的命令就是&lt;code&gt;sudo&lt;/code&gt;，代表“substitute user do”，或者，更恰当地，“super user do”。用&lt;code&gt;sudo&lt;/code&gt;执行一个命令会使用 root 账户权限。当然，使用之前需要 administrator 账户（管理员账户）的授权（如输入密码）。&lt;/p&gt;&lt;p&gt;默认情况下，任何管理员账户都可以使用&lt;code&gt;sudo&lt;/code&gt;来获取 root 权限，甚至当 root 账户在图形界面被禁用的情况下，&lt;code&gt;sudo&lt;/code&gt;依然有效。这个命令是很多情况下我们不得不使用 Terminal 的原因，——同样也是给每个用户管理员身份的危险所在。不过，你可以调整&lt;code&gt;sudo&lt;/code&gt;的配置文件，来限制它的使用。&lt;/p&gt;&lt;pre&gt;&lt;code&gt;bogon:~ renfei$ cat secret.txtcat: secret.txt: Permission deniedbogon:~ renfei$ sudo cat secret.txtPassword:This is the contents of the secret.txt text file that the user account renfei does not normally have access permissions to read. However, because he is an administrative user, she can use the sudo command to envoke root user access and read the contents of this file.&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;提示&lt;/strong&gt;：如果由于你忘了使用&lt;code&gt;sudo&lt;/code&gt;而导致命令行返回一个错误，只需输入&lt;code&gt;sudo !!&lt;/code&gt;就可以用&lt;code&gt;sudo&lt;/code&gt;来执行上一条指令。&lt;/p&gt;&lt;p&gt;记住，权力越大责任越大。不恰当地使用&lt;code&gt;sudo&lt;/code&gt;可以轻易破坏你的系统设置。命令行只会在你第一次执行严重破坏性行为之前提示你，之后，它就会假设你清楚自己正在干什么。如果你只掌握三条使用命令行的准则，那将是：总是仔细检查你的命令；总是使用Tab completion来帮助你避免拼写错误；使用&lt;code&gt;sudo&lt;/code&gt;之前，总是仔仔细细检查你的命令。&lt;/p&gt;&lt;h4 id=&quot;sudo--shell&quot;&gt;使用 sudo 切换 Shell&lt;/h4&gt;&lt;p&gt;如果你是一个管理员用户，你需要执行很多条需要 root 权限的命令，你可以临时切换整个命令行 shell 来取得 root 级别的访问权限。方法就是先输入&lt;code&gt;sudo -s&lt;/code&gt;，回车后再键入你的密码。&lt;/p&gt;&lt;h2 id=&quot;command-line-3&quot;&gt;其他Command-Line技巧提示&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;输入命令&lt;code&gt;open .&lt;/code&gt;可以用 Finder 打开当前的位置。&lt;/li&gt;&lt;li&gt;在 Terminal 的偏好里面可以设定它的外观和风格。&lt;/li&gt;&lt;li&gt;中止一个错误的或者发疯的命令，可以使用组合键&lt;span class=&quot;keybtn control&quot;&gt;control&lt;/span&gt; + &lt;span class=&quot;keybtn char&quot;&gt;C&lt;/span&gt;。&lt;/li&gt;&lt;li&gt;你可以在执行前编辑命令，只需要使用箭头和键盘上的其他字母。&lt;/li&gt;&lt;li&gt;没有输入任何命令时，你可以用&lt;span class=&quot;keybtn arrow&quot;&gt;▲&lt;/span&gt;和&lt;span class=&quot;keybtn arrow&quot;&gt;▼&lt;/span&gt;来浏览历史命令。同样可以编辑和再次执行。&lt;/li&gt;&lt;li&gt;你也可以使用&lt;code&gt;history&lt;/code&gt;命令查看历史记录。&lt;/li&gt;&lt;li&gt;你可以使用组合键&lt;span class=&quot;keybtn control&quot;&gt;control&lt;/span&gt; + &lt;span class=&quot;keybtn char&quot;&gt;L&lt;/span&gt;清屏。&lt;/li&gt;&lt;/ul&gt;</description><pubDate>Sun, 29 Jul 2012 00:00:00 +0800</pubDate><link>https://www.renfei.org/blog/mac-os-x-terminal-101.html</link><guid isPermaLink="true">https://www.renfei.org/blog/mac-os-x-terminal-101.html</guid></item><item><title>只借助 HTML分别禁用IE8, IE9的兼容视图模式（Compatibility View）</title><description>&lt;p&gt;从 IE 8 开始，IE 添加了兼容模式，开启后会以低版本的 IE 进行渲染。但是有时这样会导致网页出问题，于是我们通常在 html 中添加下列代码来使 IE 使用固定的渲染模式：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;meta&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;http-equiv=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;X-UA-Compatible&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;IE=8&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;&amp;lt;!--以IE8模式渲染--&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;meta&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;http-equiv=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;X-UA-Compatible&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;IE=7&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;&amp;lt;!--以IE7模式渲染--&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;但是我就遇到了一种情况，在 IE8 下只有不使用兼容模式页面才能显示正常，但是如果设定为 IE8 的模式，在 IE9 中却会导致 CSS3 失效。看来，我需要&lt;strong&gt;完全禁用&lt;/strong&gt;兼容模式。怎么办呢？可以在后台判断浏览器版本，如果是 IE8 就输出&lt;code&gt;content=&quot;IE=8&quot;&lt;/code&gt;，如果是 IE9 就输出 &lt;code&gt;content=&quot;IE=9&quot;&lt;/code&gt;。但是这样 html 是无法实现的。其实，可以单纯使用下面的代码来实现：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;meta&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;http-equiv=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;X-UA-Compatible&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;IE=EDGE&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我测试后完全解决了问题。这样设置后，IE 中设置兼容模式的按钮也会消失。你可以按 F12 打开开发模式来检查。&lt;/p&gt;</description><pubDate>Sat, 28 Jul 2012 00:00:00 +0800</pubDate><link>https://www.renfei.org/blog/disable-ie-compatibility-view.html</link><guid isPermaLink="true">https://www.renfei.org/blog/disable-ie-compatibility-view.html</guid></item><item><title>Google Analytics “事件追踪” 功能使用详解</title><description>&lt;p&gt;Google Developers 网站专门&lt;a href=&quot;https://developers.google.com/analytics/devguides/collection/gajs/eventTrackerGuide&quot;&gt;介绍&lt;/a&gt;了 Google Analytics 的 Event Tracking 的使用方法，但是它还没有中文版的，我大概翻译并整理了一下，形成这篇文章。&lt;/p&gt;&lt;h2 id=&quot;section&quot;&gt;简介&lt;/h2&gt;&lt;p&gt;事件追踪是 ga.js 跟踪代码中提供的一个方法，你可以追踪访客和网站的互动，尤其是追踪那些不产生新页面的访问行为（例如下载文件）。事件追踪在 Google Analytics 中单独有一个报表来显示数据，并且和 pageview 数据不互相影响。&lt;/p&gt;&lt;p&gt;使用事件追踪，最新的 Google Analytics 异步统计代码就可以使用，不用修改或添加内容。&lt;/p&gt;&lt;h2 id=&quot;section-1&quot;&gt;设置事件追踪&lt;/h2&gt;&lt;p&gt;利用 &lt;code&gt;_trackEvent()&lt;/code&gt; 方法来进行事件追踪。它的详细参数如下：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;_trackEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;category&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;opt_label&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;opt_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;opt_noninteraction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt;category（必需）：类别&lt;/li&gt;&lt;li&gt;action（必需）：和用户的行为对应，例如“下载”&lt;/li&gt;&lt;li&gt;label：标签，其他有关信息&lt;/li&gt;&lt;li&gt;value：提供数值型数据&lt;/li&gt;&lt;li&gt;non-interaction：布尔值。如果设定为 &lt;code&gt;true&lt;/code&gt;，表明这个事件不会参与跳出率的计算（详见后文）&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;如果你在链接中使用事件追踪，那么再简单不过了。给需要追踪的链接调用&lt;code&gt;_trackEvent()&lt;/code&gt;方法并设置参数。例如：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;#&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;onclick=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;_gaq.push([&amp;#39;_trackEvent&amp;#39;, &amp;#39;category&amp;#39;, &amp;#39;action&amp;#39;, &amp;#39;label&amp;#39;, &amp;#39;value&amp;#39;, &amp;#39;true&amp;#39;]);&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;link_name&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;上面的代码中，把&lt;code&gt;#&lt;/code&gt;换成你的链接，把&lt;code&gt;category&lt;/code&gt;、&lt;code&gt;action&lt;/code&gt;、&lt;code&gt;label&lt;/code&gt;、&lt;code&gt;value&lt;/code&gt;、&lt;code&gt;true&lt;/code&gt;按照说明换成相应参数（有的是可选的）。&lt;/p&gt;&lt;h2 id=&quot;event-tracking-&quot;&gt;Event Tracking 详解&lt;/h2&gt;&lt;p&gt;下面将详细介绍 Event Tracking 所包含的参数。&lt;/p&gt;&lt;h3 id=&quot;categories&quot;&gt;Categories（类别）&lt;/h3&gt;&lt;p&gt;Category 是事件追踪的根分类，用于划分事件类别，是必须的参数。一个典型情况就是，使用同一个 Category 名称，其中包含许多不同的动作。例如，你想跟踪一个视频播放工具的使用情况，需要用到：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;_gaq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;_trackEvent&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Videos&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Play&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Gone With the Wind&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_gaq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;_trackEvent&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Videos&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Pause&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Gone With the Wind&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_gaq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;_trackEvent&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Videos&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Stop&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Gone With the Wind&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;假设你也想跟踪视频被下载了多少次，你可以使用：&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;_gaq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;_trackEvent&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Videos&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Downloaded&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Gone With the Wind&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在这种情况下，只有一个 Category — Videos 在报告里出现，你可以查看 Video 下事件发生次数的总计。&lt;/p&gt;&lt;p&gt;然而，很可能你有多个 Category 需要追踪，这时你应该考虑好你要怎么组织报表，然后再在你的网站插入代码。例如，你想把所有的不同视频都放在 Video 这个分类下，因为这样你就可以看有关“视频”的总计事件发生次数了（而不管具体是哪个视频）。&lt;/p&gt;&lt;p&gt;另一方面，你也可能会建立不同的 Categories 来划分不同类型的视频——电影、音乐，以及一个单独的 Category 负责“下载”。那么，你需要三个 Category：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Videos - Movies&lt;/li&gt;&lt;li&gt;Videos - Music&lt;/li&gt;&lt;li&gt;Downloads&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;这种情况下，你可以查看这三个类别的“事件总数”（在 Total Events 报表），此报表会显示所有 Categories 的所有事件。然而你不能排除 Downloads 而单独查看所有 Videos 的事件数，因为只有各自 Category 内才能查看详细内容。&lt;/p&gt;&lt;p&gt;所以，你应该先规划好你需要的报表的结构，再使用 &lt;code&gt;_trackEvent()&lt;/code&gt; 方法进行跟踪统计。注意：Category 的名字必须保持一致。例如你之前使用 Video 作为类别，一段时间后添加新的代码时误用了 Videos，那么你的报表将同时出现这两个类别，并且他们是不能合并的。&lt;/p&gt;&lt;h3 id=&quot;actions&quot;&gt;Actions（动作）&lt;/h3&gt;&lt;p&gt;这个参数也是必要的，是&lt;code&gt;_trackEvent()&lt;/code&gt; 方法的第二个参数。一般来说你用这个参数定义事件的类型或动作的名称。例如，对于一个 Videos 类别，你可以指定不同的动作，类似播放、停止、暂停，以及“视频加载时间”。&lt;/p&gt;&lt;p&gt;和 Categories 一样，Actions 的命名也是自定的。但是注意 Actions 在报表中的两个特点：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;所有的 Actions 都是独立于他们从属的 Category 而列出的。（有点类似于 WordPress 中的“标签”系统——这给了你另一种划分事件数据的方法）&lt;/li&gt;&lt;li&gt;一个独立事件是以一个独立的 Action 名称划分的。（你可以在不同的 Categories 中使用相同的事件）详情请看后文“计算方法”。&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;为了更好地管理大量事件，请注意下列关于 Actions 的建议：&lt;/p&gt;&lt;h4 id=&quot;action-&quot;&gt;Action 名称应该与你的报表数据相关。&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;Event Tracking（事件追踪）会把不同 Categories 下的相同 Action 的事件合并。例如，如果你用 Click 同时作为 Download 类别以及 Video 类别下的某一 Action 名称，那么在 Top Actions 报表中 Click 的数据将是两个类别中 Click 数据的总和。你可以进一步把 Click 动作按类别划分。然而，如果你滥用 Click 作为动作名称，上述分层次浏览方式的有效性就会被削弱。&lt;/li&gt;&lt;/ul&gt;&lt;h4 id=&quot;action--1&quot;&gt;使用 Action 名称的两种方式。&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;方式一：例如，你把 Play 作为动作名称应用到所有的视频中。这时，你可以浏览所有 Play 的事件数，也可以把 Play 和其他类似动作（如 Pause）进行比较。&lt;/li&gt;&lt;li&gt;方式二：假设你只想建立一个 Videos 类别，但是想追踪不同类型的事件。这是你可以使用不同的 Action 名称来区分（例如分别建立 Play - Mac，Play - Windows 两个动作）。&lt;/li&gt;&lt;/ul&gt;&lt;h4 id=&quot;actions-&quot;&gt;Actions 并不总是意味着“动作”。&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;一切都是根据你的需要出发，你可以给这些参数指定任意字串作为名称。例如，你想追踪下载，那么 Actions 就可以命名为 pdf, doc, zip，这样一来，你的 “下载” 类别就可以根据不同文件类型进行细分。&lt;/li&gt;&lt;/ul&gt;&lt;h4 id=&quot;actions--1&quot;&gt;独立事件数是根据独立的 actions 来计算的。&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;一个访客触发了某 action 的事件，这个 action 的 “独立事件数” 会增加。但是此后该访客触发任何&lt;strong&gt;同一&lt;/strong&gt;action 的事件是不会增加 “独立事件” 数目的（即使该访客后来是在别的地方触发，只要还是之前的 action，就如此）。这会在报表中带来两个值得注意的结果。第一，假设一个使用者从两个不同的视频播放器分别触发了两个 Category 下的同一 Play 动作，“Top Actions” 报表中 Play 将会计算一次独立事件。第二，针对每个 Category 的 “Action” 报表都会分别记录一次独立事件（详情见后文“计算方法”）。&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&quot;labels&quot;&gt;Labels（标签）&lt;/h3&gt;&lt;p&gt;“Label” 这个词是事件追踪 &lt;code&gt;_trackEvent()&lt;/code&gt; 方法的第三个参数，而且不是必须的。你可以利用 Labels 来设定更多的信息，比如视频的名字，或者下载文件的名称。&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;_gaq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;_trackEvent&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Downloads&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;PDF&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;/salesForms/orderForm1.pdf&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;就像 Categories 和 Actions 一样，所有 Labels 在报表中都有独自的显示空间。你可以把 Labels 看作是对事件进行分类的另一个角度。例如，你在网页中有五个播放器，你想跟踪它们的使用情况。每个播放器都可以使用 Videos 这个类别，以及 Play 这个动作；但是每个播放器都有不同的 Label（例如视频名称），这样一来它们在报表中就可以区分出来了。&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;_gaq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;_trackEvent&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Videos&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Play&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Gone With the Wind&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_gaq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;_trackEvent&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Videos&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Play&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Huckleberry Finn&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&quot;values&quot;&gt;Values（数值）&lt;/h3&gt;&lt;p&gt;Value 是第四个，也是可选的参数。这个参数和其他几个的不同在于它是整数型而非字串，所以它用来给被跟踪的对象定义一个“数值型”属性。例如，你可以用它来提供一个播放器加载所消耗的秒数。&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;_gaq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;_trackEvent&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Videos&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Video Load Time&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Gone With the Wind&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;downloadTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个参数是一个非负数值，所以报表可以把 Values 加和，并进一步求平均值。在上例中， Video Load Time 动作在视频加载完毕后触发，并执行 &lt;code&gt;_trackEvent()&lt;/code&gt; 方法。标签就是视频的名字，每次视频加载完毕后由系统以某种方式计算出加载时间，然后通知 Google。经过统计，你便可以得到平均加载时间。假设你有 5 次不同的下载，每次的秒数分别为 10、25、8、5、5，那么报表会计算出总和为 53，平均为 10.6。&lt;/p&gt;&lt;h3 id=&quot;non-ineraction-&quot;&gt;Non-Ineraction 事件&lt;/h3&gt;&lt;p&gt;Non-interation 这个词作为最后一个参数（同时也是可选参数）出现，它是一个布尔变量（只能设置为 &lt;code&gt;true&lt;/code&gt; 或 &lt;code&gt;false&lt;/code&gt;）。这个变量允许你决定网站中包含事件追踪的页面的跳出率的定义。例如，你的主页有一个视频嵌入其中。你很可能想要知道主页的跳出率，但是你想如何定义它？你是否想把访问者观看视频作为一个参与的信号？是的话，你将希望把观看视频加入到跳出率的计算中——这样一来只访问主页，但却看了视频的访问是不算跳出的。相反，如果你想得到更加严格意义上的“跳出率”，那么你会希望把所有只访问主页（而不管是否观看了视频）的访问都计算到跳出率中。&lt;/p&gt;&lt;p&gt;这就是 opt_noninteration 参数负责的地方。按照默认设置（&lt;code&gt;false&lt;/code&gt;），事件属于“互动事件”，即触发事件追踪的访问是不算作跳出访问的。如果这个参数设置为 &lt;code&gt;true&lt;/code&gt;，这类事件就算作“非互动事件”，触发事件而只访问一页也算作跳出访问。所以你可以用这个参数来校正页面跳出率的计算方法。&lt;/p&gt;&lt;h3 id=&quot;section-2&quot;&gt;计算方法&lt;/h3&gt;&lt;p&gt;报表中，Total Events 计算的是总事件数。如果一个用户的一次访问中触发了多次事件，那么这在报表中将被体现为一次“包含事件的访问（Visit w/Event）”或“独立事件（Unique Event）”。&lt;/p&gt;&lt;p&gt;下表展示了数据是如何处理的。在这个例子中，两个视频播放器使用了同一个类别，每个播放器有一个标签。这些播放器都有 Play、Stop 动作。&lt;/p&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th scope=&quot;col&quot;&gt;Action Type&lt;/th&gt;&lt;th scope=&quot;col&quot;&gt;Label: &quot;Gone With the Wind&quot;&lt;/th&gt;&lt;th scope=&quot;col&quot;&gt;Label: &quot;Mr Smith Goes to Washington&quot;&lt;/th&gt;&lt;th scope=&quot;col&quot;&gt;Totals&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td scope=&quot;row&quot;&gt;Play&lt;/td&gt;&lt;td&gt;10 visits w/Event&lt;/td&gt;&lt;td&gt;5 visits w/Event&lt;/td&gt;&lt;td&gt;15 unique events &quot;Play&quot;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td scope=&quot;row&quot;&gt;Pause&lt;/td&gt;&lt;td&gt;2 visits w/Event&lt;/td&gt;&lt;td&gt;8 visits w/Event&lt;/td&gt;&lt;td&gt;10 unique events &quot;Pause&quot;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td scope=&quot;row&quot;&gt;Stop&lt;/td&gt;&lt;td&gt;2 visits w/Event&lt;/td&gt;&lt;td&gt;3 visits w/Event&lt;/td&gt;&lt;td&gt;5 unique events &quot;Stop&quot;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;总计&lt;/th&gt;&lt;td&gt;14 unique events for GWTW&lt;/td&gt;&lt;td&gt;16 unique events for Mr Smith&lt;/td&gt;&lt;td&gt;30 unique events for category &quot;videos&quot;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;上表假设每次访问中，访问者使用 Gone with the wind 和 Mr Smith goes to Washington 是独立的。然而下表展示了更复杂、更典型的情况下事件的计算方法。有些访客只在一个视频中按下 Play 按钮，而其它访客在一次访问中会使用多个视频。&lt;/p&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th scope=&quot;col&quot;&gt;Action Type&lt;/th&gt;&lt;th scope=&quot;col&quot;&gt;Label: &quot;Gone With the Wind&quot;&lt;/th&gt;&lt;th scope=&quot;col&quot;&gt;Label: &quot;Mr Smith Goes to Washington&quot;&lt;/th&gt;&lt;th scope=&quot;col&quot;&gt;Totals&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td scope=&quot;row&quot;&gt;Play&lt;/td&gt;&lt;td&gt;10 visits w/event&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;10 unique events &quot;Play&quot;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td scope=&quot;row&quot;&gt;Play&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;5 visits w/event&lt;/td&gt;&lt;td&gt;5 unique events &quot;Play&quot;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td scope=&quot;row&quot;&gt;Play&lt;/td&gt;&lt;td colspan=&quot;2&quot;&gt;1 visit w/event on BOTH movies (two clicks on &quot;Play&quot;)&lt;/td&gt;&lt;td&gt;1 unique event &quot;Play&quot;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;总计&lt;/th&gt;&lt;td&gt;11 unique play events for GWTW&lt;/td&gt;&lt;td&gt;6 unique play events for Mr Smith&lt;/td&gt;&lt;td&gt;16 unique events for category &quot;Videos&quot; and 16 unique events for action &quot;Play&quot;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;我们注意到总计是 16 次事件而非 17 次，这是符合事实的。&lt;/p&gt;&lt;h2 id=&quot;section-3&quot;&gt;注意&lt;/h2&gt;&lt;p&gt;每次访问有 500 次请求的限制（包括事件和页面浏览）。所以要注意这一点，不要设置容易发生很多次的事件（例如鼠标经过触发事件），以免影响统计。&lt;/p&gt;</description><pubDate>Sat, 07 Jul 2012 00:00:00 +0800</pubDate><link>https://www.renfei.org/blog/google-analytics-event-tracking-guidelines.html</link><guid isPermaLink="true">https://www.renfei.org/blog/google-analytics-event-tracking-guidelines.html</guid></item><item><title>MAMP：在 OS X 中搭建 Apache, MySQL, PHP 环境并本地安装、调试 WordPress</title><description>&lt;p&gt;MAMP 这个名字来源于 &lt;strong&gt;M&lt;/strong&gt;acintosh &lt;strong&gt;A&lt;/strong&gt;pache &lt;strong&gt;M&lt;/strong&gt;ySQL &lt;strong&gt;P&lt;/strong&gt;HP，显然专门用来在 Mac 环境下搭建 Apache、MySQL、PHP 平台。&lt;/p&gt;&lt;p&gt;虽然 OSX 中已经预装了 Apache 1.3.x 和 PHP 4.3.2 环境，但是启用、配置并安装整合 MySQL 仍然是一件极其复杂的工作。MAMP 的特点就是简便（从以下安装步骤即可看出），而且它不会破坏系统本身的文件，所修改、创建文件的范围仅限程序本身。&lt;/p&gt;&lt;h2 id=&quot;section&quot;&gt;系统要求：&lt;/h2&gt;&lt;p&gt;Mac OS X 10.4 (Tiger) 及以上。&lt;/p&gt;&lt;h2 id=&quot;section-1&quot;&gt;安装步骤：&lt;/h2&gt;&lt;ol&gt;&lt;li&gt;去官方网站下载最新版本。&lt;/li&gt;&lt;li&gt;打开 dmg 文件，把 MAMP 拖到 Applications 文件夹中（注意：MAMP 必须处于 Applications 文件夹才能正常工作）。&lt;/li&gt;&lt;/ol&gt;&lt;h2 id=&quot;mamp&quot;&gt;设定MAMP&lt;/h2&gt;&lt;ol&gt;&lt;li&gt;打开 MAMP（而不是 MAMP Pro），选择 Preferences 标签，会看到端口（Ports）的设定。默认 Apache 端口是 8888，如果不修改，就意味着需要通过 http://localhost:8888/ 访问。你也可以改成其他（例如改成 80，就不用输入 : 后面的部分了），但是缺点是，你每次都需要输入密码。&lt;/li&gt;&lt;li&gt;在 PHP 标签选择 PHP 5.2.4 以上版本即可（WordPress 3.2 的最低需求）。&lt;/li&gt;&lt;li&gt;在 Apache 标签，选择文件的存放地点，这个可以任意修改。比如我的设定是：&lt;br /&gt;&lt;code&gt;/Users/renfei/Documents/localhost&lt;/code&gt;&lt;/li&gt;&lt;li&gt;点 OK 完成设定。&lt;/li&gt;&lt;/ol&gt;&lt;h2 id=&quot;mamp-&quot;&gt;打开 MAMP 服务并创建数据库&lt;/h2&gt;&lt;ol&gt;&lt;li&gt;点击 Start Servers，稍等片刻，发现红灯变成绿灯，说明服务开启成功。&lt;/li&gt;&lt;li&gt;开启后，应该自动出现 MAMP 的起始页（如果没有出现，点击 Open Start Page 按钮。&lt;/li&gt;&lt;li&gt;在开启的网页中选择 phpMyAdmin，然后选择“数据库”标签。在“新建数据库”中给你的数据库起一个名字（例如我用 &lt;code&gt;wordpress&lt;/code&gt;），填好后直接点“新建”。&lt;/li&gt;&lt;/ol&gt;&lt;h2 id=&quot;wordpress&quot;&gt;安装 WordPress&lt;/h2&gt;&lt;ol&gt;&lt;li&gt;下载 WordPress。&lt;/li&gt;&lt;li&gt;下载后，解压缩，把 wordpress 文件夹放到之前设定的文件存放地点。按照我的设定，文件全部在 /Users/renfei/Documents/localhost/wordpress&lt;/li&gt;&lt;li&gt;访问 localhost（按照我的设定，地址为&lt;code&gt;localhost:8888/wordpress&lt;/code&gt;），并开始熟悉的 WordPress 安装过程。&lt;/li&gt;&lt;li&gt;相关内容按照这样填写：&lt;/li&gt;&lt;/ol&gt;&lt;pre&gt;&lt;code&gt;database name: wordpressdatabase host/server: localhostdatabase user: rootdatabase password: root&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;至此，全部安装过程完成，你可以在本机使用 WordPress 了。&lt;/p&gt;</description><pubDate>Fri, 06 Jul 2012 00:00:00 +0800</pubDate><link>https://www.renfei.org/blog/mamp-and-wordpress.html</link><guid isPermaLink="true">https://www.renfei.org/blog/mamp-and-wordpress.html</guid></item></channel></rss>