-
Notifications
You must be signed in to change notification settings - Fork 0
/
atom.xml
513 lines (287 loc) · 239 KB
/
atom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Sherlozk</title>
<subtitle>trace your love</subtitle>
<link href="https://sherlozk.github.io/atom.xml" rel="self"/>
<link href="https://sherlozk.github.io/"/>
<updated>2022-11-19T03:54:34.460Z</updated>
<id>https://sherlozk.github.io/</id>
<author>
<name>Sherlozk</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>Python 多进程下 logging 日志切分踩坑</title>
<link href="https://sherlozk.github.io/2021/01/01/python-logging/"/>
<id>https://sherlozk.github.io/2021/01/01/python-logging/</id>
<published>2021-01-01T12:25:43.000Z</published>
<updated>2022-11-19T03:54:34.460Z</updated>
<content type="html"><![CDATA[<h2 id="起"><a href="#起" class="headerlink" title="起"></a>起</h2><p>这个事情从一个新的 tornado 服务说起,这个服务部署到预发布环境之后,会有一个流水线定时每个小时触发访问塞数据到这个服务,但是每天晚上12点服务会产生大量的CLOSE_WAIT,导致所有进程被阻塞,整个服务失效。</p><p>回顾 TIME_WAIT 和 CLOSE_WAIT 相关知识可以自行 Google ,重点就是一句话:CLOSE_WAIT 问题一定出在自己的代码里面</p><h2 id="承"><a href="#承" class="headerlink" title="承"></a>承</h2><p>查日志发现没有日志…</p><p>一开始我怀疑是全局 MongoDB Client 存在进程安全问题,于是把MongoDB Client 放到了数据类当中,并且每次数据类__del__的时候会close连接。重启服务,弄了一个循环跑了一千次,怼了几十万次数据,一觉睡醒啥事儿没有。以为搞定了。</p><p>结果…第二天12点又出现了…CLOSE_WAIT</p><h2 id="转"><a href="#转" class="headerlink" title="转"></a>转</h2><p>依然没有日志,无奈只能用 pysnooper 来调试接口,把堆栈打出来,看看卡在哪里了。(这是依然不知道问题在哪里)重启服务就没问题…</p><p>这里先夸一下 pysnooper,可以让你直接在服务器上面获得调试能力</p><p><img src="/2021/01/01/python-logging/ahfeq-1gjkn.jpg"></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@pysnooper.snoop(<span class="params">output=<span class="string">"/xxx/log/pysnooper.log"</span>, depth=<span class="number">2</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">add_source_data</span>(<span class="params">self, body_json</span>):</span><br><span class="line"> ...</span><br></pre></td></tr></table></figure><p>可以指定日志输出的路径,以及堆栈深度。会把过程中赋值的结果都打印出来。</p><p>又到了 12 点…</p><p>这次终于让 pysnooper 捕捉到了信息。</p><p><img src="/2021/01/01/python-logging/at502-wajm5.jpg" alt="enter image description here"></p><p>居然是卡在打日志这里了。</p><p>知道问题在哪里就好办多了,如果是日志的原因,又每天都是 12 点出问题,那基本就实锤是因为日志切分的问题了。</p><h2 id="合"><a href="#合" class="headerlink" title="合"></a>合</h2><p>由于在多进程下面,logging 使用的 TimedRotatingFileHandler 日志切分是不安全的</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"> <span class="keyword">def</span> <span class="title function_">doRollover</span>(<span class="params">self</span>):</span><br><span class="line"><span class="comment"># 现将现有的文件流关闭</span></span><br><span class="line"> <span class="keyword">if</span> self.stream:</span><br><span class="line"> self.stream.close()</span><br><span class="line"> self.stream = <span class="literal">None</span></span><br><span class="line"> ...</span><br><span class="line"> <span class="comment"># 根据时间戳以及 baseFilename 计算出 rotate 后的文件名</span></span><br><span class="line"> dfn = self.rotation_filename(self.baseFilename + <span class="string">"."</span> +</span><br><span class="line"> time.strftime(self.suffix, timeTuple))</span><br><span class="line"> <span class="comment"># 如果目标文件已存在,则删除</span></span><br><span class="line"> <span class="keyword">if</span> os.path.exists(dfn):</span><br><span class="line"> os.remove(dfn)</span><br><span class="line"> </span><br><span class="line"> <span class="comment"># 将日志文件 rotate 到上面计算出来的新文件名,这里后面调用的是 os.rename()</span></span><br><span class="line"> self.rotate(self.baseFilename, dfn)</span><br><span class="line"> </span><br><span class="line"><span class="comment"># 根据设置的最大保留文件数删除旧日志</span></span><br><span class="line"> <span class="keyword">if</span> self.backupCount > <span class="number">0</span>:</span><br><span class="line"> <span class="keyword">for</span> s <span class="keyword">in</span> self.getFilesToDelete():</span><br><span class="line"> os.remove(s)</span><br><span class="line"> ...</span><br><span class="line"> self.rolloverAt = newRolloverAt</span><br></pre></td></tr></table></figure><p>上面是 TimedRotatingFileHandler 默认的 rotate 函数。大致分为六步,先将当前日志文件的 file descriptor(后面简称 fd)关闭,防止继续有新的内容写入。然后根据预设好的 rotate 时间规则生成文件名。第三步,如果第二步生成的文件名已存在,则将给文件删除(这里是导致多进程会丢失日志的原因)。最后再将当前日志重命名为第二步生成的新文件名,然后根据预设的最大保留文件数,删除过于老旧的日志文件,最后更新 rotate 的时间。</p><p>可以看到,rotate 的过程完全没有加锁,是非原子的。</p><p>所以,为了能在多进程服务下安全使用 logging,需要对 TimedRotatingFileHandler 的 doRollover() 进行改写,下面是我们改写的 doRollover() 函数</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">doRollover</span>(<span class="params">self</span>):</span><br><span class="line"> <span class="comment">#迟一点关闭,先用来做文件锁</span></span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> if self.stream:</span></span><br><span class="line"><span class="string"> self.stream.close()</span></span><br><span class="line"><span class="string"> """</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># 根据时间戳以及 baseFilename 计算出 rotate 后的文件名</span></span><br><span class="line"> dfn = self.baseFilename + <span class="string">"."</span> + time.strftime(self.suffix, timeTuple)</span><br><span class="line"></span><br><span class="line"> <span class="comment">#使用文件锁</span></span><br><span class="line"> fcntl.flock( self.stream, fcntl.LOCK_EX )</span><br><span class="line"></span><br><span class="line"> <span class="comment">#不存在才rename, 存在说明其他进程roller过</span></span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> os.path.exists(dfn):</span><br><span class="line"> os.rename(self.baseFilename, dfn)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 根据设置的最大保留文件数删除旧日志</span></span><br><span class="line"> <span class="keyword">if</span> self.backupCount > <span class="number">0</span>:</span><br><span class="line"> <span class="keyword">for</span> s <span class="keyword">in</span> self.getFilesToDelete():</span><br><span class="line"> os.remove(s)</span><br><span class="line"></span><br><span class="line"> <span class="comment">#关闭文件流的同时会自动释放文件锁</span></span><br><span class="line"> <span class="keyword">if</span> self.stream:</span><br><span class="line"> self.stream.close()</span><br><span class="line"></span><br><span class="line"> ...</span><br><span class="line"> </span><br><span class="line"> self.rolloverAt = newRolloverAt</span><br></pre></td></tr></table></figure><p>上面的改动加入了文件锁,使整个 rotate 的过程变成原子操作。</p><p>但是这里引入了两个新的问题:</p><ol><li>在进行实质 rotate 操作之前没有先关闭 self.stream 文件流,使得其他进程还是会往缓冲区写入数据,所以在进行滚动操作的时候,依然会持续往日志文件写入内容,导致超出 rotate 时间节点的日志也会被写入到 rotate 后的文件中。</li><li>fcntl.flock 是协议锁,并非强制锁(<a href="https://cloud.tencent.com/developer/article/1115821">Linux 中 fcntl()、lockf、flock 的区别</a>)</li></ol><p>第一个问题,如果不严格要求日志的精准切分,其实也不算是问题,起码并不会引起程序的异常。而第二个问题就是导致前文在日志切分的时候产生大量 CLOSE_WAIT 阻塞的原因。</p><p><img src="/2021/01/01/python-logging/ayz2h-hzlml.jpg" alt="enter image description here"></p><p>通过 pysnooper debug 可以看到产生大量 CLOSE_WAIT 的时候,所有进程都卡在了 <code>fcntl.flock( self.stream, fcntl.LOCK_EX)</code>这里,也就是真正执行 rotate 操作的那个进程,没有成功释放锁,导致后面在等待锁的进程全部被阻塞。</p><p>那为什么锁没有释放呢?按照设定,在第一个进入 doRollover() 的进程完成 rotate 操作的时候会 close self.stream ,随机 flock 会被解锁。看来并没有按照预定发展进行。</p><p>盘它!</p><p>有一个细节,虽然服务进程最后全都被阻塞了,但是 rotate 操作是成功了的,因为日志已经被成功 rename。从这一点来说,真相只有一个!那就是,<strong>self.stream.close() 的时候并没有成功释放锁。</strong></p><p>分析原因,先来看下面这两段引文:</p><blockquote><p>fcntl.flock(fd, operation)</p><p>Perform the lock operation operation on file descriptor fd (file objects providing a fileno() method are accepted as well). See the Unix manual flock(2) for details. (On some systems, this function is emulated using fcntl().)</p></blockquote><hr><blockquote><p>flock( )</p><p>Locks created by flock() are associated with an open file description (see open(2)). This means that duplicate file descriptors (created by, for example, fork(2) or dup(2)) refer to the same lock, and this lock may be modified or released using any of these file descriptors. Furthermore, the lock is released either by an explicit LOCK_UN operation on any of these duplicate file descriptors, or when all such file descriptors have been closed.</p><p>If a process uses open(2) (or similar) to obtain more than one file descriptor for the same file, these file descriptors are treated independently by flock(). An attempt to lock the file using one of these file descriptors may be denied by a lock that the calling process has already placed via another file descriptor.</p></blockquote><p>结合这两段引文可以的出以下结论:</p><ol><li>锁定操作是对 fd 执行的</li><li>通过 fork()或者 dup()是可以将一个进程的 fd 复制给子进程的</li><li>通过 fork()或者 dup()出来的 fd 可以用来操作同一把锁,意思就是,fd1 和 fd2 都是从 fd0 dup()出来,在 进程 1 通过 fd1 获得锁的情况下,进程 2 同样可以通过 fd2 获得锁,锁就相当于失效了</li><li>在 2 的前提下,要想释放锁有两种方法,显式调用 LOCK_UN 或者所有 fd 被 closed</li></ol><p>其实至此,稍加思考,问题已经可以被还原出来了。链路如下:</p><ol><li>在 tornado 服务的入口文件启动主进程, import logging 并且复写了 TimedRotatingFileHandler 的 doRollover()函数,并且新建了日志输出流 self.stream(本质就是一个 fd),开始监听 0.0.0.0:xxxx 端口</li><li>fork 出若干个作业子进程,于此同时将日志输出流的 fd.fileno() 一并复制了过去</li><li>当需要 rotate 日志的时候,其实锁是失效状态的,因为每个子进程都可以通过 self.stream 这个 fd 去获取到锁</li></ol><p>等等…</p><p><strong>如果锁是失效状态,那为什么还存在有进程被阻塞的情况呢?</strong></p><p>那只能说明有进程通过非 dup()出来的 fd 申请了锁,这种情况下,锁是生效的,并且需要等待显式调用 LOCK_UN 或者主进程创建的 fd 和所有 dup() 出来 fd 被 closed ,锁才会被释放。但问题就出在这里,doRollover() 里面没有主动调用 LOCK_UN 来释放锁,并且,主进程只负责监听端口,分发请求,并不会参与到日志切分的过程当中,于是悖论就产生了,没有子进程主动调用 LOCK_UN ,主进程创建的 fd 永远不会 closed ,锁也就不会释放,于是在等待锁的子进程就会被卡住,直至所有的子进程都被卡住,进入到 CLOSE_WAIT 状态。</p><p>那到了这里就还剩下一个问题,<strong>非 dup()出来的 fd 是哪来的?</strong></p><p>还是查代码,因为我们用的并非原生的 logging,而是对 logging 做了一层封装,模块名为 ilog。并且为了实现多进程下安全的按日期切分日志,创建了一个继承于 TimedRotatingFileHandler 的类 iTimedRotatingFileHandler 。而在更底层的父类 FileHandler 会 open() 一个 fd 传给父类 StreamHandler ,最后赋值给 self.stream 。这就是 import ilog 的时候做的。</p><p>上面一段话的意思就是,import ilog 的时候会创建 iTimedRotatingFileHandler 实例,并且会 open() 一个 fd 赋值给它的属性 stream。这是在主进程第一次 import ilog 的时候做的。</p><p>重点来了,那如果子进程当中,如果 ilog 是通过包结构的方式(from … import …)来 import 的呢?</p><p>import 的本质是将模块的信息添加到 sys.modules 当中,sys.modules 的本质是一个字典,通过 key-value 的形式查找 已经 import 的模块。而直接 import 和 form … import … 的产生的 key 是不同的。举例:</p><blockquote><p>import ilog -> {“ilog”: “xxxxxx”}</p></blockquote><blockquote><p>from epflower import ilog -> {“epflower.ilog”: “xxxxxx”}</p></blockquote><p><strong>这就导致,ilog 这个模块会被重复加载到内存当中,并且是两个不同的东西。</strong></p><p>所以当主进程采用直接 import ilog 这种方式引用,子进程当中一旦有地方使用了包结构的 import 方式,就会产生非 dup() 主进程的 fd。在等待获取锁的时候会被永久阻塞。</p><h2 id="改"><a href="#改" class="headerlink" title="改"></a>改</h2><p>这里要改,方法不止一种,使用强制锁比较麻烦,还可以重写 emit() 函数,直接往对应日期的文件塞日志,去掉 rotate 机制。还有一个最简单的方法,不使用 self.stream 这个 fd 去获取锁,直接 open(self.baseFilename) 获取一个没有任何关联的 fd 来获取锁,一来可以避免 fork 进程,劝告锁失效的问题,二来只需要 close 一个 fd 就可以释放锁了。上代码!</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">doRollover</span>(<span class="params">self</span>):</span><br><span class="line"> <span class="comment">#正常先释放,避免超过 rotate 时间节点的日志写入</span></span><br><span class="line"> <span class="keyword">if</span> self.stream:</span><br><span class="line"> self.stream.close()</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 根据时间戳以及 baseFilename 计算出 rotate 后的文件名</span></span><br><span class="line"> dfn = self.baseFilename + <span class="string">"."</span> + time.strftime(self.suffix, timeTuple)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 使用文件锁,保证 rotate 过程的原子性</span></span><br><span class="line"> log_fd = <span class="built_in">open</span>(self.baseFilename, <span class="string">"a"</span>)</span><br><span class="line"> fcntl.flock(log_fd, fcntl.LOCK_EX)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 不存在才rename, 存在说明其他进程roller过</span></span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> os.path.exists(dfn) <span class="keyword">and</span> os.path.exists(self.baseFilename):</span><br><span class="line"> self.rotate(self.baseFilename, dfn)</span><br><span class="line"> </span><br><span class="line"> log_fd.close()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 根据设置的最大保留文件数删除旧日志</span></span><br><span class="line"> <span class="keyword">if</span> self.backupCount > <span class="number">0</span>:</span><br><span class="line"> <span class="keyword">for</span> s <span class="keyword">in</span> self.getFilesToDelete():</span><br><span class="line"> os.remove(s)</span><br><span class="line"></span><br><span class="line"> ...</span><br><span class="line"> </span><br><span class="line"> self.rolloverAt = newRolloverAt</span><br></pre></td></tr></table></figure><h2 id="结"><a href="#结" class="headerlink" title="结"></a>结</h2><p>以上就是这次 CLOSE_WAIT 的分析过程,如有疑惑之处,抑或高明见解,欢迎讨论。</p><p>接下来还会写一篇关于多进程如何安全地写入文件的文章,深挖到 Linux 内核代码来探讨一下文件读写的过程与本质</p><h2 id="链"><a href="#链" class="headerlink" title="链"></a>链</h2><p><a href="https://docs.python.org/3/library/fcntl.html#fcntl.flock">fcntl.flock()</a></p><p>[flock()](</p>]]></content>
<summary type="html"><h2 id="起"><a href="#起" class="headerlink" title="起"></a>起</h2><p>这个事情从一个新的 tornado 服务说起,这个服务部署到预发布环境之后,会有一个流水线定时每个小时触发访问塞数据到这个服务,但是每天晚上12点服</summary>
<category term="技术" scheme="https://sherlozk.github.io/categories/%E6%8A%80%E6%9C%AF/"/>
<category term="Python" scheme="https://sherlozk.github.io/tags/Python/"/>
</entry>
<entry>
<title>与自己的对话</title>
<link href="https://sherlozk.github.io/2020/02/13/haha/"/>
<id>https://sherlozk.github.io/2020/02/13/haha/</id>
<published>2020-02-12T18:46:36.000Z</published>
<updated>2022-11-19T03:09:41.094Z</updated>
<content type="html"><![CDATA[<p>今天聊天,从工作聊到近况聊到从前再到生活人生。</p><p>让我又回想了一次许许多多的事情。趁余兴还在,记录一下吧。</p><span id="more"></span><p>可能有一部分的人跟我一样,工作的辛苦反而是久违的放松。</p><p>虽说我成长在教师家庭,但从小到大过得却不那么像在书香门第。总在奔波于生计,父亲爱摄影,为了把相机和胶卷的钱赚回来,他总带着我去给学生们拍生活照,那时候艺术照还是个奢侈品,但我们生活照只收两毛钱一张,『过胶』的话三毛钱。那时候中学生的一周零花钱是五毛,愿意拿两毛到三毛来留下自己模样的人络绎不绝。结果半年不到就把钱给赚回来了。</p><p>按理说,这样应该是赚到了第一桶金,生活自然会宽松些。然而好景不长,爷爷肝癌晚期,所有的钱都拿去治病,仍是不够。可是在生命面前,欠再多的钱,也不愿放弃。时至今日,我仍然会不时梦到在一条长提上,一辆双杠自行车,一位白发老人,载着一个黄发小儿,江水是红色的,因为斜阳在山那头…还有他在被宣告无可救治之后,回到家中,在病榻前最后教我的那首《静夜诗》…以及那个父亲叫我不用去上学,穿好衣服回老家的下午,我仿佛懂得了些什么。那年我六岁。</p><ul><li>我经常抬头望,明月我看到了,可哪颗星星是你。</li></ul><p>后来我们还干起了其他行当:小卖部、食堂加菜部、卖计算器…当然少不了,放学回来都会有几百张照片等着我过胶。那是 2003 年,我九岁。恰逢涝灾,学校募捐,当时五块钱是『起捐点』,当然这只是因为小孩子的虚荣心而定出来的。但是我再三争取,只拿到了两块钱。可第二天我依然上缴了五块钱,多出那三块是我捡了一麻袋塑料瓶子卖的。那天我从母亲的眼睛里看到两种不同的闪光…</p><p>时间来到了高中,多年的摄影游击在初中的时候结束了,母亲在菜市场马路对面开了一家小相馆,以证件照为主,附带若干小业务。父亲继续教书,只是已经不再需要做教案和备课。摄影明明一开始是父亲的爱好,最后却成了母亲的职业。这个地方也成了我课余主要的活动场所,『不好好读书就去店里帮忙』、『干活不努力的人,读书也不会努力』成了父亲训我的口头禅。以至于去店里帮忙慢慢成立我的习惯,即使现在工作了,回家也不是直接从车站回家,而是先去店里帮忙,晚上再收拾一起回家。好处是,朋友们总知道该去哪里找我。而我唯一能做的就是和他们在店门前的大树下站着聊半个小时,能坚持和我做多年朋友的,大概都是真正的朋友吧…</p><p>后来朋友们说我没童年,我说我有,偷偷去黑网吧被抓回来,差点打断腿;打架,差点被记过;成绩差,差点被退学,我一样没少。下河摸鱼、小溪抓蝌蚪、爬树偷果子、石板台睡午觉、操场放风筝、台风天等芒果掉下来、还没灶台高的我煎鸡蛋被烫伤。</p><ul><li>这些都是我的童年,无论好坏。</li></ul><p>时间很快到了大二,我开始迷茫了,读书为了什么?以前读书是为了高考,现在呢?为了找到好工作,赚钱?好像找不到什么问题。那就先试试赚钱吧!除了摄影我与父亲还有一个共同爱好--音乐。不同的是他的二胡是从垃圾堆里面捡回来的,我的吉他是他花钱给我买的。我试着去接一些商演,价格从几十块钱一首到两三百一首不等,后来门路宽些之后,最高有到过一千块一首的场合。我不喜酒吧的氛围,所以没有做过驻唱。半年过去,该挂的课一科没落,但这不是重点。关键是突然有一天我意识到,我是在做自己喜欢的事情吗?为了迎合大众,我学了很多俗套的歌曲,出入在没有真正属于我的听众的场合。我是活跃了气氛,可我的心怎么也暖不起来…</p><ul><li>兴趣如果不能做到极致,那还是让它保持纯粹吧。</li></ul><p>最后我还是回到了正轨,回到相比不太需要与人打交道的行当之中。孤独成为了我主要需要面对的问题。毕业只身来到深圳,每天早上等二十分钟的电梯是我与最多人共处的时候,可每个人的手掌都有自己的天地,并不会理会旁边是谁。晚上下班两百号人排队打车,几乎让我每天走一个小时回家。走在路上会对我笑笑的只有天桥上比我更晚下班的流浪汉和街头卖艺人。『家』就是需要自己打扫卫生的旅馆,只为图个安睡。刚开始只有潇洒自由,自给自足很快乐。但当作息越来越乱,饮食越来越不均衡,最为致命的是周围的人开始讨论深圳房价又涨了的时候,我开始有点怀疑人生,不禁问,这真的是我能承受的吗?无论是近一点的二手楼还是远一点的一手楼,价格都让我望洋兴叹。</p><ul><li>深圳赚钱深圳花,一分别想带回家。</li></ul><p>于是一年后,我一边笑着一边可耻地逃回了广州。这里有我熟悉的人情味,爱管闲事的大叔,在街边总是小小声聊天的大妈,骑着单车卖益力多的阿姨,还有我可爱的朋友们,以及那可以安身立命的机会。于是孤独不再是我主要需要面对的问题,让我有闲暇思考一些别的问题。</p><ul><li>比如,工作是为了什么?</li></ul><p>虽然我也尝试过像龙哥一样去思考人存在的意义,但花了三秒钟我就明白,刚解决温饱问题的我是想不明白的,还是想想工作吧。工作上有一个人为划分的等级,半年一次的考评会决定你能否升级,有点像游戏里打怪升级。而『专家』是竖在前面的一座山峰,我也想登上去,成为一个领域的专家。可是细想,专家只不过是别人给你的一个称号,在这十年或者更长的时间里,是什么可以让我坚持下去。</p><ul><li>实现理想和价值。</li></ul><p>人类的一生都在追寻边缘,但真正到达边缘的人少之又少。最让人兴奋的就是走在边缘的时候,世界因为你范围又扩大了一点,人们的视线又远了一点的时候。而我想做的,只是找一个喜欢的方向,一直往前走,走到没人的地方。你问我那是什么样一种状态,我没好意思说我自己,就给你举了『学霸』的例子。</p><p>学校或者班级是一个小的世界,永远考第一名的人,看到的知识和其他的人是不一样的。这时候课本和考纲就是那个边缘,考第一名的人在看课本和考纲外的知识,他能考第一名除了努力做题之外,还在于他在摸索边缘外的知识,利用课本没教的知识去帮助他理解这个世界里面的东西。</p><p>当追寻边缘成为一种习惯,到了大学,他慢慢开始思考一些学习之外的东西了,以前学习是为了高考,现在学习是为了什么,所以会比别人更早去接触社会,更早地去尝试,看看自己真正喜欢什么。所以这是一条人越少越让人兴奋的路。</p><ul><li>顺便养活自己和家人。</li></ul><p>每个人的理想和价值我不得而知,但是责任和义务大抵是相近的。所谓安身立命,精神得到了寄托,还需生活有着落,方能在社会立足。</p><p>实现理想和价值,顺便养活自己和家人。就是我现在坚持的动力。</p>]]></content>
<summary type="html"><p>今天聊天,从工作聊到近况聊到从前再到生活人生。</p>
<p>让我又回想了一次许许多多的事情。趁余兴还在,记录一下吧。</p></summary>
<category term="散文" scheme="https://sherlozk.github.io/categories/%E6%95%A3%E6%96%87/"/>
<category term="随笔" scheme="https://sherlozk.github.io/tags/%E9%9A%8F%E7%AC%94/"/>
</entry>
<entry>
<title>逆向从 Instruments 中获取 GPU 数据</title>
<link href="https://sherlozk.github.io/2018/02/14/traceDump/"/>
<id>https://sherlozk.github.io/2018/02/14/traceDump/</id>
<published>2018-02-14T15:12:36.000Z</published>
<updated>2022-11-19T03:11:36.148Z</updated>
<content type="html"><![CDATA[<p> 背景: RTMP SDK需要获取硬编硬解时候的GPU数据,第一时间想起了<code>TraceParser</code>, 但是<code>TraceParser</code>不支持<code>GPU Driver</code>模板. 于是想着能不能扩展<code>TraceParser</code>模板. 发现<code>main.m</code>文件只有寥寥几行代码,完全不知道做了什么, 但是google和km之后发现应该是采用了反序列化的方式来dump出数据. 缺点很明显, 需要自己实现<code>NSCoding</code>,还需要先将.trace文件解压出.run文件. 不知道结构无从下手.</p><p>在 github 上发现有一种新的思路<a href="https://github.com/Qusic/TraceUtility?spm=a2c4e.11153940.blogcont398701.14.609e5861UgLC7e">TraceUtility</a>, 通过调用 Undocument API 去解析 Trace 文件.<br>下面结合 Hopper 分析<code>Instruments</code>看看. </p><p><img src="/2018/02/14/traceDump/42a27c3d-000f-485b-bd92-5ee096bf8827.png"></p><p>在逆向过程中发现打开文档用的是<code>NSDocument</code>,<code>-showTemplateChooser:</code>模板选择器. 这意味着一个trace文件是通过<code>NSDocument</code>加上自定义文档类型(模板)来储存的, 当然这个文件类型并不会存在于<a href="http://www.w3school.com.cn/media/media_mimeref.asp">MIME</a>当中的. </p><p><img src="/2018/02/14/traceDump/be4c10be-44ac-4151-8c6e-ecacd18a55e1.png"><br>在<code>-initialize:</code>中对 Instruments 做了初始化, 包括一些链接 XCode 中 ShareFramework 的 Undocument 库.</p><p><img src="/2018/02/14/traceDump/e6b8897e-7a6d-424d-b7ac-3ce315e83307.png"><br>在初始化完成之后,还有很重要的一步 —- 加载插件(<code>PFTLoadPlugins()</code>), XCode 8之后, Instruments 的插件就从<code>Plugins</code>文件夹下移到了<code>Packages</code>文件下面, 并且用了新的打包方式,以<code>.instrdst</code>扩展名结尾, 打开之后可以安装插件.如果不去安装, 在后面编码阶段发现是会抛除异常提示的. 相对的, 在<code>-terminate:</code>里面有<code>PFTClosePlugins()</code>. 基于以上条件, 可以直接通过调用接口来加载模板, 并且只有在加载了正确加载模板插件之后才能读入 Trace 文件. </p><p><img src="/2018/02/14/traceDump/2018-02-11-20-17-01.png"></p><p><img src="/2018/02/14/traceDump/2018-02-12-09-12-31.jpg"></p><p>在加载完 trace 文件之后就可以开始抽丝剥茧了.</p><p><img src="/2018/02/14/traceDump/2018-02-12-09-16-07.jpg"><br>根据这里的调试信息, 去 dump 出来的 instruments 头文件中搜索出需要的类, 放到自己的头文件当中, 成员变量的获取需要用到 runtime 特性.以我需要的 GPU 数据来说, 最后的层级关系如下:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">PFTDocuments > XRTrace > XRInstrument > XRRun > _data</span><br><span class="line">或</span><br><span class="line">PFTDocuments > XRTrace > XRInstrument >XRAnalysisCoreStandardController > XRContextContainer </span><br></pre></td></tr></table></figure><p><img src="/2018/02/14/traceDump/2018-02-12-19-17-48.jpg"></p><p>最后在格式化输出就好了. </p><p><img src="/2018/02/14/traceDump/2018-02-14-23-32-49.jpg"> </p><p>配合 Python 脚本可以直接输出成 Excel 的形式更加可观, 甚至可以自动化跑起来,每天直接输出邮件, 监控迭代性能表现. </p><p><img src="/2018/02/14/traceDump/2018-02-14-23-41-47.jpg"></p>]]></content>
<summary type="html"><p> 背景: RTMP SDK需要获取硬编硬解时候的GPU数据,第一时间想起了<code>TraceParser</code>, 但是<code>TraceParser</code>不支持<code>GPU Driver</code>模板. 于是想着能不能扩展<code>Tra</summary>
<category term="技术" scheme="https://sherlozk.github.io/categories/%E6%8A%80%E6%9C%AF/"/>
<category term="iOS逆向" scheme="https://sherlozk.github.io/tags/iOS%E9%80%86%E5%90%91/"/>
</entry>
<entry>
<title>不懂汇编,如何逆向(iOS)</title>
<link href="https://sherlozk.github.io/2017/10/22/ios-hook/"/>
<id>https://sherlozk.github.io/2017/10/22/ios-hook/</id>
<published>2017-10-22T04:10:38.000Z</published>
<updated>2022-11-19T03:10:00.018Z</updated>
<content type="html"><![CDATA[<p>** $ 写给像我一样的小白**</p><span id="more"></span><h2 id="0x1-逆向一个APP有哪些步骤-不越狱"><a href="#0x1-逆向一个APP有哪些步骤-不越狱" class="headerlink" title="0x1 逆向一个APP有哪些步骤(不越狱)"></a>0x1 逆向一个APP有哪些步骤(不越狱)</h2><ol><li>砸壳</li><li>dump出头文件</li><li>分析功能界面</li><li>hopper || iDA 分析伪代码</li><li>写hook</li><li>打包动态库</li><li>注入动态库到APP</li><li>APP重签名</li><li>安装到手机上</li></ol><h3 id="MonkeyDev"><a href="#MonkeyDev" class="headerlink" title="MonkeyDev"></a>MonkeyDev</h3><p><code>MonkeyDev</code>是一个xcode插件, 此处先膜一下<code>@庆哥</code></p><pre><code>原有iOSOpenDev的升级,非越狱插件开发集成神器!- 可以使用Xcode开发CaptainHook Tweak、Logos Tweak 和 Command-line Tool,在越狱机器开发插件,这是原来iOSOpenDev功能的迁移和改进。- 只需拖入一个砸壳应用,自动集成class-dump、restore-symbol、Reveal、Cycript和注入的动态库并重签名安装到非越狱机器。- 支持调试自己编写的动态库和第三方App- 支持通过CocoaPods第三方应用集成SDK以及非越狱插件,简单来说就是通过CocoaPods搭建了一个非越狱插件商店。</code></pre><p>庆哥的github如是说.</p><p><code>MonkeyDev</code>解决了上面说到的50%的步骤, 再外加一个动态调试.</p><p><a href="https://github.com/AloneMonkey/MonkeyDev">https://github.com/AloneMonkey/MonkeyDev</a></p><p>** PS:问问题之前先熟读wiki**</p><h2 id="0x2-剩下的工作"><a href="#0x2-剩下的工作" class="headerlink" title="0x2 剩下的工作"></a>0x2 剩下的工作</h2><h3 id="砸壳"><a href="#砸壳" class="headerlink" title="砸壳"></a>砸壳</h3><p>其实这个是非必要项, 自己手动砸壳需要已越狱的手机. 想手动砸壳可以参考这篇文章.</p><p><a href="http://www.jianshu.com/p/4cfd86600ced?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io">iOS逆向工程之Clutch砸壳</a></p><p>不想自己手动砸壳的可以去各大应用平台,如PP助手等.下载越狱版的软件,这些都是已经砸好壳的了.</p><h3 id="分析功能界面-动态分析"><a href="#分析功能界面-动态分析" class="headerlink" title="分析功能界面(动态分析)"></a>分析功能界面(动态分析)</h3><p>主流有两种方法:</p><ol><li><a href="http://www.jianshu.com/p/621210ec3ff0">Cycript</a></li><li><a href="http://www.jianshu.com/p/f2970ef365fe">Reveal</a></li></ol><p>但是使用了<code>MonkeyDev</code>之后多了一种方法. hook oc的消息通知函数,如:<a href="https://github.com/qhd/ANYMethodLog">ANYMethodLog</a>.</p><p>因为每个app启动的时候都会调用appDelegate里面的<code>didFinishLaunchingWithOptions:</code>这个方法, 也就是说每个app都会有这个方法. 那我们可以在class-dump出来的头文件中找到某个app的<code>appDelegate</code>文件名,然后hook掉<code>didFinishLaunchingWithOptions:</code>把所有基于UIViewController或者其他类执行的方法在运行的时候全部打出来.甚至连函数的参数都可打印出来.</p><h3 id="分析伪代码-静态分析"><a href="#分析伪代码-静态分析" class="headerlink" title="分析伪代码(静态分析)"></a>分析伪代码(静态分析)</h3><p>上面一步分析功能界面是为了定位具体要hook的函数, 当定位出来要hook的函数之后, 就要去分析函数的具体实现了.</p><p><a href="https://www.hopperapp.com/">Hopper</a><br><a href="https://www.hex-rays.com/products/ida/index.shtml">iDA</a></p><p>这两个都是收费的,但都提供了demo版,只是做静态分析的话已经够用了.</p><p><a href="http://www.jianshu.com/p/176c5e1d06ae">IDA + Hopper 逆向开发近期学习</a></p><p>我们在这一步的目的只是为了搞清楚函数的实现和函数之间的调用关系, 所以并不需要去直接修改汇编或者二进制代码, 只是反编译出来的伪代码有可能也会带有一下寄存器或者内存地址等一些看不懂的信息,完全可以把这些当做成命名简单的变量,我们只需要看懂其中的逻辑就好了. </p><h3 id="编写hook代码"><a href="#编写hook代码" class="headerlink" title="编写hook代码"></a>编写hook代码</h3><p>OK, 现在要hook 的函数已经找到了,函数具体的实现也已经知道, 那下一步当然就是编写代码把函数hook掉.</p><h4 id="captainhook"><a href="#captainhook" class="headerlink" title="captainhook"></a>captainhook</h4><p>在<code>MonkeyDev</code>中提供了<code>logos</code>和<code>captainhook</code>两种语法,用来编写hook代码.如果原来做过越狱开发的应该比较喜欢用<code>logos</code>,网上的教程也比较多.但是, 我学习的时候选择用<code>captainhook</code>(两个都好用,纯粹个人喜好).这里简单说一下写代码的过程:</p><h5 id="1-如何调用已有的类和方法"><a href="#1-如何调用已有的类和方法" class="headerlink" title="1) 如何调用已有的类和方法"></a>1) 如何调用已有的类和方法</h5><p>如果需要使用到类的属性或类方法,最好自行创建一个头文件把<code>@interface</code>写进去,然后import这个头文件,写hook的时候就可以找到相应的属性了,但是如果你想通过这种方式给类添加属性是行不通的,我猜测是因为在程序运行的过程当中,内存的分配已经完成,想要添加属性值进去就需要对这个对象的内存进行扩容或者重新分配,但是通过写在自定义的头文件里面属性值,虽然是在同名的类下面,但是并不会添加在原来代码申请的内存当中,所以当你调用这个自己添加的属性的时候,原对象是找不到访问不了这个属性的,类似于<code>Category</code>.</p><p>如果一定要添加属性,必须实现该属性的<code>get</code>和<code>set</code>方法,在里面调用runtime的外联方法<code>objc_getAssociatedObject</code>使对象和属性建立映射关系,这样在运行时对象才能找到你添加的属性.详细参考:<a href="http://blog.leichunfeng.com/blog/2015/06/26/objective-c-associated-objects-implementation-principle/">Objective-C Associated Objects 的实现原理</a></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@interface</span> CMessageWrap</span><br><span class="line"></span><br><span class="line"><span class="meta">@property(nonatomic, strong)</span> NSString* m_nsContent; <span class="comment">//发送消息的内容</span></span><br><span class="line"><span class="meta">@property(nonatomic, strong)</span> NSString* m_nsToUsr; <span class="comment">//发送人</span></span><br><span class="line"><span class="meta">@property(nonatomic, strong)</span> NSString* m_nsFromUsr; <span class="comment">//接收人</span></span><br><span class="line"><span class="meta">@property(nonatomic, strong)</span> NSMutableString *m_nsPushContent; </span><br><span class="line"></span><br><span class="line">+ (BOOL)isSenderFromMsgWrap:(CMessageWrap*) msgWrap;</span><br><span class="line"></span><br><span class="line">- (CMessageWrap*)initWithMsgType:(<span class="type">int</span>) type;</span><br><span class="line"></span><br><span class="line"><span class="meta">@end</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><h5 id="2-CHDeclareClass"><a href="#2-CHDeclareClass" class="headerlink" title="2) CHDeclareClass"></a>2) CHDeclareClass</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">#define <span class="title function_">CHDeclareClass</span><span class="params">(name)</span> \</span><br><span class="line"><span class="meta">@class</span> name; \</span><br><span class="line"><span class="keyword">static</span> CHClassDeclaration_ name ## $;</span><br></pre></td></tr></table></figure><p>这个宏用于声明你要hook的类.</p><h5 id="3-CHOptimizedMethod"><a href="#3-CHOptimizedMethod" class="headerlink" title="3) CHOptimizedMethod"></a>3) CHOptimizedMethod</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">#define <span class="title function_">CHOptimizedMethod1</span><span class="params">(optimization, return_type, class_type, name1, type1, arg1)</span> \</span><br><span class="line">CHMethod_ ## optimization ## _(return_type, class_type *, class_type, CHClass(class_type), CHSuperClass(class_type), name1 ## $, name1:, CHDeclareSig1_(return_type, type1), (self, _cmd, arg1), type1 arg1)</span><br></pre></td></tr></table></figure><p><code>CHOptimizedMethod</code>编写你要hook的方法,这个宏后面跟着一个数字,[0-9]代表着你要hook的方法的参数个数.</p><h5 id="4-CHSuper"><a href="#4-CHSuper" class="headerlink" title="4) CHSuper"></a>4) CHSuper</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">#define <span class="title function_">CHSuper1</span><span class="params">(class_type, name1, val1)</span> \</span><br><span class="line">CHSuper_(class_type, <span class="meta">@selector(name1:)</span>, name1 ## $, val1)</span><br></pre></td></tr></table></figure><p><code>CHSuper</code>用于在<code>CHOptimizedMethod</code>内执行完自己的代码之后继续执行原函数的代码.</p><h5 id="5-CHConstructor"><a href="#5-CHConstructor" class="headerlink" title="5) CHConstructor"></a>5) CHConstructor</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">#define CHConstructor <span class="keyword">static</span> <span class="title function_">__attribute__</span><span class="params">((constructor)</span>) <span class="keyword">void</span> <span class="title function_">CHConcat</span><span class="params">(CHConstructor, __LINE__)</span><span class="params">()</span></span><br></pre></td></tr></table></figure><p>在<code>__attribute__((constructor))</code>后的内容能保证在 dylib 加载时运行.</p><h5 id="6-CHLoadLateClass"><a href="#6-CHLoadLateClass" class="headerlink" title="6) CHLoadLateClass"></a>6) CHLoadLateClass</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">#define <span class="title function_">CHLoadLateClass</span><span class="params">(name)</span> CHLoadClass_(&name ## $, objc_getClass(#name))</span><br></pre></td></tr></table></figure><p>加载需要hook的类,写在<code>CHConstructor</code>里面.</p><h5 id="7-CHClassHook"><a href="#7-CHClassHook" class="headerlink" title="7) CHClassHook"></a>7) CHClassHook</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">#define <span class="title function_">CHClassHook1</span><span class="params">(class, name1)</span> CHHook_(class, name1 ## $)</span><br></pre></td></tr></table></figure><p>加载需要hook的方法,写在<code>CHConstructor</code>里面.</p><h5 id="8-CHDeclareMethod"><a href="#8-CHDeclareMethod" class="headerlink" title="8) CHDeclareMethod"></a>8) CHDeclareMethod</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">#define <span class="title function_">CHDeclareMethod1</span><span class="params">(return_type, class_type, name1, type1, arg1)</span> \</span><br><span class="line">CHDeclareMethod_(return_type, class_type *, class_type, CHClass(class_type), CHSuperClass(class_type), name1 ## $, name1:, CHDeclareSig1_(return_type, type1), (self, _cmd, arg1), type1 arg1)</span><br></pre></td></tr></table></figure><p>声明和实现自己的方法, 要注意声明要写在调用之前.</p><h5 id="9-Sample"><a href="#9-Sample" class="headerlink" title="9) Sample"></a>9) Sample</h5><p>这是我hook了微信聊天页面出现和消失两个代理方法的例子…</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//聊天基本页面</span></span><br><span class="line">CHDeclareClass(BaseMsgContentViewController)</span><br><span class="line"></span><br><span class="line"><span class="comment">//实现自己新加的方法</span></span><br><span class="line">CHDeclareMethod1(<span class="keyword">void</span>, MMUIViewController, backToMsgContentViewController, id, sender){</span><br><span class="line"> [sender removeFromSuperview];</span><br><span class="line"> UINavigationController *navi = [objc_getClass(<span class="string">"CAppViewControllerManager"</span>) getCurrentNavigationController];</span><br><span class="line"> LKButton *btn = (LKButton *)sender;</span><br><span class="line"> [LKNewestMsgManager sharedInstance].didTouchBtnName = btn.username;</span><br><span class="line"> [[NSNotificationCenter defaultCenter] postNotificationName:@<span class="string">"btnDidTouch"</span> object:nil];</span><br><span class="line"> MMServiceCenter* serviceCenter = [objc_getClass(<span class="string">"MMServiceCenter"</span>) defaultCenter];</span><br><span class="line"> CContactMgr *contactMgr = [serviceCenter getService:[objc_getClass(<span class="string">"CContactMgr"</span>) class]];</span><br><span class="line"> CContact *contact = [contactMgr getContactByName:btn.username];</span><br><span class="line"></span><br><span class="line"> MMMsgLogicManager *logicManager = [serviceCenter getService:[objc_getClass(<span class="string">"MMMsgLogicManager"</span>) class]];</span><br><span class="line"> [logicManager PushOtherBaseMsgControllerByContact:contact navigationController:navi animated:YES];</span><br><span class="line"></span><br><span class="line">}</span><br><span class="line"><span class="comment">//hook viewDidAppear 方法</span></span><br><span class="line">CHOptimizedMethod1(self, <span class="keyword">void</span>, BaseMsgContentViewController, viewDidAppear, BOOL, flag){</span><br><span class="line"> [LKNewestMsgManager sharedInstance].currentChat = [(BaseMsgContentViewController*)[[LKNewestMsgManager sharedInstance] getCurrentVC]getCurrentChatName];</span><br><span class="line"></span><br><span class="line"> NSLog(@<span class="string">"%@"</span>, [LKNewestMsgManager sharedInstance].currentChat);</span><br><span class="line"> CHSuper1(BaseMsgContentViewController, viewDidAppear, flag);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//hook viewWillDisappear 方法</span></span><br><span class="line">CHOptimizedMethod1(self, <span class="keyword">void</span>, BaseMsgContentViewController, viewWillDisappear, BOOL, disappear){</span><br><span class="line"> [LKNewestMsgManager sharedInstance].currentChat = NULL;</span><br><span class="line"> CHSuper1(BaseMsgContentViewController, viewWillDisappear, disappear);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//注册需要hook的类和方法</span></span><br><span class="line">CHConstructor{</span><br><span class="line"> CHLoadLateClass(BaseMsgContentViewController);</span><br><span class="line"> CHClassHook1(BaseMsgContentViewController, viewWillDisappear);</span><br><span class="line"> CHClassHook1(BaseMsgContentViewController, viewDidAppear);</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="0x3-做些有趣的事情"><a href="#0x3-做些有趣的事情" class="headerlink" title="0x3 做些有趣的事情"></a>0x3 做些有趣的事情</h2><p>我平常喜欢在微信公众号看些文章, 但是这时候如果有人发消息过来, 手机震了一下…….但是你并不知道是谁发来的消息, 这时候,按照微信培养的用户习惯….置顶保存文章,然后点击n个返回按钮然后点击<code>close</code>返回消息页面…..回了消息然后在回去看文章…..</p><p>so,这就是痛点所在,不能快速查看回复消息.</p><p>搞起来….</p><h3 id="功能设想"><a href="#功能设想" class="headerlink" title="功能设想"></a>功能设想</h3><p>在任意页面, 当接收到异步消息, 通知当前页面弹出一个按钮提示, 点击按钮 push 对应聊天页面, pop 可返回原来的页面. </p><h3 id="定位函数"><a href="#定位函数" class="headerlink" title="定位函数"></a>定位函数</h3><p>一开始只是因为看文章的痛点,只想hook webVC页面就好了,但是后来细想,当你在弄设置或者干其他事情的时候其实也有同样的问题,干脆直接搞底层<code>UIViewController</code>, 但是在分析的过程中,发现微信有一个自己实现的<code>MMUIViewController</code>.如此甚好, 直接搞它. </p><p>通过动态分析的方法快速定位到需要hook的类:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">BaseMsgContentViewController <span class="comment">//基本聊天页面</span></span><br><span class="line">MMUIViewController <span class="comment">//VC基类</span></span><br><span class="line">CMessageMgr <span class="comment">//消息接收类</span></span><br></pre></td></tr></table></figure><p>对于<code>BaseMsgContentViewController</code>和<code>MMUIViewController</code>我们目的很明确,就是监听通知,当有消息来的时候,弹出按钮.</p><p>这里可能有疑问,<code>BaseMsgContentViewController</code>应该也是继承<code>MMUIViewController</code>的,为什么还要单独hook. 原因很简单,因为你在和某人的聊天页面当中,当然不应该在弹出这个人的消息按钮.</p><p>接下来就是借助<code>class-dump</code>和<code>Hopper</code>去定位和分析函数, 比如,我这里需要分析的就是点击按钮之后,如何跳转到对应的聊天页面.</p><h3 id="hook"><a href="#hook" class="headerlink" title="hook"></a>hook</h3><p>OK, 所有需要用到的消息都拿到了, 开始写hook代码.</p><p>下面是一些关键代码,全部的代码在github:<a href="https://github.com/sherlockZ/LKMessageSwitchPod">LKMessageSwitchPod</a></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// hook 消息接收类</span></span><br><span class="line">CHDeclareClass(CMessageMgr)</span><br><span class="line">CHOptimizedMethod2(self, <span class="keyword">void</span>, CMessageMgr, AsyncOnAddMsg, NSMutableString*, msg, MsgWrap, CMessageWrap*, wrap){</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>(![wrap.m_nsPushContent isEqual: @<span class="string">""</span>] && wrap.m_nsPushContent != NULL){</span><br><span class="line"> [LKNewestMsgManager sharedInstance].username = msg;</span><br><span class="line"> [LKNewestMsgManager sharedInstance].content = wrap.m_nsPushContent;</span><br><span class="line"> [[NSNotificationCenter defaultCenter] postNotificationName:@<span class="string">"LkWechatMessageNotification"</span> object:nil];</span><br><span class="line"> }</span><br><span class="line"> CHSuper2(CMessageMgr, AsyncOnAddMsg, msg, MsgWrap, wrap);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">CHDeclareMethod1(<span class="keyword">void</span>, MMUIViewController, backToMsgContentViewController, id, sender){</span><br><span class="line"> [sender removeFromSuperview];</span><br><span class="line"> UINavigationController *navi = [objc_getClass(<span class="string">"CAppViewControllerManager"</span>) getCurrentNavigationController];</span><br><span class="line"></span><br><span class="line"> LKButton *btn = (LKButton *)sender;</span><br><span class="line"> [LKNewestMsgManager sharedInstance].didTouchBtnName = btn.username;</span><br><span class="line"> [[NSNotificationCenter defaultCenter] postNotificationName:@<span class="string">"btnDidTouch"</span> object:nil];</span><br><span class="line"> MMServiceCenter* serviceCenter = [objc_getClass(<span class="string">"MMServiceCenter"</span>) defaultCenter];</span><br><span class="line"> CContactMgr *contactMgr = [serviceCenter getService:[objc_getClass(<span class="string">"CContactMgr"</span>) class]];</span><br><span class="line"> CContact *contact = [contactMgr getContactByName:btn.username];</span><br><span class="line"> MMMsgLogicManager *logicManager = [serviceCenter getService:[objc_getClass(<span class="string">"MMMsgLogicManager"</span>) class]];</span><br><span class="line"> [logicManager PushOtherBaseMsgControllerByContact:contact navigationController:navi animated:YES];</span><br><span class="line"></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">CHDeclareMethod0(<span class="keyword">void</span>, MMUIViewController, messageCallBack){</span><br><span class="line"> NSLog(@<span class="string">"收到消息!!!"</span>);</span><br><span class="line"></span><br><span class="line"> NSString *currentChatName = [LKNewestMsgManager sharedInstance].currentChat;</span><br><span class="line"> <span class="keyword">if</span>(self == [[LKNewestMsgManager sharedInstance]getCurrentVC] && ![currentChatName isEqual: [LKNewestMsgManager sharedInstance].username]){</span><br><span class="line"></span><br><span class="line"> LKButton *btn = [LKButton buttonWithType:UIButtonTypeRoundedRect];</span><br><span class="line"> btn.frame = CGRectMake(self.view.frame.size.width-<span class="number">100</span>-<span class="number">2</span>, <span class="number">74</span>, <span class="number">100</span>, <span class="number">40</span>);</span><br><span class="line"> btn.backgroundColor = [[UIColor blackColor]colorWithAlphaComponent:<span class="number">0.8</span>];</span><br><span class="line"> btn.tintColor = [UIColor whiteColor];</span><br><span class="line"> [btn setTitle:[LKNewestMsgManager sharedInstance].content forState:UIControlStateNormal];\</span><br><span class="line"> btn.username = [LKNewestMsgManager sharedInstance].username;</span><br><span class="line"> btn.clipsToBounds = YES;</span><br><span class="line"> btn.layer.cornerRadius = <span class="number">10</span>;</span><br><span class="line"> btn.contentEdgeInsets = UIEdgeInsetsMake(<span class="number">2</span>, <span class="number">2</span>, <span class="number">2</span>, <span class="number">2</span>);</span><br><span class="line"> [btn addTarget:self action:<span class="meta">@selector(backToMsgContentViewController:)</span> forControlEvents:UIControlEventTouchUpInside];</span><br><span class="line"> [btn registerNotification];</span><br><span class="line"> btn.swipeGestureRecognizer = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:<span class="meta">@selector(handleSwipes:)</span>];</span><br><span class="line"> btn.swipeGestureRecognizer.direction = UISwipeGestureRecognizerDirectionRight;</span><br><span class="line"> btn.swipeGestureRecognizer.numberOfTouchesRequired = <span class="number">1</span>;</span><br><span class="line"> [btn addGestureRecognizer:btn.swipeGestureRecognizer];</span><br><span class="line"></span><br><span class="line"> [self.view addSubview:btn];</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="效果"><a href="#效果" class="headerlink" title="效果"></a>效果</h3><p><img src="/2017/10/22/ios-hook/1508489189.gif" alt="1508489189.gif"></p>]]></content>
<summary type="html"><p>** $ 写给像我一样的小白**</p></summary>
<category term="技术" scheme="https://sherlozk.github.io/categories/%E6%8A%80%E6%9C%AF/"/>
<category term="iOS逆向" scheme="https://sherlozk.github.io/tags/iOS%E9%80%86%E5%90%91/"/>
</entry>
<entry>
<title>闭上眼,你看到的更多</title>
<link href="https://sherlozk.github.io/2017/08/27/party/"/>
<id>https://sherlozk.github.io/2017/08/27/party/</id>
<published>2017-08-26T17:00:50.000Z</published>
<updated>2022-11-19T03:10:52.355Z</updated>
<content type="html"><![CDATA[<p>今天以一个志愿者的身份参加了一场特殊的音乐会,地点在深圳市残疾人综合服务中心的13楼.</p><span id="more"></span><p>我参加过很多音乐会, 但都是以观众或者表演者的身份, 一周前看到事业群发来邮件, 招募<code>视障者音乐会</code>工作人员, 心头触动了一下,就点击了报名. 因为我觉得,对于同样热爱音乐的他们, 或许我该做些什么. </p><p>这场音乐会,没有直播,没有炒作,甚至连DJ都是我这样的新手. 表演者们,小的只有几岁, 年长的我叫一声老爷爷老奶奶不过分. 他们都让人意外地好相处,好商量,伴奏找不到? 没关系,我清唱.</p><p><code>震撼</code>和<code>开心</code>这两个词可以概括我这一天的感受.</p><p>至此刻,我心里依然无法平静. 我在音控室, 很多次都是站起来听他们演唱的. 除去了所有的舞台风格, 表现形式……当你闭上眼睛,去感受他们给你传达的东西,真真切切能感受到,他们在并不完整的人生当中, 萃取出来的那种乐观,坚强,豁达. 真想知道蓝天白云,车水马龙,在他们脑海中都是怎样的一种表达. </p><p>音乐本就不是用来看的,用眼睛去量度就狭隘了.</p><p>一天下来我不敢有丝毫怠慢,播放伴奏,调音,暖场,设备调整….因为我知道, 为了台上这几分钟,需要倾注多少热爱. 以至于我回到公司之后,直接在行军床上昏睡了四个小时.起来已是晚上十二点.</p><p>请原谅我,我无法用言语去描绘今天的场景,只能用上面这些只言片语来表达我难以平复的心情.如果你看到了这篇博文,如果你想知道我此刻的感受,如果你也喜欢音乐,希望你可以去了解一下他们,去帮助他们组织交流音乐的活动,因为音乐对于他们有更加神圣的使命. 他们的音乐的细腻程度绝对会出乎你意料.</p><iframe frameborder="0" width="480" height="320" src="https://v.qq.com/iframe/player.html?vid=p0542qfteye&tiny=0&auto=0" allowfullscreen></iframe>]]></content>
<summary type="html"><p>今天以一个志愿者的身份参加了一场特殊的音乐会,地点在深圳市残疾人综合服务中心的13楼.</p></summary>
<category term="散文" scheme="https://sherlozk.github.io/categories/%E6%95%A3%E6%96%87/"/>
<category term="随笔" scheme="https://sherlozk.github.io/tags/%E9%9A%8F%E7%AC%94/"/>
</entry>
<entry>
<title>ARM汇编基础</title>
<link href="https://sherlozk.github.io/2017/08/23/ARM-base/"/>
<id>https://sherlozk.github.io/2017/08/23/ARM-base/</id>
<published>2017-08-23T11:55:24.000Z</published>
<updated>2022-11-19T03:07:27.511Z</updated>
<content type="html"><![CDATA[<p>在高级语言,如OC、C中,操作的对象是变量,而在ARM汇编语言中,操作的对象是<code>寄存器(register)</code>、<code>内存(RAM)</code>、<code>栈(stack)</code>。</p><span id="more"></span><p><code>寄存器</code> - 可以看成CPU自带的变量,数量有限,需要更多的时候会把他们放到内存中</p><p><code>内存</code> - 变量存储的主要载体,容量大,但是对内存的操作要比对寄存器的操作慢得多</p><p><code>栈</code> - 其实本质也是内存,有特定的读写顺序:<code>先进后出</code>,而且ARM的栈是满递减(Full Descending)的,向下增长,新的变量会被存放到栈底的位置,而且越靠近栈底,内存地址越小. </p><p>Continues……</p>]]></content>
<summary type="html"><p>在高级语言,如OC、C中,操作的对象是变量,而在ARM汇编语言中,操作的对象是<code>寄存器(register)</code>、<code>内存(RAM)</code>、<code>栈(stack)</code>。</p></summary>
<category term="技术" scheme="https://sherlozk.github.io/categories/%E6%8A%80%E6%9C%AF/"/>
<category term="iOS逆向" scheme="https://sherlozk.github.io/tags/iOS%E9%80%86%E5%90%91/"/>
</entry>
<entry>
<title>生活、音乐、远方</title>
<link href="https://sherlozk.github.io/2017/08/04/lmf-md/"/>
<id>https://sherlozk.github.io/2017/08/04/lmf-md/</id>
<published>2017-08-04T02:26:54.000Z</published>
<updated>2022-11-19T03:10:13.998Z</updated>
<content type="html"><![CDATA[<span id="more"></span><h3 id="生活"><a href="#生活" class="headerlink" title="生活"></a>生活</h3><p><img src="/2017/08/04/lmf-md/life.JPG" alt="生活"><br>我每天上班几乎都会坐车,打车或是走到深大小西门坐深大的电瓶车到北门,我喜欢坐车的时候发呆,头靠在窗边,目光总不在近处。每天都在走着同一条路,但是对沿途的建筑、人和事都一概不知,那个奇形怪状的雕塑有什么故事,邻街的马路是否一样拥堵,看起来装修豪华的餐厅食物味道如何。有时候,我又只想看看我喜欢的她在哪里留下什么话,希望全世界只有她……</p><p>几乎每天都在各种想象中度过这么一段时光。</p><h3 id="音乐"><a href="#音乐" class="headerlink" title="音乐"></a>音乐</h3><p><img src="/2017/08/04/lmf-md/music.JPG" alt="音乐"><br>我是一个对音乐有瘾的人,因为它总能使我异常兴奋。而且我喜欢无拘无束的街头音乐。以前住在一个叫大学城的孤岛,周末的公交和地铁不会让你有想出去的欲望。音乐成了唯一的消遣,谁能告诉我,体育馆闪烁的灯光是否是在开live show,城北和中心湖的卖唱大叔大家喜不喜欢,吉他弹得很好的小哥今天在哪条小路,我会加入他们也会拿着吉他走上街头,我们不在乎彼此的曾经,这一刻只用音乐交流。但经常缺少一个很显眼的东西告诉其他人或者告诉我,这里有音乐,你要来吗?</p><h3 id="远方"><a href="#远方" class="headerlink" title="远方"></a>远方</h3><p><img src="/2017/08/04/lmf-md/faraway.jpg" alt="远方"><br>『到此一游』似乎已经是中国人甩不掉的一个flag。我原谅你有感,又不善措辞。但那又丑又不显眼的几个字,我看不出任何个人情怀,还期望后来者,能有所同感? </p><p>在火车环境变得封闭而且恶劣之前,我很喜欢坐可以开窗,时速只有60~80公里/小时的绿皮车去旅行。在这样的一程中,不同的地域不同的环境,即使同一首歌,也会产生不同气息不同色彩的版本,并且我希望能把它留下,希望下一个到来的你,也会因为眼前的景象明白我音乐里的心声。</p>]]></content>
<summary type="html"><span id="more"></span>
<h3 id="生活"><a href="#生活" class="headerlink" title="生活"></a>生活</h3><p><img src="/2017/08/04/lmf-md/life.JPG" alt="生</summary>
<category term="散文" scheme="https://sherlozk.github.io/categories/%E6%95%A3%E6%96%87/"/>
<category term="随笔" scheme="https://sherlozk.github.io/tags/%E9%9A%8F%E7%AC%94/"/>
</entry>
<entry>
<title>致我们终将逝去的浪荡</title>
<link href="https://sherlozk.github.io/2017/03/25/NewBegin/"/>
<id>https://sherlozk.github.io/2017/03/25/NewBegin/</id>
<published>2017-03-25T11:12:32.000Z</published>
<updated>2022-11-19T03:10:43.133Z</updated>
<content type="html"><![CDATA[<p>Nothing come from nothing</p><p>早上踩着九点出门,避开所有的阳光走向公司。二月份的深圳,海风恰好能把你的鼻子冻红。</p><p>这是我离职回校完成毕业设计的日子,毕设不过是个借口,才离开两个月我才发现我是有多么想念这里的一切。</p><p>生活就是你在深圳想念着广州。</p><p>离校那天,我哭了,比谈一场四年的恋爱,最后分手了哭得还要惨,只是没有任何声音发出而已。我在开车,副驾的表弟在装作睡觉。最后那个孤岛终于也从地图上被移走了。我没有回头,不忍回头。</p><p>生活的孤独才是一个人前行最好的环境,不用去交新的女朋友,没有时刻要一起玩才能维持关系的兄弟,没有爸妈在耳边的唠叨。在陌生的城市,除了时不时心里传来的孤独感,没有其他事情可以打扰到我。我可以一点钟下班,因为家里没有人等我回家。我可以晚上通宵周末睡觉,因为没有人拉我出去玩。</p><p>但是这样的生活久了,会让你怀疑人生。</p><p>所以,我必须去交一些朋友,往心里去的那种。时间给我们的间隔,带来的是相聚时的热泪盈眶。当然这也包括我深爱的家人。他们作为一份礼物珍藏在我心里,怀疑人生的时候,想想他们。</p><p>我是一个半浪漫主义者,这是我天生的理性决定的。我曾经希望能像福尔摩斯一样完全做一个冷眼看世间,不被感情左右的人。发现我也做不到。也曾希望成为李煜那样洞察世间多情的人,发现也做不到。</p><p>但,这也或许是我之所以是我的缘故吧。</p>]]></content>
<summary type="html"><p>Nothing come from nothing</p>
<p>早上踩着九点出门,避开所有的阳光走向公司。二月份的深圳,海风恰好能把你的鼻子冻红。</p>
<p>这是我离职回校完成毕业设计的日子,毕设不过是个借口,才离开两个月我才发现我是有多么想念这里的一切。</p>
<</summary>
<category term="散文" scheme="https://sherlozk.github.io/categories/%E6%95%A3%E6%96%87/"/>
<category term="随笔" scheme="https://sherlozk.github.io/tags/%E9%9A%8F%E7%AC%94/"/>
</entry>
<entry>
<title>使用iproxy通过USB将iPhone的端口映射到电脑</title>
<link href="https://sherlozk.github.io/2016/11/01/iproxy/"/>
<id>https://sherlozk.github.io/2016/11/01/iproxy/</id>
<published>2016-11-01T09:41:12.000Z</published>
<updated>2022-11-19T03:10:05.003Z</updated>
<content type="html"><![CDATA[<p>在研究ATX的过程中,发现通过WIFI来连接发送指令,会有延迟,响应不够迅速.<br>所以如果可以通过usb代替进行连接,效率会有很大的提高</p><p>iproxy是usbmuxd附带的一个小工具,它的作用是将设备的某个端口映射到电脑的某个端口<br>mac下可以通过brew安装</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">brew install usbmuxd</span><br></pre></td></tr></table></figure><p><img src="/2016/11/01/iproxy/iproxy.png" alt="iproxy"></p><p>用法很简单:</p><ol><li>第一个参数是你要映射到的电脑端口</li><li>第二个是iPhone的端口</li><li>UDID一般不用填,会自动获取,不过多设备连接时,需要用于区分设备</li></ol><p>iproxy的作用很丰富,一般搞越狱的大牛,会用来映射iPhone的22端口(ssh端口),我使用atx的过程中使用了iPhone的8100端口所以直接指令如下:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">iproxy 2222 8100</span><br></pre></td></tr></table></figure><p>相应的,代码或者脚本里面原本写设备IP的地方就要改成localhost:2222</p><p><img src="/2016/11/01/iproxy/iproxy2.png" alt="before">—-><img src="/2016/11/01/iproxy/iproxy3.png" alt="after"></p><p>这样也就没有了很多手机必须要跟电脑在同一网段的尴尬情况</p>]]></content>
<summary type="html"><p>在研究ATX的过程中,发现通过WIFI来连接发送指令,会有延迟,响应不够迅速.<br>所以如果可以通过usb代替进行连接,效率会有很大的提高</p>
<p>iproxy是usbmuxd附带的一个小工具,它的作用是将设备的某个端口映射到电脑的某个端口<br>mac下可以通过b</summary>
<category term="技术" scheme="https://sherlozk.github.io/categories/%E6%8A%80%E6%9C%AF/"/>
<category term="iOS" scheme="https://sherlozk.github.io/tags/iOS/"/>
</entry>
<entry>
<title>ATX自动化测试框架-iOS</title>
<link href="https://sherlozk.github.io/2016/11/01/atx/"/>
<id>https://sherlozk.github.io/2016/11/01/atx/</id>
<published>2016-11-01T08:18:22.000Z</published>
<updated>2022-11-19T03:08:47.087Z</updated>
<content type="html"><![CDATA[<h3 id="要用到的两大框架"><a href="#要用到的两大框架" class="headerlink" title="要用到的两大框架"></a>要用到的两大框架</h3><p><code>ATX</code>: AutomatorX简称,本文主要讨论的iOS自动化测试的框架<br><code>WDA</code>: WebDriverAgent的简称,是Facebook为苹果开发的一个自动化测试框架<br>优点(对比Appium)<br>引用作者原话:</p><pre><code>1. ATX依赖比较少,安装快,上手也快2. 可以测试第三方应用,比如微信,网易新闻之类3. 更新方便,可以使用pip直接更新应用4. 集成图像识别,可以解决仅靠UI无法定位识别的问题5. 集成测试报告6. 由知名企业网易的游戏测试开发团队开发(其实就是我们开发的),可以比appium更及时的响应需求,还可以用中文交流和沟通。</code></pre><span id="more"></span><h3 id="WebDeiverAgent"><a href="#WebDeiverAgent" class="headerlink" title="WebDeiverAgent"></a>WebDeiverAgent</h3><p>使用ATX之前要先安装WebDeiverAgent(以下简称WDA).<br>其实也很简单:</p><pre><code>1. 到github上面把包下了2. 进工程的根目录,运行./Scripts/bootstrap.sh下载依赖库(要用到Carthage和npm)3. 打开工程文件WebDriverAgent.xcodeproj4. Scheme选择WebDriverAgentRunner5. 设备选择模拟器或者iPhone都可以6. command + u</code></pre><p><img src="https://testerhome.com/photo/2016/c108b0dda5c0ad005290767842f302eb.gif" alt="wda"><br>WDA还可以通过命令行启动:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">xcodebuild -project WebDriverAgent.xcodeproj -scheme WebDriverAgentRunner -destination <span class="string">"id=<span class="subst">$(idevice_id -l)</span>"</span> <span class="built_in">test</span></span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">xcodebuild -project WebDriverAgent.xcodeproj -scheme WebDriverAgentRunner -destination <span class="string">'platform=iOS Simulator,name=iPhone 6'</span> <span class="built_in">test</span></span><br></pre></td></tr></table></figure><p>——WDA据说是Facebook的开发人员,因为Linux下没有iTunes可以用,所以破解了iTunes和iPhone之间的通信协议,以此获取当前的屏幕的元素树.——<br>上面的操作完成后,Xcode控制台会输出如下log:<br><img src="/2016/11/01/atx/log.png" alt="log"><br>我们就可以通过ServerURLHere->后面的连接去监听设备了,WDA里面有一个js脚本,建立一个inspector的页面,供我们方便查看元素:<br><img src="/2016/11/01/atx/inspector.png" alt="inspector"><br> 中间视图显示的树,就是我们可以使用xpath来查找元素的基础了.</p><h3 id="AutomatorX"><a href="#AutomatorX" class="headerlink" title="AutomatorX"></a>AutomatorX</h3><h4 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h4><p>ATX其实是一个纯Python库,可以直接通过pip安装,但是要先安装opencv:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">brew install python pillow opencv</span><br></pre></td></tr></table></figure><p>安装开发版的ATX:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install --upgrade --pre atx</span><br></pre></td></tr></table></figure><p><img src="/2016/11/01/atx/numpy2.png" alt="numpy2"><br>看到这行就安心了……<br>PS:最后要说的是输入法,测试之前需要将输入法却换到系统默认的英文输入法。某狗输入法是不可以的。</p><h4 id="测试脚本"><a href="#测试脚本" class="headerlink" title="测试脚本"></a>测试脚本</h4><h5 id="bundle-id"><a href="#bundle-id" class="headerlink" title="bundle_id"></a>bundle_id</h5><p>首先要知道应用的bundle_id, 有两种方法:<br>连接手机,终端使用命令 ideviceinstaller -l 查看当前手机所有已安装的应用:<br><img src="/2016/11/01/atx/bundle_id.png" alt="bundle_id"><br>通过Xcode 查看, 在美聊实验的时候,才知道,美聊Xcode编译出来的,测试版本的,线上版本的,bundle_id全是不同的…….<br><img src="/2016/11/01/atx/xcode.png" alt="xcode"><br>然后可以写第一个脚本了:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># coding: utf-8 </span></span><br><span class="line"><span class="keyword">import</span> atx</span><br><span class="line">d = atx.connect(<span class="string">'http://localhost:8100'</span>)</span><br><span class="line">d.start_app(<span class="string">'com.netease.idate4dt'</span>)</span><br><span class="line"><span class="built_in">print</span> d.status()</span><br></pre></td></tr></table></figure><p>这里我们把DEVICE_URL写成了localhost:8100,如果是真机的话,需要根据实际情况改成对应的手机IP和Port.<br> 这是我之前写的一个demo:</p><p>打开应用<br>点击登录<br>输入帐号<br>输入密码<br>点击登录<br>点击个人<br>进入设置<br>退出登录</p><p><img src="/2016/11/01/atx/script.png" alt="script"><br>说明:<br>尽量使用<a href="http://www.w3school.com.cn/xpath/xpath_syntax.asp">xpath语法</a><br>因为美聊会保存账户,所以输入帐号之前先清除textField的内容<br>倒数第3,4行多了一个scroll() 它的功能是滑动屏幕使其按钮可见</p><h4 id="测试过程报告"><a href="#测试过程报告" class="headerlink" title="测试过程报告"></a>测试过程报告</h4><p>因为ATX集成了测试报告,所以生成响应的测试报告也很简单 在第3行代码之后加入以下代码</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> atx.ext.report <span class="keyword">import</span> Report</span><br><span class="line">rp = Report(d)</span><br><span class="line">rp.patch_wda()</span><br></pre></td></tr></table></figure><p>再次运行一遍代码,在当前脚本所在目录下就可以看到一个report目录,里面有一个image目录,里面是每一步的截图</p><h4 id="ATX-GUI-基于Python的截图软件"><a href="#ATX-GUI-基于Python的截图软件" class="headerlink" title="ATX GUI(基于Python的截图软件)"></a>ATX GUI(基于Python的截图软件)</h4><p><img src="/2016/11/01/atx/gui.png" alt="gui"><br>因为一般游戏的测试会用到图片识别多一点,所以用了一个我自己玩的游戏做例子:<br><img src="/2016/11/01/atx/gui2.png" alt="gui2"><br>ATX的图片识别,是支持分辨率缩放匹配的,前提就是,在保存截图的时候要注明是在哪个分辨率下面截的图,命名方式如下:<br><img src="/2016/11/01/atx/screenshot.png" alt="screenshot"><br>原理大概就是:从文件名获取源分辨率,从设备获取设备分辨率,然后将图片按照比例缩放</p><h3 id="问题"><a href="#问题" class="headerlink" title="问题"></a>问题</h3><ul><li>真机有可能会在第6步的时候遇到Test Failed的错误,<a href="https://github.com/facebook/WebDriverAgent/wiki/Common-Issues">点我</a></li><li>如果Xcode直接显示Test Succeed,请重启手机和Xcode</li><li>如果之前有安装刚过numpy这个库的话,安装或者更新ATX的时候会报下面的错误:</li></ul><p><img src="/2016/11/01/atx/numpy.png" alt="numpy"><br>因为numpy的版本冲突,但是os x10.10之后, 对系统文件夹有SIP保护,所以没有办法对特定的系统文件夹进行修改,<a href="http://blog.csdn.net/hqzxsc2006/article/details/51602654">解决方法</a></p><ul><li>在升级iOS10之后, 坐标系貌似有所改变, 原来的(x, y)变成了(width - y, x)</li></ul>]]></content>
<summary type="html"><h3 id="要用到的两大框架"><a href="#要用到的两大框架" class="headerlink" title="要用到的两大框架"></a>要用到的两大框架</h3><p><code>ATX</code>: AutomatorX简称,本文主要讨论的iOS自动化测试的框架<br><code>WDA</code>: WebDriverAgent的简称,是Facebook为苹果开发的一个自动化测试框架<br>优点(对比Appium)<br>引用作者原话:</p>
<pre><code>1. ATX依赖比较少,安装快,上手也快
2. 可以测试第三方应用,比如微信,网易新闻之类
3. 更新方便,可以使用pip直接更新应用
4. 集成图像识别,可以解决仅靠UI无法定位识别的问题
5. 集成测试报告
6. 由知名企业网易的游戏测试开发团队开发(其实就是我们开发的),可以比appium更及时的响应需求,还可以用中文交流和沟通。
</code></pre></summary>
<category term="技术" scheme="https://sherlozk.github.io/categories/%E6%8A%80%E6%9C%AF/"/>
<category term="自动化测试" scheme="https://sherlozk.github.io/tags/%E8%87%AA%E5%8A%A8%E5%8C%96%E6%B5%8B%E8%AF%95/"/>
</entry>
<entry>
<title>常用SQL语句</title>
<link href="https://sherlozk.github.io/2016/08/19/sql/"/>
<id>https://sherlozk.github.io/2016/08/19/sql/</id>
<published>2016-08-19T08:32:21.000Z</published>
<updated>2022-11-19T03:11:31.304Z</updated>
<content type="html"><![CDATA[<h1 id="SQL语句的种类"><a href="#SQL语句的种类" class="headerlink" title="SQL语句的种类"></a>SQL语句的种类</h1><ul><li><p>数据定义语句(DDL:Data Definition Language)</p><ul><li>包括create和drop等操作;</li><li>在数据库中创建新表或删除表(create table或 drop table);</li></ul></li><li><p>数据操作语句(DML:Data Manipulation Language)</p><ul><li>包括insert、update、delete等操作</li></ul></li><li><p>数据查询语句(DQL:Data Query Language)</p><ul><li>可以用于查询获得表中的数据</li><li>关键字select是DQL(也是所有SQL)用得最多的操作</li><li>其他DQL常用的关键字有where,order by,group by和having</li></ul></li></ul><span id="more"></span><h1 id="DDL语句"><a href="#DDL语句" class="headerlink" title="DDL语句"></a>DDL语句</h1><ul><li><p>删除表</p><pre><code> DROP TABLE IF EXISTS '表名';</code></pre></li><li><p>创建表</p><pre><code> CREATE TABLE IF NOT EXISTS '表名' ( '字段名' 类型(INTEGER, REAL, TEXT, BLOB) NOT NULL 不允许为空 PRIMARY KEY 主键 AUTOINCREMENT 自增长, '字段名2' 类型, ... )</code></pre><ul><li><p>具体使用:</p><pre><code> CREATE TABLE IF NOT EXISTS 't_student' ( "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, "name" TEXT, "age" INTEGER, "height" REAL)</code></pre><ul><li>语句说明<ul><li>CREATE TABLE:创建一张表</li><li>IF NOT EXISTS:不存在则创建</li><li>‘t_student’:表的名称</li><li>NOT NULL:不允许为空</li><li>PRIMARY KEY:主键</li><li>AUTOINCREMENT:自动增加</li><li>‘id’ INTEGER:有一个ID字段,类型是INTEGER</li></ul></li></ul></li></ul></li></ul><h1 id="DML语句"><a href="#DML语句" class="headerlink" title="DML语句"></a>DML语句</h1><ul><li><p>插入数据</p><pre><code> INSERT INTO 't_student' (name, age, height) VALUES ('why', 18, 1.88);</code></pre><ul><li>语句说明<ul><li>INSERT INTO: 插入数据</li><li>‘t_student’: 在哪一个表中插入数据</li><li>(数据的字段): 给哪些字段插入数据</li><li>VALUES (‘why’, 18, 1.88): 插入的具体值</li></ul></li></ul></li><li><p>更新数据</p><pre><code> UPDATE 't_student' SET 字段 = '值' WHERE 条件判断;</code></pre><ul><li><p>语句说明</p><ul><li>UPDATE: 跟新数据</li><li>‘t_student’: 在哪一个表中更新数据</li><li>SET 字段 = ‘值’: 更新怎样的数据</li><li>WHERE 条件判断: 更新哪些数据</li></ul></li><li><p>具体使用</p><pre><code> UPDATE t_student SET name = 'me' WHERE age = 14; UPDATE t_student SET name = 'liu' WHERE age is 20; UPDATE t_student SET name = 'yy' WHERE age < 20; UPDATE t_student SET name = 'A' WHERE age < 100 and score > 60; UPDATE t_student SET name = 'ly';</code></pre></li></ul></li><li><p>删除数据</p><pre><code> DELETE FROM t_student; DELETE FROM t_student WHERE age < 30;</code></pre><ul><li>语法说明<ul><li>DELETE FROM: 从表中删除数据</li><li>t_student : 表名</li><li>可以跟条件也可以不跟:不跟表示删除所有的数据</li></ul></li></ul></li></ul><h1 id="DQL语句"><a href="#DQL语句" class="headerlink" title="DQL语句"></a>DQL语句</h1><ul><li><p>查询语句</p><ul><li><p>基本查询(查询整个表格)</p><pre><code> SELECT * FROM t_student;</code></pre></li><li><p>查询某些字段(查询name和age两个字段)</p><pre><code> SELECT name, age FROM t_student;</code></pre></li><li><p>通过条件判断来查询对应的数据(年龄大于等于18)</p><pre><code> SELECT * FROM t_student WHERE age >= 18;</code></pre></li><li><p>通过条件判断来查询对应的数据(名字以i开头),使用like关键字(模糊查询)</p><pre><code> SELECT * FROM t_student WHERE name like '%i%';</code></pre></li><li><p>计算个数</p><ul><li><p>计算一共多少列</p><pre><code> SELECT count(*) FROM t_student;</code></pre></li><li><p>计算某一个列个数</p><pre><code> SELECT count(age) FROM t_student;</code></pre></li></ul></li><li><p>排序</p><ul><li><p>升序 ASC (默认是升序)</p><pre><code> SELECT * FROM t_student ORDER BY age;</code></pre></li><li><p>降序 DESC</p><pre><code> SELECT * FROM t_student ORDER BY age DESC;</code></pre></li><li><p>按照年龄升序排序,如果年龄相同,按照名字的降序排列</p><pre><code> SELECT * FROM t_student ORDER BY age,name DESC;</code></pre></li></ul></li><li><p>起别名</p><ul><li><p>给列起别名(as可以省略)</p><pre><code> SELECT name AS myName, age AS myAge FROM t_student;</code></pre></li><li><p>给表起别名</p><pre><code> SELECT s.name, s.age FROM t_student as s;</code></pre></li></ul></li><li><p>limit</p><pre><code> SELECT * FROM t_student LIMIT 数字1,数字2;</code></pre><ul><li><p>跳过前9条数据,再查询3条数据</p><pre><code> SELECT * FROM t_student LIMIT 9, 3;</code></pre></li><li><p>跳过0条数据,取5条数据</p><pre><code> SELECT * FROM t_student LIMIT 5;</code></pre></li></ul></li></ul></li></ul>]]></content>
<summary type="html"><h1 id="SQL语句的种类"><a href="#SQL语句的种类" class="headerlink" title="SQL语句的种类"></a>SQL语句的种类</h1><ul>
<li><p>数据定义语句(DDL:Data Definition Language)</p>
<ul>
<li>包括create和drop等操作;</li>
<li>在数据库中创建新表或删除表(create table或 drop table);</li>
</ul>
</li>
<li><p>数据操作语句(DML:Data Manipulation Language)</p>
<ul>
<li>包括insert、update、delete等操作</li>
</ul>
</li>
<li><p>数据查询语句(DQL:Data Query Language)</p>
<ul>
<li>可以用于查询获得表中的数据</li>
<li>关键字select是DQL(也是所有SQL)用得最多的操作</li>
<li>其他DQL常用的关键字有where,order by,group by和having</li>
</ul>
</li>
</ul></summary>
<category term="技术" scheme="https://sherlozk.github.io/categories/%E6%8A%80%E6%9C%AF/"/>
<category term="MySQL" scheme="https://sherlozk.github.io/tags/MySQL/"/>
</entry>
<entry>
<title>Http状态码分析</title>
<link href="https://sherlozk.github.io/2016/08/15/HTTPCode/"/>
<id>https://sherlozk.github.io/2016/08/15/HTTPCode/</id>
<published>2016-08-15T12:41:12.000Z</published>
<updated>2022-11-19T03:09:48.195Z</updated>
<content type="html"><![CDATA[<p>开发过程中经常需要与后台配合调试接口,在调试过程中后台会返回一些Http状态码,程序员可以通过状态码分析接口是否调通以及如果失败问题在哪。因此了解Http状态码的含义对于开发是很有帮助的,这里我收集了并总结了一些状态码的含义。</p><h2 id="1xx(临时响应)"><a href="#1xx(临时响应)" class="headerlink" title="1xx(临时响应)"></a>1xx(临时响应)</h2><p>表示临时响应并需要请求者继续执行操作的状态代码。</p><ul><li>100 (继续) 请求者应当继续提出请求。 服务器返回此代码表示已收到请求的第一部分,正在等待其余部分。</li><li>101 (切换协议) 请求者已要求服务器切换协议,服务器已确认并准备切换。</li></ul><span id="more"></span><h2 id="2xx-(成功)"><a href="#2xx-(成功)" class="headerlink" title="2xx (成功)"></a>2xx (成功)</h2><p>表示成功处理了请求的状态代码。</p><ul><li>200 (成功) 服务器已成功处理了请求。 通常,这表示服务器提供了请求的网页。</li><li>201 (已创建) 请求成功并且服务器创建了新的资源。</li><li>202 (已接受) 服务器已接受请求,但尚未处理。</li><li>203 (非授权信息) 服务器已成功处理了请求,但返回的信息可能来自另一来源。</li><li>204 (无内容) 服务器成功处理了请求,但没有返回任何内容。</li><li>205 (重置内容) 服务器成功处理了请求,但没有返回任何内容。</li><li>206 (部分内容) 服务器成功处理了部分 GET 请求。</li></ul><h2 id="3xx-(重定向)"><a href="#3xx-(重定向)" class="headerlink" title="3xx (重定向)"></a>3xx (重定向)</h2><p>表示要完成请求,需要进一步操作。 通常,这些状态代码用来重定向。</p><ul><li>300 (多种选择) 针对请求,服务器可执行多种操作。 服务器可根据请求者 (user agent) 选择一项操作,或提供操作列表供请求者选择。</li><li>301 (永久移动) 请求的网页已永久移动到新位置。 服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自动将请求者转到新位置。</li><li>302 (临时移动) 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。</li><li>303 (查看其他位置) 请求者应当对不同的位置使用单独的 GET 请求来检索响应时,服务器返回此代码。</li><li>304 (未修改) 自从上次请求后,请求的网页未修改过。 服务器返回此响应时,不会返回网页内容。</li><li>305 (使用代理) 请求者只能使用代理访问请求的网页。 如果服务器返回此响应,还表示请求者应使用代理。</li><li>307 (临时重定向) 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。</li></ul><h2 id="4xx(客户端请求错误)"><a href="#4xx(客户端请求错误)" class="headerlink" title="4xx(客户端请求错误)"></a>4xx(客户端请求错误)</h2><p>这些状态代码表示请求可能出错,妨碍了服务器的处理。</p><ul><li>400 (错误请求) 服务器不理解请求的语法。</li><li>401 (未授权) 请求要求身份验证。 对于需要登录的网页,服务器可能返回此响应。<ul><li>HTTP 401.1 - 未授权:登录失败</li><li>HTTP 401.2 - 未授权:服务器配置问题导致登录失败</li><li>HTTP 401.3 - ACL 禁止访问资源</li><li>HTTP 401.4 - 未授权:授权被筛选器拒绝</li><li>HTTP 401.5 - 未授权:ISAPI 或 CGI 授权失败</li></ul></li><li>403 (禁止) 服务器拒绝请求。<ul><li>HTTP 403.1 - 禁止访问:禁止可执行访问</li><li>HTTP 403.2 - 禁止访问:禁止读访问</li><li>HTTP 403.3 - 禁止访问:禁止写访问</li><li>HTTP 403.4 - 禁止访问:要求 SSL</li><li>HTTP 403.5 - 禁止访问:要求 SSL 128</li><li>HTTP 403.6 - 禁止访问:IP 地址被拒绝</li><li>HTTP 403.7 - 禁止访问:要求客户证书</li><li>HTTP 403.8 - 禁止访问:禁止站点访问</li><li>HTTP 403.9 - 禁止访问:连接的用户过多</li><li>HTTP 403.10 - 禁止访问:配置无效</li><li>HTTP 403.11 - 禁止访问:密码更改</li><li>HTTP 403.12 - 禁止访问:映射器拒绝访问</li><li>HTTP 403.13 - 禁止访问:客户证书已被吊销</li><li>HTTP 403.15 - 禁止访问:客户访问许可过多</li><li>HTTP 403.16 - 禁止访问:客户证书不可信或者无效</li><li>HTTP 403.17 - 禁止访问:客户证书已经到期或者尚未生效</li></ul></li><li>404 (未找到) 服务器找不到请求的网页。</li><li>405 (方法禁用) 禁用请求中指定的方法。</li><li>406 (不接受) 无法使用请求的内容特性响应请求的网页。</li><li>407 (需要代理授权) 此状态代码与 401(未授权)类似,但指定请求者应当授权使用代理。</li><li>408 (请求超时) 服务器等候请求时发生超时。</li><li>409 (冲突) 服务器在完成请求时发生冲突。 服务器必须在响应中包含有关冲突的信息。</li><li>410 (已删除) 如果请求的资源已永久删除,服务器就会返回此响应。</li><li>411 (需要有效长度) 服务器不接受不含有效内容长度标头字段的请求。</li><li>412 (未满足前提条件) 服务器未满足请求者在请求中设置的其中一个前提条件。</li><li>413 (请求实体过大) 服务器无法处理请求,因为请求实体过大,超出服务器的处理能力。</li><li>414 (请求的 URI 过长) 请求的 URI(通常为网址)过长,服务器无法处理。</li><li>415 (不支持的媒体类型) 请求的格式不受请求页面的支持。</li><li>416 (请求范围不符合要求) 如果页面无法提供请求的范围,则服务器会返回此状态代码。</li><li>417 (未满足期望值) 服务器未满足”期望”请求标头字段的要求。</li></ul><h2 id="5xx(服务器错误)"><a href="#5xx(服务器错误)" class="headerlink" title="5xx(服务器错误)"></a>5xx(服务器错误)</h2><p>这些状态代码表示服务器在尝试处理请求时发生内部错误。 这些错误可能是服务器本身的错误,而不是请求出错。</p><ul><li>500 (服务器内部错误) 服务器遇到错误,无法完成请求。<ul><li>HTTP 500.100 - 内部服务器错误 - ASP 错误</li><li>HTTP 500-11 服务器关闭</li><li>HTTP 500-12 应用程序重新启动</li><li>HTTP 500-13 - 服务器太忙</li><li>HTTP 500-14 - 应用程序无效</li><li>HTTP 500-15 - 不允许请求 global.asa</li></ul></li><li>501 (尚未实施) 服务器不具备完成请求的功能。 例如,服务器无法识别请求方法时可能会返回此代码。</li><li>502 (错误网关) 服务器作为网关或代理,从上游服务器收到无效响应。</li><li>503 (服务不可用) 服务器目前无法使用(由于超载或停机维护)。 通常,这只是暂时状态。</li><li>504 (网关超时) 服务器作为网关或代理,但是没有及时从上游服务器收到请求。</li><li>505 (HTTP 版本不受支持) 服务器不支持请求中所用的 HTTP 协议版本。</li></ul><hr><p>文/花花0809(简书作者)<br>原文链接:<a href="http://www.jianshu.com/p/ad13969ca87a">http://www.jianshu.com/p/ad13969ca87a</a><br>著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。</p>]]></content>
<summary type="html"><p>开发过程中经常需要与后台配合调试接口,在调试过程中后台会返回一些Http状态码,程序员可以通过状态码分析接口是否调通以及如果失败问题在哪。因此了解Http状态码的含义对于开发是很有帮助的,这里我收集了并总结了一些状态码的含义。</p>
<h2 id="1xx(临时响应)"><a href="#1xx(临时响应)" class="headerlink" title="1xx(临时响应)"></a>1xx(临时响应)</h2><p>表示临时响应并需要请求者继续执行操作的状态代码。</p>
<ul>
<li>100 (继续) 请求者应当继续提出请求。 服务器返回此代码表示已收到请求的第一部分,正在等待其余部分。</li>
<li>101 (切换协议) 请求者已要求服务器切换协议,服务器已确认并准备切换。</li>
</ul></summary>
<category term="技术" scheme="https://sherlozk.github.io/categories/%E6%8A%80%E6%9C%AF/"/>
<category term="网络" scheme="https://sherlozk.github.io/tags/%E7%BD%91%E7%BB%9C/"/>
</entry>
<entry>
<title>数据结构简单要点总结(转)</title>
<link href="https://sherlozk.github.io/2016/07/26/data-structure/"/>
<id>https://sherlozk.github.io/2016/07/26/data-structure/</id>
<published>2016-07-26T12:07:43.000Z</published>
<updated>2022-11-19T03:08:58.157Z</updated>
<content type="html"><![CDATA[<h3 id="栈"><a href="#栈" class="headerlink" title="栈"></a>栈</h3><p>栈是只能在一端进行插入和删除的线性表。<br>(别看只是个定义,非常重要,已经道出了运算方法:只能在一端插入和删除。)</p><p>栈的特征:后进先出,先进后出。</p><p>插入和删除元素的一端称为栈顶。(说明了我们在栈顶操作)<br>另一端称为栈底。<br>插入元素和删除元素的操作称为入栈和出栈。</p> <span id="more"></span><h4 id="顺序栈"><a href="#顺序栈" class="headerlink" title="顺序栈"></a>顺序栈</h4><p>结构:(top总是指向数组最后的元素,比如data[n],而不是前面)</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> MAXSIZE 100</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span></span></span><br><span class="line"><span class="class">{</span></span><br><span class="line"> elementtype data[MAXSIZE];</span><br><span class="line"> <span class="type">int</span> top;</span><br><span class="line">} seqstack;</span><br></pre></td></tr></table></figure><p>初始化栈:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">init_stack</span><span class="params">(seqstack *S)</span></span><br><span class="line">{</span><br><span class="line"> S->top = <span class="number">-1</span>; <span class="comment">//一个元素也没有,注意因为TOP是下标而不是元素个数,用-1</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>判断栈是否为空:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">stack_empty</span><span class="params">(seqstack *S)</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">if</span> (S->top == <span class="number">-1</span>)</span><br><span class="line"> <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>取栈顶元素:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">elementtype <span class="title function_">stack_top</span><span class="params">(seqstack *S)</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">if</span> (stack_empty(S))</span><br><span class="line"> error(<span class="string">"栈为空!"</span>);</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> <span class="keyword">return</span> S->data[S->top];</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>入栈:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">push_stack</span><span class="params">(seqstack *S, elementtype x)</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">if</span> (S->top == MAXSIZE <span class="number">-1</span>)</span><br><span class="line"> error(<span class="string">"溢出!"</span>);</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> S->data[++S->top] = x; <span class="comment">//注意->运算符的优先级是最高的</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>出栈:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">elementtype <span class="title function_">pop_stack</span><span class="params">(seqstack *S)</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">if</span> (stack_empty(S))</span><br><span class="line"> error(<span class="string">"栈为空!"</span>);</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> <span class="keyword">return</span> S->data[S->top--];</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>判断栈是否为满:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">stack_full</span><span class="params">(seqstack *S)</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">if</span> (S->top == MAXSIZE <span class="number">-1</span>)</span><br><span class="line"> <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>总体来说,顺序栈很简单,出的时候取最后的元素,进的时候一样进在尾部。</p><h4 id="链栈"><a href="#链栈" class="headerlink" title="链栈"></a>链栈</h4><p>栈的链式存储结构称为链栈。<br>其插入和删除操作仅限制在表头位置上进行。<br>由于只能在链表头部进行操作,故链栈没有必要象单链表那样添加头结点。栈顶指针就是链表的头指针。<br>结构:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">node</span> //和一般链表的结构一样。</span></span><br><span class="line"><span class="class">{</span></span><br><span class="line"> elementtype data;</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">node</span> *<span class="title">next</span>;</span></span><br><span class="line">} linkstack; </span><br><span class="line">linkstack *top;</span><br></pre></td></tr></table></figure><p>当top=NULL时,链栈为空栈。</p><p>入栈:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">push_stack</span><span class="params">(linkstack *top, elementtype x)</span></span><br><span class="line">{</span><br><span class="line"> linkstack *P = (linkstack *)<span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(linkstack));</span><br><span class="line"> P->data = x;</span><br><span class="line"> P->next = top->next;</span><br><span class="line"> top = P;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>出栈:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">elementype <span class="title function_">pop_stack</span><span class="params">(linkstack *top)</span></span><br><span class="line">{</span><br><span class="line"> elementtype x;</span><br><span class="line"> linkstack *P;</span><br><span class="line"> <span class="keyword">if</span> (top == <span class="literal">NULL</span>)</span><br><span class="line"> error(<span class="string">"栈为空!"</span>);</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> {</span><br><span class="line"> x = top->data;</span><br><span class="line"> P = top;</span><br><span class="line"> top = top->next;</span><br><span class="line"> <span class="built_in">free</span>(P);</span><br><span class="line"> <span class="keyword">return</span> x;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="队列"><a href="#队列" class="headerlink" title="队列"></a>队列</h3><p>队列是只能在一端插入,另一端删除的线性表。<br>特征是:先进先出,后进后出。</p><h4 id="顺序队列"><a href="#顺序队列" class="headerlink" title="顺序队列"></a>顺序队列</h4><p>注意顺序队列多是循环队列,这里要注意几点:<br>(1)front是队头的前一个位置。<br>(2)尾部入队,头部出队。<br>(3)由于循环,任何的位置移动计算之后要取余:$P = (P + 1) % MAXSIZE $。<br>结构:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> MAXSIZE 100</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span></span></span><br><span class="line"><span class="class">{</span></span><br><span class="line"> elementtype data[MAXSIZE];</span><br><span class="line"> <span class="type">int</span> front; <span class="comment">//头序号(注意是队头的前一个位置)</span></span><br><span class="line"> <span class="type">int</span> rear; <span class="comment">//尾序号(直接指向尾元素)</span></span><br><span class="line">} seqqueue;</span><br></pre></td></tr></table></figure><p>初始化队列:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">init_queue</span><span class="params">(seqqueue *Q)</span></span><br><span class="line">{</span><br><span class="line"> Q->front = <span class="number">0</span>;</span><br><span class="line"> Q->rear = <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>还有一种写法:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">init_queue</span><span class="params">(seqqueue *Q)</span></span><br><span class="line">{</span><br><span class="line"> Q->front = MAXSIZE - <span class="number">1</span>;</span><br><span class="line"> Q->rear = MAXSIZE - <span class="number">1</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>两种方法的区别是第一种插入第一个元素是data[1],而第二种是data[0]。<br>判断队列是否为空:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">queue_empty</span><span class="params">(seqqueue *Q)</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">if</span> (Q->front == Q->rear)</span><br><span class="line"> <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>判断队列是否为满:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">queue_full</span><span class="params">(seqqueue *Q)</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">if</span> ((Q->rear + <span class="number">1</span>) % MAXSIZE == Q->front)</span><br><span class="line"> <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>取队头元素:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">elementtype <span class="title function_">queue_front</span><span class="params">(seqqueue *Q)</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">if</span> (queue_empty(Q))</span><br><span class="line"> error(<span class="string">"队列为空!"</span>);</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> <span class="keyword">return</span> Q->data[(Q->front + <span class="number">1</span>) % MAXSIZE];</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>入队:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">Enqueue</span><span class="params">(seqqueue *Q, elementtype x)</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">if</span> (queue_full(Q))</span><br><span class="line"> error(<span class="string">"队列满!"</span>);</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> {</span><br><span class="line"> Q->rear = (Q->rear + <span class="number">1</span>) % MAXSIZE; <span class="comment">//千万不能直接用Q->rear++,在循环队列要特别注意</span></span><br><span class="line"> Q->data[Q->rear] = x;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>出队:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">elementtype <span class="title function_">Outqueue</span><span class="params">(seqqueue *Q)</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">if</span> (queue_empty(Q))</span><br><span class="line"> error(<span class="string">"队列为空!"</span>);</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> {</span><br><span class="line"> Q->front = (Q->front + <span class="number">1</span>) % MAXSIZE;</span><br><span class="line"> <span class="keyword">return</span> Q->data[Q->front];</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="链队列"><a href="#链队列" class="headerlink" title="链队列"></a>链队列</h4><p>出队时,删除表头操作,入队时,在表尾添加结点。(也就是头部出,尾部进)<br>使用带头结点的单链表形式。(注意链栈是不带头结点的)<br>结构:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">mynode</span></span></span><br><span class="line"><span class="class">{</span></span><br><span class="line"> elementtype data;</span><br><span class="line"> mynode *next;</span><br><span class="line">} node; <span class="comment">//就是单链表</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span></span></span><br><span class="line"><span class="class">{</span></span><br><span class="line"> node *front;</span><br><span class="line"> node *rear;</span><br><span class="line">} linkqueue;</span><br></pre></td></tr></table></figure><p>初始化队列:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">init_queue</span><span class="params">(linkqueue *Q)</span></span><br><span class="line">{</span><br><span class="line"> Q->front = (node *)<span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(node)); <span class="comment">//生成头结点(注意是NODE类型,Q结构是已有的一个结构,这里有点特殊,仔细体会)</span></span><br><span class="line"> Q->rear = Q->front;</span><br><span class="line"> Q->front = <span class="literal">NULL</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>判断队列是否为空:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">queue_empty</span><span class="params">(linkqueue *Q)</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">if</span> (Q->front == Q->rear)</span><br><span class="line"> <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>取队头元素:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">elementtype <span class="title function_">queue_front</span><span class="params">(linkqueue *Q)</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">if</span> (queue_empty(Q))</span><br><span class="line"> error(<span class="string">"队列为空!"</span>);</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> <span class="keyword">return</span> Q->front->next->data;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>入队:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">Enqueue</span><span class="params">(linkqueue *Q, elementtype x)</span></span><br><span class="line">{</span><br><span class="line"> node *P = (node *)<span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(node));</span><br><span class="line"> P->data = x;</span><br><span class="line"> P->next = <span class="literal">NULL</span>;</span><br><span class="line"> Q->rear->next = P;</span><br><span class="line"> Q->rear = P;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>出队:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">elementtype <span class="title function_">Outqueue</span><span class="params">(linkqueue *Q)</span></span><br><span class="line">{</span><br><span class="line"> node *P;</span><br><span class="line"> elmenttype x;</span><br><span class="line"> <span class="keyword">if</span> (queue_empty(Q))</span><br><span class="line"> error(<span class="string">"队列为空!"</span>);</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> {</span><br><span class="line"> P = Q->front->next;</span><br><span class="line"> Q->front->next = P->next;</span><br><span class="line"> x = P->data;</span><br><span class="line"> <span class="built_in">free</span>(P);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (Q->front->next == <span class="literal">NULL</span>) <span class="comment">//只剩一个结点删除后队列为空时的特殊情况,一定要注意处理</span></span><br><span class="line"> Q->rear = Q->front;</span><br><span class="line"> <span class="keyword">return</span> x;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="数组"><a href="#数组" class="headerlink" title="数组"></a>数组</h3><p>主要是稀疏矩阵的压缩存储:<br>当数组中非零元素非常少时,称之为稀疏矩阵。<br>存储特别如下:<br>(1)对稀疏矩阵压缩存储时,除了存储非零元素的值v以外,还要存储其行列号i和j,故每个元素对应一个三元组(i, j, v)。将这些元素的三元组组织起来构成三元组表。<br>(2)需要在三元组表中增设元素个数、行列数,以唯一确定一个稀疏矩阵。</p><p>结构如下:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> MAXSIZE 100</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> //三元组结构</span></span><br><span class="line"><span class="class">{</span></span><br><span class="line"> <span class="type">int</span> i, j;</span><br><span class="line"> elementtype v;</span><br><span class="line">} tuple;</span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span></span></span><br><span class="line"><span class="class">{</span></span><br><span class="line"> <span class="type">int</span> mu, nu, tu; <span class="comment">//行数、列数、非0元素个数</span></span><br><span class="line"> tuple data[MAXSIZE];</span><br><span class="line">} spmatrix;</span><br></pre></td></tr></table></figure><h3 id="树"><a href="#树" class="headerlink" title="树"></a>树</h3><h4 id="树-1"><a href="#树-1" class="headerlink" title="树"></a>树</h4><p>树中的每个结点最多只有一个前驱(父辈),但可能有多个后继(后代)。<br>一个结点的度是指该结点的孩子数目。<br>若一个结点的度为0,称为叶子结点或终结点,否则称为分支结点或非终结点。<br>一棵树的度是树中最大的结点的度。<br>某个结点的子树的根称为其孩子结点,而该结点为其孩子结点的双亲结点或父结点。<br>同一个结点的孩子互相称为兄弟结点。<br>根的层次为1,其余结点的层次为父结点的层次数加1,而最大的层次数称为树的高度或深度。<br>如果树中各兄弟结点之间的排列次序是无关的,则称之为有序树,否则称为无序树。<br>称多棵树为森林。</p><h4 id="二叉树"><a href="#二叉树" class="headerlink" title="二叉树"></a>二叉树</h4><p>二叉树和树一样,都可以为空树。<br>注意二叉树每个结点的孩子都有左右之分,每个结点都有左右两个子树,这与树结构明显不同。<br>二叉树和树本质上是完全不同的两种结构。<br> 定义:满二叉树是指每层都有最大数目结点的二叉树,即高度为k的满二叉树中有2k-1个结点。而完全二叉树则是指在满二叉树的最下层从右到左连续地删除若干个结点所得到的二叉树。 </p><p>二叉树的性质:</p><p> 在二叉树的第i层上的结点个数$<=2^{i-1}$(i>0)<br> 深度(高度)为k的二叉树的结点个数$<=2^k-1$<br>3.对任一棵非空的二叉树,如果其叶子数为$n_0$, 度为2的结点数为$n_2$, 则有下面的关系式成立:$n_0=n_2+1$<br>(这个性质很重要。主要是有个概念:除去根结点,每个结点都与一个它上面的分支一一对应,也就是说,结点数=分支数+1,所以有:$n-1=n_1+2*n_2$)<br>4.有n个结点的完全二叉树(n>0)的深度为[log2n]+1([]为取整)<br>5.在编号的完全二叉树中,各结点的编号之间的关系为:<br>编号为i的结点如果存在左孩子,则其编号为2i,如果存在右孩子,则其编号为2i+1,如果存在父结点,则其编号为[i/2]。</p><p>二叉树的存储结构:<br> 顺序存储结构:<br>按完全二叉树的编号次序进行,即编号为i的结点存储在数组中下标为i的元素中。<br>缺点:若二叉树不是完全二叉树,则为了保持结点之间的关系,不得不空出许多元素来,这就造成了空间的浪费。</p><p> 二叉链表存储结构:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">node</span></span></span><br><span class="line"><span class="class">{</span></span><br><span class="line"> datatype data;</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">node</span> *<span class="title">lchild</span>, *<span class="title">rchild</span>;</span></span><br><span class="line">} bitree;</span><br></pre></td></tr></table></figure><h4 id="二叉树的遍历:"><a href="#二叉树的遍历:" class="headerlink" title="二叉树的遍历:"></a>二叉树的遍历:</h4><p>所谓遍历二叉树是指按某种次序访问二叉树中每个结点一次且仅一次。<br>根据访问根结点的次序,可以分为先序遍历,中序遍历,后序遍历。<br>先序遍历可描述为:<br>若二叉树T不为空:<br>(1)访问T的根结点;<br>(2)先序遍历T的左子树;<br>(3)先序遍历T的右子树。<br>遍历的算法非常简单,只写出先序遍历算法:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">preorder</span><span class="params">(bitree *T)</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">if</span> (T != <span class="literal">NULL</span>)</span><br><span class="line"> {</span><br><span class="line"> visit(T); <span class="comment">//一般用的最多的就是输出</span></span><br><span class="line"> preorder(T->lchild);</span><br><span class="line"> preorder(T->rchild);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="线索二叉树"><a href="#线索二叉树" class="headerlink" title="线索二叉树"></a>线索二叉树</h4><p>线索二叉树主要是为了求解在某种次序下的前驱或后继结点。<br>将二叉树各结点中的空的左孩子指针域改为指向其前驱,空的右孩子指针域改为指向其后继。称这种新的指针(前驱或后继)为线索,所得到的二叉树被称为线索二叉树,将二叉树转变成线索二叉树的过程称为线索化。<br>同时,为了区分到底指针是指向前驱(后继)还是孩子,要加入两个标志来判断。<br>结构:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">node</span></span></span><br><span class="line"><span class="class">{</span></span><br><span class="line"> <span class="type">int</span> ltag, rtag; <span class="comment">//0为孩子,1为前驱或后继</span></span><br><span class="line"> datatype data;</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">node</span> *<span class="title">lchild</span>, *<span class="title">rchild</span>;</span></span><br><span class="line">} ordertree;</span><br></pre></td></tr></table></figure><p>先序后继的求解:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">ordertree *<span class="title function_">presuc</span><span class="params">(ordertree *P)</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">if</span> (P->ltag == <span class="number">0</span>)</span><br><span class="line"> <span class="keyword">return</span> P->lchild;</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> <span class="keyword">return</span> P->rchild;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>中序后继:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">ordertree *<span class="title function_">insuc</span><span class="params">(ordertree *P)</span></span><br><span class="line">{</span><br><span class="line"> ordertree *q = P->rchild;</span><br><span class="line"> <span class="keyword">if</span> (P->rtag == <span class="number">1</span>)</span><br><span class="line"> <span class="keyword">return</span> q;</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">while</span> (q->ltag == <span class="number">0</span>)</span><br><span class="line"> q = q->lchild;</span><br><span class="line"> <span class="keyword">return</span> q;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>中序先驱:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">ordertree *<span class="title function_">infore</span><span class="params">(ordertree *P)</span></span><br><span class="line">{</span><br><span class="line"> ordertree *q = P->lchild;</span><br><span class="line"> <span class="keyword">if</span> (P->ltag == <span class="number">1</span>)</span><br><span class="line"> <span class="keyword">return</span> q;</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">while</span> (q->rtag == <span class="number">0</span>)</span><br><span class="line"> q = q->rchild;</span><br><span class="line"> <span class="keyword">return</span> q;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>后序先驱:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">ordertree *<span class="title function_">postfore</span><span class="params">(ordertree *P)</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">if</span> (P->rtag == <span class="number">0</span>)</span><br><span class="line"> <span class="keyword">return</span> P->rchild;</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> <span class="keyword">return</span> P->lchild;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="二叉查找树"><a href="#二叉查找树" class="headerlink" title="二叉查找树"></a>二叉查找树</h4><p>即二叉搜索树或二叉排序树:</p><p>1.所有非叶子结点至多拥有两个儿子(Left和Right);</p><p>2.所有结点存储一个关键字;</p><p>3.非叶子结点的左指针指向小于其关键字的子树,右指针指向大于其关键字的子树;</p><p>如:<br><img src="/2016/07/26/data-structure/BTree.JPG" alt="二叉查找树"><br>查找树的搜索,从根结点开始,如果查询的关键字与结点的关键字相等,那么就命中;否则,如果查询关键字比结点关键字小,就进入左儿子;如果比结点关键字大,就进入右儿子;如果左儿子或右儿子的指针为空,则报告找不到相应的关键字;如果查找树的所有非叶子结点的左右子树的结点数目均保持差不多(平衡),那么查找树的搜索性能逼近二分查找;但它比连续内存空间的二分查找的优点是,改变查找树结构(插入与删除结点)不需要移动大段的内存数据,甚至通常是常数开销;</p><p>如:<br><img src="/2016/07/26/data-structure/BTree2.JPG" alt="二叉查找树2"><br>但查找树在经过多次插入与删除后,有可能导致不同的结构:<br><img src="/2016/07/26/data-structure/BTree3.JPG" alt="二叉查找树3"><br>右边也是一个查找树,但它的搜索性能已经是线性的了;同样的关键字集合有可能导致不同的树结构索引;所以,使用查找树还要考虑尽可能让B树保持左图的结构,和避免右图的结构,也就是所谓的“平衡”问题; </p><p>实际使用的B树都是在原查找树的基础上加上平衡算法,即“平衡二叉树”;如何保持查找树结点分布均匀的平衡算法是平衡二叉树的关键;平衡算法是一种在B树中插入和删除结点的策略;</p><h4 id="B-树"><a href="#B-树" class="headerlink" title="B-树"></a>B-树</h4><p>是一种多路搜索树(并不是二叉的):</p><p>1.定义任意非叶子结点最多只有M个儿子;且M>2;</p><p>2.根结点的儿子数为[2, M];</p><p>3.除根结点以外的非叶子结点的儿子数为[M/2, M];</p><p>4.每个结点存放至少M/2-1(取上整)和至多M-1个关键字;(至少2个关键字)</p><p>5.非叶子结点的关键字个数=指向儿子的指针个数-1;</p><p>6.非叶子结点的关键字:K[1], K[2], …, K[M-1];且K[i] < K[i+1];</p><p>7.非叶子结点的指针:P[1], P[2], …, P[M];其中P[1]指向关键字小于K[1]的子树,P[M]指向关键字大于K[M-1]的子树,其它P[i]指向关键字属于(K[i-1], K[i])的子树;</p><p>8.所有叶子结点位于同一层;</p><p>如:(M=3)<br><img src="/2016/07/26/data-structure/B-Tree.JPG" alt="B-树"><br>B-树的搜索,从根结点开始,对结点内的关键字(有序)序列进行二分查找,如果命中则结束,否则进入查询关键字所属范围的儿子结点;重复,直到所对应的儿子指针为空,或已经是叶子结点;</p><p>B-树的特性:</p><p>1.关键字集合分布在整颗树中;</p><p>2.任何一个关键字出现且只出现在一个结点中;</p><p>3.搜索有可能在非叶子结点结束;</p><p>4.其搜索性能等价于在关键字全集内做一次二分查找;</p><p>5.自动层次控制;</p><p>由于限制了除根结点以外的非叶子结点,至少含有M/2个儿子,确保了结点的至少利用率,其最底搜索性能为:<br><img src="/2016/07/26/data-structure/B-Tree2.JPG" alt="B-树2"><br>其中,M为设定的非叶子结点最多子树个数,N为关键字总数;所以B-树的性能总是等价于二分查找(与M值无关),也就没有B树平衡的问题;由于M/2的限制,在插入结点时,如果结点已满,需要将结点分裂为两个各占M/2的结点;删除结点时,需将两个不足M/2的兄弟结点合并;</p><h4 id="B-树-1"><a href="#B-树-1" class="headerlink" title="B+树"></a>B+树</h4><p>B+树是B-树的变体,也是一种多路搜索树:</p><p>1.其定义基本与B-树同,除了:</p><p>2.非叶子结点的子树指针与关键字个数相同;</p><p>3.非叶子结点的子树指针P[i],指向关键字值属于[K[i], K[i+1])的子树</p><p>(B-树是开区间);</p><p>5.为所有叶子结点增加一个链指针;</p><p>6.所有关键字都在叶子结点出现;</p><p>如:(M=3)<br><img src="/2016/07/26/data-structure/B+Tree.JPG" alt="B+树"><br>B+的搜索与B-树也基本相同,区别是B+树只有达到叶子结点才命中(B-树可以在非叶子结点命中),其性能也等价于在关键字全集做一次二分查找;</p><p>B+的特性:</p><p>1.所有关键字都出现在叶子结点的链表中(稠密索引),且链表中的关键字恰好是有序的;</p><p>2.不可能在非叶子结点命中;</p><p>3.非叶子结点相当于是叶子结点的索引(稀疏索引),叶子结点相当于是存储(关键字)数据的数据层;</p><p>4.更适合文件索引系统;</p><h4 id="B-树-2"><a href="#B-树-2" class="headerlink" title="B*树"></a>B*树</h4><p>是B+树的变体,在B+树的非根和非叶子结点再增加指向兄弟的指针;<br><img src="/2016/07/26/data-structure/B*Tree.JPG" alt="B*树"><br>B*树定义了非叶子结点关键字个数至少为(2/3)*M,即块的最低使用率为2/3(代替B+树的1/2);</p><p>B+树的分裂:<br>当一个结点满时,分配一个新的结点,并将原结点中1/2的数据复制到新结点,最后在父结点中增加新结点的指针;B+树的分裂只影响原结点和父结点,而不会影响兄弟结点,所以它不需要指向兄弟的指针;</p><p>B*树的分裂:</p><p>当一个结点满时,如果它的下一个兄弟结点未满,那么将一部分数据移到兄弟结点中,再在原结点插入关键字,最后修改父结点中兄弟结点的关键字(因为兄弟结点的关键字范围改变了);如果兄弟也满了,则在原结点与兄弟结点之间增加新结点,并各复制1/3的数据到新结点,最后在父结点增加新结点的指针;所以,B*树分配新结点的概率比B+树要低,空间使用率更高;</p><p>小结</p><p>B树:二叉树,每个结点只存储一个关键字,等于则命中,小于走左结点,大于走右结点;</p><p>B-树:多路搜索树,每个结点存储M/2到M个关键字,非叶子结点存储指向关键字范围的子结点;所有关键字在整颗树中出现,且只出现一次,非叶子结点可以命中;</p><p>B+树:在B-树基础上,为叶子结点增加链表指针,所有关键字都在叶子结点中出现,非叶子结点作为叶子结点的索引;B+树总是到叶子结点才命中;</p><p>B*树:在B+树基础上,为非叶子结点也增加链表指针,将结点的最低利用率从1/2提高到2/3;</p><h4 id="树和森林"><a href="#树和森林" class="headerlink" title="树和森林"></a>树和森林</h4><p> 树的存储结构:<br>(1)双亲表示法</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">tnode</span></span></span><br><span class="line"><span class="class">{</span></span><br><span class="line"> datatype data;</span><br><span class="line"> <span class="type">int</span> parent;</span><br><span class="line">}</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">tnode</span> <span class="title">treelist</span>[<span class="title">MAXSIZE</span>];</span> <span class="comment">//整个树的存储数组说明</span></span><br></pre></td></tr></table></figure><p>其中parent指示该结点父结点的下标,data存放结点的值。<br>优点:便于搜索相应结点的父结点和祖先结点。<br>缺点:若要搜索孩子结点或后代结点需要搜索整个表,浪费时间。</p><p>(2)孩子链表表示法<br>分别将每个结点的孩子结点连成一个链表,然后将各表头指针放在一个表中构成一个整体结构。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">node</span> //链表中每个孩子结点的定义</span></span><br><span class="line"><span class="class">{</span></span><br><span class="line"> <span class="type">int</span> data;</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">node</span> *<span class="title">next</span>;</span></span><br><span class="line">} listnode;</span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> //数组元素的定义,每个数组元素都是一个单链表,单头元素不同</span></span><br><span class="line"><span class="class">{</span></span><br><span class="line"> datatype info;</span><br><span class="line"> listnode *firstchild;</span><br><span class="line">} arrnode;</span><br><span class="line">arrnode tree[MAXSIZE]; <span class="comment">//MAXSIZE为所有结点的个数</span></span><br></pre></td></tr></table></figure><p>优缺点:与双亲表示法恰好相反。</p><p>(3)孩子-兄弟链表表示法(二叉链表表示法,二叉树表示法)<br>树中每个结点用一个链表结点来存储,每个链表结点中除了存放结点的值外,还有两个指针,一个用来指示该结点的第一个孩子,另一个用于指示该结点的下一个兄弟结点。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">node</span></span></span><br><span class="line"><span class="class">{</span></span><br><span class="line"> datatype data;</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">node</span> *<span class="title">firstchild</span>, *<span class="title">nextbrother</span>;</span></span><br><span class="line">} tnode;</span><br></pre></td></tr></table></figure><p> 树(森林)与二叉树的转换<br>树或森林的子树转换为二叉树的左子树,兄弟转化为右子树。</p><p>3.树(森林)的遍历<br>树的遍历可分为先序遍历和后序遍历。(注意没有中序,因为树有不只两个孩子)即结点是在其子树之前还是之后访问。<br>遍历树(森林)要转换为遍历其对应的二叉树:<br>先序遍历:(同二叉树的先序遍历)</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">preorder</span><span class="params">(tnode *T)</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">if</span> (T != <span class="literal">NULL</span>)</span><br><span class="line"> {</span><br><span class="line"> visit(T);</span><br><span class="line"> preorder(T->firstchild);</span><br><span class="line"> preorder(T->nextbrother);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>后序遍历:(同二叉树的中序遍历)</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">postorder</span><span class="params">(tnode *T)</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">if</span> (T != <span class="literal">NULL</span>)</span><br><span class="line"> {</span><br><span class="line"> postorder(T->firstchild);</span><br><span class="line"> visit(T);</span><br><span class="line"> postorder(T->nextbrother);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="哈夫曼树"><a href="#哈夫曼树" class="headerlink" title="哈夫曼树"></a>哈夫曼树</h4><p>哈夫曼树主要用来处理压缩算法。<br>一般的判断问题的流程就象是一棵二叉树,其中分支(判断)结点对应于二叉树的分支结点;而最后得出的结论对应于叶子结点;一个结论所需要的判断次数是从根结点到该叶子结点的分支线数(层次数-1);每个结论成立的次数作为叶子结点的权值。<br>(这个权值可能比较少接触,但是其实它非常重要,因为我们平时设计的系统,判断的结果常常都是通过长年的实践会有一个出现机率分配,而不可能是平分的,比如考试,如果常常80-90分的比较多,也许就要换一种算法,当然这是后话,和考试无关了.)</p><p>哈夫曼算法步骤如下:<br>(1)根据给定的n个权值,构成一排结点T,每个的值都是相应的权值.<br>(2)从T中选两棵权值最小的二叉树,作为左右子树构成一棵新的二叉树T’,并且新二叉树的权值为左右子树权值之和.<br>(3)将新二叉树T’并入到T中,删除原来的两棵二叉树.<br>(4)重复2,3直到只剩一棵二叉树.这棵树就是哈夫曼树.</p><p>哈夫曼树的带权路径长度$WPL=\sum wL$<br>即所有叶子结点的 权值*比较次数(层次数-1) 之和.<br>而WPL也正好等于所有分支结点(不包括叶子结点)的值之和.</p><h3 id="图"><a href="#图" class="headerlink" title="图"></a>图</h3><p>图中将每个对象用一个顶点表示,并常用一个序号来标识一个顶点。<br>其中弧表示单向关系,边表示双向关系,用离散数学中的术语来说,则分别表示为非对称关系和对称关系。<br>弧用<A, B>表示(A为尾,B为头),边用(A, B)表示。<br>一个图G由两部分内容构成,即顶点(vertex)集合(V)和边(或弧edge)的集合(E),并用二元组(V, E)来表示,记做G = (V, E)<br>根据顶点间的关系是否有向而引入有向图和无向图。<br>给每条边或弧加上权值,这样的带权图称为网络。</p><p>若无向图中任意两点间都有一条边,则称此图G为无向完全图。(共有边数 $n*(n-1)/2$)</p><p>若有向图中任意一个顶点到其余各点间均有一条弧,则称为有向完全图。(共有弧数 $n*(n-1)$)</p><p>若一个图G1是从G中选取部分顶点和部分边(或弧)组成,则称G1是G的子图。(注意,顶点和边必须都为子关系)</p><p>若无向图中两个顶点i, j之间存在一条边,则称i, j相邻接,并互为邻接点。<br>在有向图中,若存在弧<Vi, Vj>,也做Vi, Vj相邻接,但为区别弧的头、尾顶点,可进一步称做Vi邻接到Vj,Vj邻接于Vi。</p><p>与一个顶点相邻接的顶点数称为该顶点的度。<br>在有向图中,进入一个顶点的弧数称为该顶点的入度,从一个顶点发出的弧数为该顶点的出度,并将入度和出度之和作为该顶点的度。</p><p>一个顶点经过一定的可经路程到达另一个顶点,就为顶点之间的路径。<br>若某路径所经过的顶点不重复,则称此路径为简单路径。<br>若某路径的首尾相同,则称此路径为回路(或称为环)。<br>若某回路的中间不重复,则称之为简单回路。</p><p>若无向图中任意两点之间均存在路径,则称G为连通图,否则不连通,就存在若干个连通分量。<br>若有向图中任意两点间可以互相到达,则称为强连通图。</p><p>一个无向图,连通并且无回路,称这样的图为树。<br>若有向图中仅有一个顶点的入度为0,其余顶点的入度都为1,称此图为有向树,入度为0的顶点为根。</p><p>图的存储结构:<br>1。邻接矩阵表示<br>对n个顶点的图来说,其邻接矩阵为n*n阶的。<br>邻接矩阵的元素存放边(弧)的权值,对不存在的边(弧),则用0或∞表示。<br>定义格式如下:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> n 6 <span class="comment">/* 图顶点数 */</span> </span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> e 8 <span class="comment">/* 图的边(弧)数 */</span></span></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span></span></span><br><span class="line"><span class="class">{</span></span><br><span class="line"> vextype vexs[n]; <span class="comment">/* 顶点类型 */</span></span><br><span class="line"> datatype arcs[n][n]; <span class="comment">/* 权值类型 */</span></span><br><span class="line">} graph; </span><br><span class="line">建立一个无向网络的算法: </span><br><span class="line">CreateGraph(graph *G) </span><br><span class="line">{ </span><br><span class="line"> <span class="type">int</span> i, j, k; </span><br><span class="line"> <span class="type">float</span> w; </span><br><span class="line"> <span class="keyword">for</span> (i=<span class="number">0</span>; i<n; i++) </span><br><span class="line"> G->vexs[i] = getchar(); <span class="comment">/* 读入顶点信息,创建表,这里用字符型 */</span> </span><br><span class="line"> <span class="keyword">for</span> (i=<span class="number">0</span>; i<n; i++) </span><br><span class="line"> <span class="keyword">for</span> (j=<span class="number">0</span>; j<n; j++) </span><br><span class="line"> G->arcs[i][j] = <span class="number">0</span>; <span class="comment">/* 邻接矩阵初始化 */</span> </span><br><span class="line"> <span class="keyword">for</span> (k=<span class="number">0</span>; k<e; k++) </span><br><span class="line"> { </span><br><span class="line"> <span class="built_in">scanf</span>(<span class="string">"%d%d%f"</span>, &i, &j, &w); <span class="comment">/* 读入边(vi, vj)上的权w(暂用float类型) */</span> </span><br><span class="line"> G->arcs[i][j] = w; </span><br><span class="line"> G->arcs[j][i] = w; </span><br><span class="line"> } </span><br><span class="line">}</span><br></pre></td></tr></table></figure><p> 邻接表表示法<br>将每个顶点的邻接点连成链表,并将各链表的表头指针合在一起(用数组或链表表示均可),其中每个头指针与该结点的信息合为一个整体结点。<br>如果将邻接表中各顶点的邻接表变为其前驱顶点即可,从而得到逆邻接表。<br>用邻接表存储网络时,需要将各条边(弧)的权值作为相应邻接结点中的一个字段。<br>结构:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">node</span></span></span><br><span class="line"><span class="class">{</span></span><br><span class="line"> <span class="type">int</span> adjvex; <span class="comment">/* 邻接点域 */</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">node</span> *<span class="title">next</span>;</span> <span class="comment">/* 链域 */</span></span><br><span class="line"> datatype arc; <span class="comment">/* 权值 */</span></span><br><span class="line">} edgenode; <span class="comment">/* 边表指针 */</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span></span></span><br><span class="line"><span class="class">{</span></span><br><span class="line"> vextype vertex; <span class="comment">/* 顶点信息 */</span></span><br><span class="line"> edgenode *link; <span class="comment">/* 边表头指针 */</span></span><br><span class="line">} vexnode; <span class="comment">/* 顶点表结点 */</span></span><br><span class="line">vexnode gnode[n]; <span class="comment">/* 整个图的构成 */</span></span><br></pre></td></tr></table></figure><p> 建立无向图的邻接表:</p> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">CreateAdjlist(gnode)</span><br><span class="line">{</span><br><span class="line"> <span class="type">int</span> i, j, k;</span><br><span class="line"> edgenode *s;</span><br><span class="line"> <span class="keyword">for</span> (i=<span class="number">0</span>; i<n; i++) <span class="comment">/* 读入顶点信息 */</span></span><br><span class="line"> {</span><br><span class="line"> gnode[i].vertex = getchar();</span><br><span class="line"> gnode[i].link = <span class="literal">NULL</span>; <span class="comment">/* 边表指针初始化 */</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (k=<span class="number">0</span>; k<e; k++) <span class="comment">/* 建立边表 */</span></span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">scanf</span>(<span class="string">"%d%d"</span>, &i, &j); <span class="comment">/* 读入边(vi,vj)的顶点序号 */</span></span><br><span class="line"> s = <span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(edgenode)); <span class="comment">/* 生成邻接点序号为j的表结点 */</span></span><br><span class="line"> s->adjvex = j;</span><br><span class="line"> s->next = gnode[i].link;</span><br><span class="line"> gnode[i].link = s; <span class="comment">/* 将*s插入顶点vi的边表头部(插到头部比尾部简单) */</span></span><br><span class="line"> s = <span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(edgenode)); <span class="comment">/* 生成邻接点序号为i的边表结点*s */</span></span><br><span class="line"> s->adjvex = i;</span><br><span class="line"> s->next = gnode[j].link;</span><br><span class="line"> gnode[j].link = s; <span class="comment">/* 将*s插入顶点vj的边表头部(最后四行由于是无向图,所以相互,两次) */</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="图的遍历算法及其应用"><a href="#图的遍历算法及其应用" class="headerlink" title="图的遍历算法及其应用"></a>图的遍历算法及其应用</h4><p> 深度遍历<br>(1)访问V0<br>(2)依次从V0 的各个未被访问的邻接点出发深度遍历<br>(两句话说的非常清楚。是一种以深度为绝对优先的访问。)</p><p>2。深度优先搜索遍历算法<br>由于实际算法比较复杂,这里算法依赖两个函数来求解(对于不同的存储结构有不同的写法)<br>firstadj(G, v):返回图G中顶点v的第一个邻接点。若不存在,返回0。<br>nextadj(G, v, w):返回图G中顶点v的邻接点中处于w之后的那个邻接点。若不存在,返回0。<br>depth first search:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">dfs</span><span class="params">(graph G, <span class="type">int</span> v)</span></span><br><span class="line">{</span><br><span class="line"> <span class="type">int</span> w;</span><br><span class="line"> visit(v);</span><br><span class="line"> visited[v] = <span class="number">1</span>;</span><br><span class="line"> w = firstadj(G, v)</span><br><span class="line"> <span class="keyword">while</span> (w != <span class="number">0</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (visited[w] == <span class="number">0</span>)</span><br><span class="line"> dfs(w);</span><br><span class="line"> w = nextadj(G, v, w);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>如果不是连通图,或者是有向图,那么访问一个v不可能遍历所有顶点。所以,需要再选择未被访问的顶点作为起点再调用dfs.</p><p>所以,深度遍历图的算法如下:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">dfs_travel</span><span class="params">(graph G)</span></span><br><span class="line">{</span><br><span class="line"> <span class="type">int</span> i;</span><br><span class="line"> <span class="keyword">for</span> (i=<span class="number">1</span>; i<=n; i++)</span><br><span class="line"> visited[i] = <span class="number">0</span>; <span class="comment">//初始化各顶点的访问标志</span></span><br><span class="line"> <span class="keyword">for</span> (i=<span class="number">1</span>; i<=n; i++)</span><br><span class="line"> <span class="keyword">if</span> (visited[i] == <span class="number">0</span>)</span><br><span class="line"> dfs(G, i);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>3.广度优先搜索遍历算法<br>广度优先搜索遍历算法(bfs)是一种由近而远的层次遍历算法,从顶点V0出发的广度遍历bfs描述为:<br>(1)访问V0(可作为访问的第一层);<br>(2)假设最近一层的访问顶点依次为V1, V2, …, Vk,则依次访问他们的未被访问的邻接点。<br>(3)重复2,直到找不到未被访问的邻接点为止。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">bfs</span><span class="params">(graph G, <span class="type">int</span> V0)</span></span><br><span class="line">{</span><br><span class="line"> <span class="type">int</span> w;</span><br><span class="line"> <span class="type">int</span> v;</span><br><span class="line"> <span class="built_in">queue</span> Q;</span><br><span class="line"> init_queue(Q);</span><br><span class="line"> visit(V0);</span><br><span class="line"> visited[V0] = <span class="number">1</span>;</span><br><span class="line"> Enqueue(Q, V0);</span><br><span class="line"> <span class="keyword">while</span> (!empty(Q))</span><br><span class="line"> {</span><br><span class="line"> v = Outqueue(Q);</span><br><span class="line"> w = firstadj(G, v);</span><br><span class="line"> <span class="keyword">while</span> (w != <span class="number">0</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (visited[w] == <span class="number">0</span>)</span><br><span class="line"> {</span><br><span class="line"> visit(w);</span><br><span class="line"> visited[w] = <span class="number">1</span>;</span><br><span class="line"> Enqueue(Q, w);</span><br><span class="line"> }</span><br><span class="line"> w = nextadj(G, v, w);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>广度遍历图的算法和深度一样:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">bfs_travel</span><span class="params">(graph G)</span></span><br><span class="line">{</span><br><span class="line"> <span class="type">int</span> i;</span><br><span class="line"> <span class="keyword">for</span> (i=<span class="number">1</span>; i<=n; i++)</span><br><span class="line"> visited[i] = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (i=<span class="number">1</span>; i<=n; i++)</span><br><span class="line"> <span class="keyword">if</span> (visited[i] = <span class="number">0</span>)</span><br><span class="line"> bfs(G, i);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="最小生成树:"><a href="#最小生成树:" class="headerlink" title="最小生成树:"></a>最小生成树:</h4><p>从图中选取若干条边,将所有顶点连接起来,并且所选取的这些边的权值之和最小。<br>这样所选取的边构成了一棵树,称这样的树为生成树,由于权值最小,称为最小生成树。</p><p>构造最小生成树有两种方法:<br> Prim算法:<br>首先将所指定的起点作为已选顶点,然后反复在满足如下条件的边中选择一条最小边,直到所有顶点成为已选顶点为止(选择n-1条边):一端已选,另一端未选。<br>(简单的说,就是先任选一点,然后每次选择一条最小权值的边,而且只连接到一个已选顶点)</p><p> Kruskal算法:<br>反复在满足如下条件的边中选出一条最小的,和已选边不够成回路。<br>(条件就是不够成回路就OK,反复选最小边,知道所有顶点都有连接)</p><p>最短路径:<br>一般即是要一个顶点到其余各个顶点的最短路径。(比如隔很远的顶点,要绕哪几条边走)<br>求解方法:<br>首先,我们要画一个表,每个顶点有path和dist两个值,分别用来存储到各点的最短路径(比如(1,5,6),就是1-5-6这个路径)和相应的长度(到该点的权值之和)。<br>(1)对V以外的各顶点,若两点间的邻接路径存在,则将其作为最短路径和最短长度存到path[v]和dist[v]中。(实际上也就是最开始对顶点的直接后继进行处理)<br>(2)从未解顶点中选择一个dist值最小的顶点v,则当前的path[v]和dist[v]就是顶点v的最终解(从而使v成为已解顶点)。<br>(3)如果v的直接后继经过v会更近一些,则修改v的直接后继的path和dist值。</p><p>(上面的确是很难懂,只能通过例子自己慢慢熟悉。)<br> 查找</p><p>在软件设计中,通常是将待查找的数据元素集以某种表的形式给出,从而构成一种新的数据结构--查找表。<br>表包括一些“元素”,“字段”等等概念。</p><p>在一个数据表中,若某字段的值可以标识一个数据元素,则称之为关键字(或键)。<br>若此关键字的每个值均可以唯一标识一个元素,则称之为主关键字,否则,若该关键字可以标识若干个元素,则称之为次关键字。</p><p>查找算法的时间性能一般以查找次数来衡量。所谓查找长度是指查找一个元素所进行的关键字的比较次数。常以平均查找次数、最大查找次数来衡量查找算法的性能。</p><h3 id="简单顺序查找"><a href="#简单顺序查找" class="headerlink" title="简单顺序查找"></a>简单顺序查找</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">seq_seach</span><span class="params">(elementtype A[], <span class="type">int</span> n, keytype x)</span></span><br><span class="line">{</span><br><span class="line"> <span class="type">int</span> i;</span><br><span class="line"> A[<span class="number">0</span>].key = x; <span class="comment">//设定监视哨</span></span><br><span class="line"> <span class="keyword">for</span> (i=n; A[i].key!=x; i--);</span><br><span class="line"> <span class="keyword">return</span> i;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>监视哨是一个小技巧,查找失败时,这里设定的数据是A<a href="/data-structure/BTree.JPG">1</a>-A[n],肯定可以在A[0]中找到该元素,并返回0表示查找失败。如果不设定监视哨,则在每次循环中要判断下标是否越界:for (i=1; i!=n&&A[i].key!=x;i–); 可以节省一半的时间。</p><h3 id="有序表的二分查找"><a href="#有序表的二分查找" class="headerlink" title="有序表的二分查找"></a>有序表的二分查找</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">bin_search</span><span class="params">(elementtype A[], <span class="type">int</span> n, keytype x)</span></span><br><span class="line">{</span><br><span class="line"> <span class="type">int</span> mid, low, high;</span><br><span class="line"> low = <span class="number">0</span>;</span><br><span class="line"> high = n - <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">while</span> (low <= high)</span><br><span class="line"> {</span><br><span class="line"> mid = (low + high) / <span class="number">2</span>;</span><br><span class="line"> <span class="keyword">if</span> (x == A[mid].key)</span><br><span class="line"> <span class="keyword">return</span> mid;</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (x < A[mid].key)</span><br><span class="line"> high = mid - <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> low = mid + <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>也可以使用递归算法:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">bin_search</span><span class="params">(elementtype A[], <span class="type">int</span> low, <span class="type">int</span> high, keytype x)</span></span><br><span class="line">{</span><br><span class="line"> <span class="type">int</span> mid;</span><br><span class="line"> <span class="keyword">if</span> (low > high)</span><br><span class="line"> <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> {</span><br><span class="line"> mid = (low + high) / <span class="number">2</span>;</span><br><span class="line"> <span class="keyword">if</span> (x == A[mid].key)</span><br><span class="line"> <span class="keyword">return</span> mid;</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (x < A[mid],key)</span><br><span class="line"> <span class="keyword">return</span> bin_search(A, low, mid - <span class="number">1</span>, x);</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> <span class="keyword">return</span> bin_search(A, mid - <span class="number">1</span>, high, x);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="排序"><a href="#排序" class="headerlink" title="排序"></a>排序</h3><p>增排序和减排序:如果排序的结果是按关键字从小到大的次序排列的,就是增排序,否则就是减排序。<br>内部排序和外部排序:如果在排序过程中,数据表中所有数据均在内存中进行,则这类排序为内部排序,否则就是外部排序。<br>稳定排序和不稳定排序:在排序过程中,如果关键字相同的两个元素的相对次序不变,则称为稳定排序,否则是不稳定排序。</p><p>在分析算法的时间性能时,主要以算法中用的最多的基本操作的执行次数(或者其数量级)来衡量,这些操作主要是比较、移动和交换元素。有时,可能要用这些次数的平均数来表示。</p><h4 id="插入排序"><a href="#插入排序" class="headerlink" title="插入排序"></a>插入排序</h4><p>基本思想:把整个待排序子表看作是左右两部分,其中左边为有序区,右边为无序区,整个排序过程就是把右边无序区中的元素逐个插入到左边的有序区中,以构成新的有序区。<br>实际中,开始排序时把第一个元素A[0](或A[1])看作左边的有序区,然后把剩下的2~N个元素依次插入到有序表中。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">insert_sort</span><span class="params">(elementtype A[n+<span class="number">1</span>])</span></span><br><span class="line">{</span><br><span class="line"> <span class="type">int</span> i;</span><br><span class="line"> <span class="keyword">for</span> (i=<span class="number">2</span>; i<=n; i++)</span><br><span class="line"> {</span><br><span class="line"> A[<span class="number">0</span>] = A[i]; <span class="comment">//设置监视哨,这个数组同样是从1开始,A[0]就设为监视哨</span></span><br><span class="line"> j = i - <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">while</span> (A[j].key > A[<span class="number">0</span>].key)</span><br><span class="line"> {</span><br><span class="line"> A[j + <span class="number">1</span>] = A[j];</span><br><span class="line"> j--;</span><br><span class="line"> }</span><br><span class="line"> A[j + <span class="number">1</span>] = A[<span class="number">0</span>];</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>明白这种方法的简单原理:<br>$a_1 a_2 a_3 … a_{i-1} a_i …$<br>先将ai临时保存起来,然后把$a_{i-1}$向前只要是比$a_i$大的向后移,再把$a_i$填进去即可。</p><h4 id="快速排序"><a href="#快速排序" class="headerlink" title="快速排序"></a>快速排序</h4><p>速度最快的办法!一定要掌握,考试重点。<br>基本思想:首先,选定一个元素作为中间元素,然后将表中所有元素与该中间元素相比较,将表中比中间元素小的元素调到表的前面,将比中间元素大的元素调到后面,再将中间数放在这两部分之间作为分界点,这样便得到一个划分;然后再对左右两部分分别进行快速排序,如此反复,直到每个子表仅有一个元素或空表为止。<br>中间数一般选择部分的第一个元素。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">partition</span><span class="params">(elementtype A[n], <span class="type">int</span> s, <span class="type">int</span> t)</span> <span class="comment">//s,t是要排序元素的起点和终点,并返回最后中间元素位置</span></span><br><span class="line">{</span><br><span class="line"> elementtype x = A[s]; <span class="comment">//保存中间元素到临时变量x,以腾出空位</span></span><br><span class="line"> <span class="type">int</span> i = s; <span class="comment">//置两端搜索位置的初值</span></span><br><span class="line"> <span class="type">int</span> j = t;</span><br><span class="line"> <span class="keyword">while</span> (i != j) <span class="comment">//两端位置重和再停止</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">while</span> (i < j && A[j].key > x.key) j--; <span class="comment">//从后面搜索“小”的元素</span></span><br><span class="line"> <span class="keyword">if</span> (i < j) <span class="comment">//如果找到,就调到前面的空位中</span></span><br><span class="line"> {</span><br><span class="line"> A[i] = A[j];</span><br><span class="line"> i++;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">while</span> (i < j && A[i].key < x.key) i++; <span class="comment">//从前面搜索“大”的元素</span></span><br><span class="line"> <span class="keyword">if</span> (i < j) <span class="comment">//如果找到,调到后面的空位中</span></span><br><span class="line"> {</span><br><span class="line"> A[j] = A[i];</span><br><span class="line"> j--;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> A[i] = x; <span class="comment">//将中间数移到最终位置上</span></span><br><span class="line"> <span class="keyword">return</span> i;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>整个算法:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">quick_sort</span><span class="params">(elementtype A[n], <span class="type">int</span> s, <span class="type">int</span> t)</span> <span class="comment">//对数组中下标从s到t的部分进行快速排序,如果是整个表就是0, n-1</span></span><br><span class="line">{</span><br><span class="line"> <span class="type">int</span> i;</span><br><span class="line"> <span class="keyword">if</span> (s < t) <span class="comment">//表中至少有两个元素时</span></span><br><span class="line"> {</span><br><span class="line"> i = partition(A, s, t); <span class="comment">//划分排序一次</span></span><br><span class="line"> quick_sort(A, i + <span class="number">1</span>, t); <span class="comment">//对后面部分快速排序</span></span><br><span class="line"> quick_sort(A, s, i - <span class="number">1</span>); <span class="comment">//对前面部分快速排序</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="选择排序:"><a href="#选择排序:" class="headerlink" title="选择排序:"></a>选择排序:</h4><p>在待排序子表中完整地比较一遍以确定最大(小)元素,并将该元素放在子表的最前(后)面。<br>【注:可能发觉和冒泡法比较类似,但注意选择法是全部比较一遍,找到最小元素的下标,再进行一次交换,而冒泡则是进行多次交换】</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">select_sort</span><span class="params">(elementtype A[n])</span></span><br><span class="line">{</span><br><span class="line"> <span class="type">int</span> min, i, j;</span><br><span class="line"> elementtype temp;</span><br><span class="line"> <span class="keyword">for</span> (i=<span class="number">0</span>; i<n<span class="number">-1</span>; i++)</span><br><span class="line"> {</span><br><span class="line"> min = i;</span><br><span class="line"> <span class="keyword">for</span> (j=i+<span class="number">1</span>; j<n; j++)</span><br><span class="line"> <span class="keyword">if</span> (A[min].key > A[j].key) min = j;</span><br><span class="line"> <span class="keyword">if</span> (min != i)</span><br><span class="line"> {</span><br><span class="line"> temp = A[i];</span><br><span class="line"> A[i] = A[min];</span><br><span class="line"> A[min] = temp;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="归并排序"><a href="#归并排序" class="headerlink" title="归并排序"></a>归并排序</h4><p>所谓归并是指将两个或两个以上的有序表合并成一个新的有序表。<br>归并算法:<br>假设两个序列A[m]和B[n]为非降序列(即存在相同元素的升序列),现要把他们合并为一个非降序列C[m+n]。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">merge</span><span class="params">(elementtype A[], elementtype B[], elementtype C[], <span class="type">int</span> m, <span class="type">int</span> n)</span></span><br><span class="line">{</span><br><span class="line"> <span class="type">int</span> ia = <span class="number">0</span>, ib = <span class="number">0</span>, ic = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">while</span> (ia < m && ib < n)</span><br><span class="line"> <span class="keyword">if</span> (A[ia] <= B[ib])</span><br><span class="line"> C[ic++] = A[ia++];</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> C[ic++] = B[ib++];</span><br><span class="line"> <span class="keyword">while</span> (ia < m)</span><br><span class="line"> C[ic++] = A[ia++];</span><br><span class="line"> <span class="keyword">while</span> (ib < n)</span><br><span class="line"> C[ic++] = B[ib++];</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>部分转自:<a href="http://www.cnblogs.com/lebronjames/archive/2010/08/11/1797419.html">天堂大鸟</a></p>]]></content>
<summary type="html"><h3 id="栈"><a href="#栈" class="headerlink" title="栈"></a>栈</h3><p>栈是只能在一端进行插入和删除的线性表。<br>(别看只是个定义,非常重要,已经道出了运算方法:只能在一端插入和删除。)</p>
<p>栈的特征:后进先出,先进后出。</p>
<p>插入和删除元素的一端称为栈顶。(说明了我们在栈顶操作)<br>另一端称为栈底。<br>插入元素和删除元素的操作称为入栈和出栈。</p></summary>
<category term="技术" scheme="https://sherlozk.github.io/categories/%E6%8A%80%E6%9C%AF/"/>
<category term="数据结构" scheme="https://sherlozk.github.io/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/"/>
</entry>
<entry>
<title>这个暑假,这个三亚</title>
<link href="https://sherlozk.github.io/2016/07/23/sanya/"/>
<id>https://sherlozk.github.io/2016/07/23/sanya/</id>
<published>2016-07-23T08:36:06.000Z</published>
<updated>2022-11-19T03:11:20.411Z</updated>
<content type="html"><![CDATA[<p>在即将要投入实习拥抱工作的暑假,毅然和放牛班的各位踏上开往三亚的列车。</p><span id="more"></span><p>因为一些个人原因,来时并不能和伙伴们同行,行程被迫推迟一天,还好一个人的16个小时不算太难熬。中间跨越琼州海峡的三个小时,最是令人印象深刻,开始车上的空调冷如雪柜,在海安停车后,一点点融化,被拉上渡轮之后,又如蒸桑拿。旁边的父子,孩儿因为兴奋了半天,现在睡得正香,父亲也没闲着,赤膀子拿着一本儿子的图书来回帮儿子扇着,但仍止不住渗出的汗珠。到三亚已是子时午夜时分,月光正好。司机载这我去酒店与朋友汇合,一路收集这对三亚的第一印象—-除了酒店还是酒店。我们住在一个大得吓人的公寓小区,房间却是意外地好找,服务员给我指了指:看,中间亮着灯的两个房间就是你朋友的。才发现,整栋楼也就两个房间亮着灯。</p><p>第二天是从一场舒服的晨泳开始的,直到阳光把整个泳池洒满我们才离开。回去洗澡整顿之后,出发到我们的第一站:呀诺达热带雨林森林公园。由于早上游泳并没有吃早餐,所以八十大洋的自助餐,连本带利全吃了回来。这里的景色时而开明壮阔,时而迷途不知所处,树木错节盘根,有许多长着压路之势的树木横生,游往客人只能弯腰穿行,这里美其名曰:强龙不压地头蛇。下午四时,让司机把我们送回了三亚湾,看日落。三亚湾十八公里长的海岸线,无论怎样贪婪的目光也无法将其一眼尽收眼底。这里,能让人忘却身上的伤口疼痛,也要去拥抱大海,而大海一浪一浪将我们推回岸上,又在一浪一浪之间将我们扯向自己,我们就这样被挑逗得不亦乐乎。抬头看看东方,一轮清白的月影已经印在天空,再望望西边,长云断日,似有上下两个太阳正交相辉映,金光四散。忍着肚子饿,终于在临近晚上七时,西方被太阳灼烧了数小时的云,开始呈现不同的缤纷。中间亮得发白,两边金黄,上下残红,拖着外围一片似尾巴又似翅膀的紫色幻云,被打湿的海滩也照应了这余晖,让狭长的海湾美不胜收。</p><p>三亚的天空若不是远方,多半是无云的。晚上一轮明月像海上的船儿孤零零地飘在空中,我想也只有我这种无聊的人,才会花上一个晚上,看着她从我的左边飘到右侧。</p><p>来三亚的旅人,有几个不是因为向往这里的阳光、沙滩和海浪,偏偏我是个晕船的人儿。潜水是我此行对期待的环节,我们选择了无数司机口中推荐的分界洲岛,即使是上岛的短短十几分钟就已经让我瘫倒在船上了,干脆以帽盖脸,眼不见则不晕。熬过千年,终于到了岛上,立马就直奔潜水而去。佛家说,一叶一菩提,一花一世界。一线海面隔开的是相去千里的两个世界。俗话说得好,没去过深井永远不知道烧鹅到底有多好吃,光看《海底世界》是永远体会不到身在其中的乐趣的。对于第一次潜水的人来说,有两大挑战,呼吸方式和耳膜压。在水底,只能用嘴巴吸气呼气,协调不好会吸氧不足,最糟糕的是会产生恐慌心理,产生鼻子被封堵住就要窒息的错觉,然后没有大口吸入足量的氧气,就无法平衡耳膜压,造成巨大的疼痛感,如果单独下潜,这将会是致命的。我们可怜的YL,花了不小力气才搞定。三分钟解决问题的我,要开始享受海底的奇幻世界了。我想先引用分界洲岛的一段介绍:</p><blockquote><p>“一到海底,就像刚刚出生的婴儿第一次张开双眼,看到一个全新的、色彩斑斓的世界:一切都是那么新鲜,一切都是那么亲切。让潜水者会想去抚摸一下嬉戏的小鱼,仔细看看那一半金黄一半碧绿的艳丽色泽;会想盘桓于珊瑚丛中,掬起那一片片火红;你或许还想同鲨鱼周旋,体验斗鲨的感觉……海底是一个美丽的世界,蔚蓝、透明、清澈的海水:洁净、平缓、温柔的白沙平原;摇曳生姿的藻类植物;追来逐去的庞大鱼群;茂密而奇形的珊瑚丛林…令陆地上最骄人的图画也相形见绌、黯然失色。”</p></blockquote><p>原来无论海面多么波浪翻涌,水下都能波澜不惊。成群的热带鱼就在你身边流窜,但你缺怎么都抓不住它们,就像时间,这让我开始有点伤感,因为无意外明天就要回程投入枯燥重复的生活了。强烈的耳膜疼痛将我从神游中扯了回来,我深吸一口气,示意教练,继续下潜。这一刻我想去拥抱我所见到的一切,斑斓的鱼群,嶙峋的珊瑚礁,怪石沉寂的海底,还有回头看到那摇曳的太阳……</p><p>如果说之前所有的行程都令我们很开心,那么让我们真正放开的是晚上的鱼疗温泉。在南田温泉尝试了第一次鱼疗,我只能说是惨不忍睹的舒服,有多惨不忍睹?这么说,那叫唤放在电影里是要被和谐的。不过有多舒服?谁去谁知道……</p><p>意外,还是有的……</p><p>最后一天可以用莫名其妙,但又惊喜不断来形容,莫名其妙被放到不知道是哪里的公交站,然后莫名其妙找到一家很nice的旅馆,又很莫名其妙的被谁一说:不想回去,立马集体退票。晚上在鹿回头公园瞎转,遇到山顶莫名其妙的商店老板:扎辫胖子、段子手、大砍刀开椰子、同时又是咖啡厅老板。连开八个椰子的我们最后也心满意足地回去了。就这么,说留就留的旅程也真的要快结束了。回头看看山下的三亚,那,不是我第一天下车到的地方吗?那,不是我们第一天住的酒店吗?那,不是我们一起看日落的沙滩吗?所有,所有的场景又在脑海浮现了一遍。</p><p>后记:这些都是我对三亚印象最深的一些片段,所以也都是最值得我留念的回忆。希望读者能透过这些场景稍微领略到三亚的风情。当然,我的三亚之旅绝不仅以上的精彩,还有许多比如聚众看恐怖片,在aiLin餐吧玩半小时杯子什么,这些是属于珍藏在心里的有趣,如有天,你有酒,我也愿意给你们分享。</p><p><img src="/2016/07/23/sanya/pic.jpg" alt="鹿回头"></p>]]></content>
<summary type="html"><p>在即将要投入实习拥抱工作的暑假,毅然和放牛班的各位踏上开往三亚的列车。</p></summary>
<category term="散文" scheme="https://sherlozk.github.io/categories/%E6%95%A3%E6%96%87/"/>
<category term="随笔" scheme="https://sherlozk.github.io/tags/%E9%9A%8F%E7%AC%94/"/>
<category term="游记" scheme="https://sherlozk.github.io/tags/%E6%B8%B8%E8%AE%B0/"/>
</entry>
<entry>
<title>Mac OS X 10.11 安装 Pygame</title>
<link href="https://sherlozk.github.io/2016/06/01/Installing-Pygame/"/>
<id>https://sherlozk.github.io/2016/06/01/Installing-Pygame/</id>
<published>2016-06-01T03:34:06.000Z</published>
<updated>2022-11-19T03:09:54.486Z</updated>
<content type="html"><![CDATA[<p>学完python之后就想玩些进阶的, 比如pygame.<br>但是在Mac OS X下安装pygame遇到了不少坑, 但最终还是顺利解决了, 也再一次深刻体会到google和百度的差距.</p><p><strong>Installing Pygame for OS X</strong></p><p>Note: For most of these instructions you will need to use the command line. Don’t be intimidated - programmers work on the command line all the time. Once you get used to it, you’ll find it’s the quickest and easiest way to do lots of useful things on the computer.</p><p>You access the command line by running the Terminal application - click on the Spotlight icon and type “terminal” to find it.</p><span id="more"></span><p>Step 1: Install XCode command line tools<br>XCode is the tool from Apple for creating Mac and iOS applications. It can be installed from the App Store (it’s free). When it’s finished, type the following at the command line:</p><pre><code>$ xcode-select --install</code></pre><p>Step 2: Install Homebrew (<a href="http://brew.sh/">http://brew.sh</a>)<br>Homebrew is a tool to easily install all kinds of software from the command line. It saves you having to go to a bunch of different sites and download lots of individual installers. Copy and paste this on the command line:</p><pre><code>$ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"</code></pre><p>and follow the directions. You’ll also need to install Homebrew Cask (<a href="http://caskroom.io/">http://caskroom.io</a>):</p><pre><code>$ brew install caskroom/cask/brew-cask</code></pre><p>Step 3: Install the rest of the software<br>Now we can start installing all the requirements for Pygame. Just type the following commands one at a time and let the computer do its thing:</p><pre><code>$ brew cask install xquartz$ brew install python3$ brew install python$ brew linkapps python3$ brew linkapps python$ brew install git$ brew install sdl sdl_image sdl_ttf portmidi libogg libvorbis$ brew install sdl_mixer --with-libvorbis$ brew tap homebrew/headonly$ brew install smpeg$ brew install mercurial$ pip3 install hg+http://bitbucket.org/pygame/pygame</code></pre><p>Step 4: See if it works!<br>Now we can see if it works. Run Python from the command line:</p><pre><code>$ python3</code></pre><p>and try loading Pygame:</p><pre><code>>>> import pygame</code></pre><p>If you don’t see an error message, you’re all set! Now head over to our YouTube Channel and start coding!</p><p>Important<br>You will not be able to use Pygame from IDLE. You will need to run your programs from the command line like this:</p><pre><code>$ python3 mygame.py</code></pre><p>Some code editors will also let you run Python programs from inside the program, but we’ll talk about that in a separate post.</p><p>上面英文部分出自:<a href="http://kidscancode.org/blog/2015/09/pygame_install/">KidsCanCode</a></p><p>有几个坑:</p><hr><p>一开始,都是直接到<a href="http://www.pygame.org/download.shtml">pygame.org</a>下载,熟知…上面只有32位的pygame,结果导致import pygame错误:</p><pre><code>Traceback (most recent call last):File "<stdin>", line 1, in <module>File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/pygame/__init__.py", line 95, in <module>from pygame.base import *ImportError: dlopen(/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/pygame/base.so, 2): no suitable image found. Did find:/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/pygame/base.so: no matching architecture in universal wrapper</code></pre><p>stackoverflow上面有解答:<a href="http://stackoverflow.com/questions/8275808/installing-pygame-for-mac-os-x-10-6-8">Installing Pygame for Mac OS X 10.6.8</a></p><hr><p>如果你之前已经安装过homebrew,在安装sdl_image的时候遇到404,那么可能是你安装homebrew的镜像地址有问题, 你可以试试, 可能连git都装不了<br>方法:卸载homebrew,方法如下:</p><pre><code>$ cd `brew --prefix`$ rm -rf Cellar $ brew prune $ rm `git ls-files` $ rm -r Library/Homebrew Library/Aliases Library/Formula Library/Contributions $ rm -rf .git $ rm -rf ~/Library/Caches/Homebrew</code></pre><p>然后再按照上面的步骤重头开始吧~~~ </p><hr><p>安装完sdl之后缺link不上:</p><pre><code>Error: The \`brew link\` step did not complete successfullyThe formula built, but is not symlinked into /usr/localCould not symlink bin/sdl/usr/local/bin is not writable.You can try again using:brew link sdl</code></pre><p>方法:</p><p><strong>Multiuser Homebrew</strong></p><p>If you have multiple user accounts, and you want more than one of them to be able to use brew, you need to run through a few steps, otherwise you will constantly have to change ownership of the Homebrew file structure every time you switch users, and that’s not a great idea.</p><p>Detailed instructions can be found online, but the quick answer is this:</p><p>Create a group named brew:<br>Open System preferences<br>Click Accounts<br>Click the “+” (unlock first if necessary)<br>Under New account select Group<br>enter brew<br>Click Create Group<br>Select the brew group, and add the user accounts you want to use brew to it.<br>change the /usr/local folder group ownership:</p><pre><code>sudo chgrp -R brew /usr/local</code></pre><p>change the permissions to add write to /usr/local as group:</p><pre><code>sudo chmod -R g+w /usr/local</code></pre><p>change homebrew cache directory group: </p><pre><code>sudo chgrp -R brew /Library/Caches/Homebrew</code></pre><p>change the homebrew cache directory permissions: </p><pre><code>sudo chmod -R g+w /Library/Caches/Homebrew</code></pre><p><strong>Single User Homebrew</strong></p><p>If you’re not trying to use more than one user with Homebrew, then the solution provided by the other answers is probably sufficient:</p><pre><code>sudo chown -R $(whoami) /usr/localsudo chown -R $(whoami) /Library/Caches/Homebrew</code></pre><p>还遇到什么坑的话,给我留言,大家一起解决~</p>]]></content>
<summary type="html"><p>学完python之后就想玩些进阶的, 比如pygame.<br>但是在Mac OS X下安装pygame遇到了不少坑, 但最终还是顺利解决了, 也再一次深刻体会到google和百度的差距.</p>
<p><strong>Installing Pygame for OS X</strong></p>
<p>Note: For most of these instructions you will need to use the command line. Don’t be intimidated - programmers work on the command line all the time. Once you get used to it, you’ll find it’s the quickest and easiest way to do lots of useful things on the computer.</p>
<p>You access the command line by running the Terminal application - click on the Spotlight icon and type “terminal” to find it.</p></summary>
<category term="技术" scheme="https://sherlozk.github.io/categories/%E6%8A%80%E6%9C%AF/"/>
<category term="Python" scheme="https://sherlozk.github.io/tags/Python/"/>
</entry>
<entry>
<title>markdown-TOC</title>
<link href="https://sherlozk.github.io/2016/05/27/markdown-TOC/"/>
<id>https://sherlozk.github.io/2016/05/27/markdown-TOC/</id>
<published>2016-05-27T05:07:36.000Z</published>
<updated>2022-11-19T03:10:24.955Z</updated>
<content type="html"><![CDATA[<h1 id="一级"><a href="#一级" class="headerlink" title="一级"></a>一级</h1><h2 id="二级"><a href="#二级" class="headerlink" title="二级"></a>二级</h2><h3 id="三级"><a href="#三级" class="headerlink" title="三级"></a>三级</h3><p>今天写了一篇比较长的文章, 想用目录使结构清晰一点,但是却发现markdown对TOC的支持实在是……</p><p>我用的作业部落的<code>Cmd Markdown</code>编辑器,在这个编辑器里面是支持使用TOC的,直接使用命令<code>[TOC]</code>就好了.</p><p>然而发现…传到hexo之后,就给我显示[TOC]…(这TM就尴尬了…)</p><p>随后在去google,发现有些人的做法是插入JS代码实现TOC,但是没学过JS,给代码我都不知道放哪里…</p><span id="more"></span><p>后面终于看到一个Sublime Text的插件:<a href="https://packagecontrol.io/packages/MarkdownTOC">markdownTOC</a>. 在看了他们神器的gif图之后,果断去试了试,于是一步一步去弄…..看到后面一句,真是心都碎了….<br><img src="/2016/05/27/markdown-TOC/autolink.png" alt="autolink"></p><p><code>Auto link</code>只在Github能用.不服气的我还是没放弃,结果真不行,<code>markdownTOC</code>能做到的就只是把文章的标题以文本的形式归纳起来.</p><p>所以,开始改变思路, 能不能让hexo弄一个目录出来, 而不以来markdown. 结果是可行的.</p><p>具体的做法我就不做搬运工,搬来我自己这里了. 直接给连接:<a href="http://kuangqi.me/tricks/enable-table-of-contents-on-hexo/">为Hexo博客添加目录</a></p>]]></content>
<summary type="html"><h1 id="一级"><a href="#一级" class="headerlink" title="一级"></a>一级</h1><h2 id="二级"><a href="#二级" class="headerlink" title="二级"></a>二级</h2><h3 id="三级"><a href="#三级" class="headerlink" title="三级"></a>三级</h3><p>今天写了一篇比较长的文章, 想用目录使结构清晰一点,但是却发现markdown对TOC的支持实在是……</p>
<p>我用的作业部落的<code>Cmd Markdown</code>编辑器,在这个编辑器里面是支持使用TOC的,直接使用命令<code>[TOC]</code>就好了.</p>
<p>然而发现…传到hexo之后,就给我显示[TOC]…(这TM就尴尬了…)</p>
<p>随后在去google,发现有些人的做法是插入JS代码实现TOC,但是没学过JS,给代码我都不知道放哪里…</p></summary>
<category term="技术" scheme="https://sherlozk.github.io/categories/%E6%8A%80%E6%9C%AF/"/>
<category term="Markdown" scheme="https://sherlozk.github.io/tags/Markdown/"/>
</entry>
<entry>
<title>Python基础知识总结</title>
<link href="https://sherlozk.github.io/2016/05/26/python-base/"/>
<id>https://sherlozk.github.io/2016/05/26/python-base/</id>
<published>2016-05-26T12:25:43.000Z</published>
<updated>2022-11-19T03:11:01.595Z</updated>
<content type="html"><![CDATA[<p><img src="/2016/05/26/python-base/python_humor.png" alt="python"></p><span id="more"></span><h1 id="WHY-Python"><a href="#WHY-Python" class="headerlink" title="WHY Python?"></a>WHY Python?</h1><p>首先,学一门语言都会问:点解要学这门语言?<br>而学Python的原因很简单,原因就是…..好鬼简单.(这句话不是我说的)<br>很喜欢Python极简的代码风格,以及众多功能强大的模块……<br>学了两天Python有点点体会,觉得应该总结一下有哪些应该注意的地方.</p><h1 id="基本问题"><a href="#基本问题" class="headerlink" title="基本问题"></a>基本问题</h1><h2 id="学习途径"><a href="#学习途径" class="headerlink" title="学习途径"></a>学习途径</h2><p>初学者推荐一个公众号:<a href="http://crossincode.com/home/">Crossin的编程教室</a>(喜欢作者的教学方式)</p><h2 id="环境配置"><a href="#环境配置" class="headerlink" title="环境配置"></a>环境配置</h2><p>用Mac或者Linux的同学是幸福的,直接在终端输入<code>idle</code>就好了,这两个系统都是默认自带Python的,如果想直接在终端打开Python Shell,直接输入<code>python</code>就好了.windows下就有点麻烦,好吧,事实上麻烦到我不想去尝试配置了,详情见<code>Crossin的编程教室</code>第一课.</p><p>配置好之后就可以打出你的第一行代码啦!</p><blockquote><p>>>>print ‘Hello World!’</p></blockquote><h1 id="基本语法"><a href="#基本语法" class="headerlink" title="基本语法"></a>基本语法</h1><h2 id="print"><a href="#print" class="headerlink" title="print"></a>print</h2><p>这个函数的兼容性可以说极强</p><blockquote><p>>>> print “hello” #双引号可以<br>hello<br>>>> print ‘world’ #单引号可以<br>world<br>>>> print 1 #纯数字直接写<br>1<br>>>> print 3.14<br>3.14<br>>>> print 3e30<br>3e+30<br>>>> print 1 + 2 * 3 #支持表达式计算<br>7<br>>>> print(2 > 5) #支持真假判断<br>False</p></blockquote><p>有一点值得注意的是:<strong>2.0版本print和print()都是可以的,但是3.0之后print后面的表达式必须加括号.</strong></p><h2 id="input-和raw-input"><a href="#input-和raw-input" class="headerlink" title="input()和raw_input()"></a>input()和raw_input()</h2><p><code>input()</code> 接收的是一个值或变量,也就是说,你如果输 123,程序接收到的就是整数 123,你输 True,就是 bool 值 True。如果你输了 python,程序会认为这是一个叫做 python 的变量,而假如你没有定义过这个变量,就会报错。所以如果想通过input获得一段文字,<strong>输入的时候必须把文字用双引号或者单引号括起来.</strong></p><p><code>raw_input()</code>接收的是一个字符串,不管你输入什么都会当做一个字符串.</p><p>然后在3.0之后,将<code>input()</code>和<code>raw_input()</code>合并了,为了减少混乱吧…保留input关键字,但是功能确保留了raw_input的功能,就是说,在3.0之后,**你用input(),获得的就是一段字符串,无论你输入什么.**那么问题来了,我想要获得一个值或者一个变量肿么办?<code>value = eval(input())</code>即可.</p><h2 id="变量"><a href="#变量" class="headerlink" title="变量"></a>变量</h2><p>命名规则:</p><ul><li>第一个字符必须是字母或者下划线“_”</li><li>剩下的部分可以是字母、下划线“_”或数字(0-9)</li><li>变量名称是对大小写敏感的,myname和myName不是同一个变量。</li></ul><p>Python是动态变量类型的,定义变量的时候不需要指明这个变量是什么类型,编译器会根据赋给变量的值去判断类型:</p><blockquote><p>name = ‘Crossin’ #字符型(需要用’ ‘或者” “引起来)<br> myVar = 123 #整型<br> price = 5.99 #浮点型<br> visible = True #布尔型<br> a = 1 > 3 #通过逻辑表达式赋值</p></blockquote><h2 id="bool-‘False’"><a href="#bool-‘False’" class="headerlink" title="bool(‘False’)"></a>bool(‘False’)</h2><p>在python中,以下数值会被认为是False:</p><ul><li>为0的数字, 包括0,0.0</li><li>空字符串, 包括 ‘’ , “”</li><li>表示空值的None</li><li>空集合, 包括() , [] , {}</li></ul><p>其他的值都认为是True。<br>因为’False’是一个非空字符串,所以转成bool类型之后,就是True,所以bool(‘False’) = True,同理bool(‘ ‘) = True.</p><h2 id="格式化"><a href="#格式化" class="headerlink" title="格式化"></a>格式化</h2><p>Python不需要用分号来结束一行代码,取而代之的就是严格的缩进格式,刚刚开始就是过因为缩进格式不对,不断出现错误,具体你在敲一个回车的时候,下一行要缩进几格是可以设置的:Preferences->Indentation Width(这个是mac下的设置路径,windows下的名字也应该大同小异)</p><h2 id="import"><a href="#import" class="headerlink" title="import"></a>import</h2><pre><code>import random</code></pre><p>import语句告诉python,我们要用random模块中的内容。然后便可以使用random中的方法,比如:</p><pre><code>random.randint(1, 10)random.choice([1, 3, 5])</code></pre><p>注意,函数前面需要加上“random.”,这样python才知道你是要调用random中的方法。</p><p>想知道random有哪些函数和变量,可以用dir()方法:</p><pre><code>dir(random)</code></pre><p>如果你只是用到random中的某一个函数或变量,也可以通过from…import…指明:</p><pre><code>from math import piprint pi</code></pre><p>为了便于理解和避免冲突,你还可以给引入的方法换个名字:</p><pre><code>from math import pi as math_piprint math_pi</code></pre><p> </p><h2 id="读写文件"><a href="#读写文件" class="headerlink" title="读写文件"></a>读写文件</h2><p>读写文件有三种模式:</p><ul><li><code>r</code>:只读模式</li><li><code>w</code>:覆盖模式</li><li><code>a</code>:添加模式</li></ul><p>函数格式:<br> file(‘文件名’,’参数’)<br> open(‘文件名’,’参数’)</p><p>读文件命令很简单:</p><pre><code>file('文件名') #默认参数为r</code></pre><p>如果文件和代码放在同一个文件夹,直接写名字就好了,不是就要写文件的绝对路径.还有很神奇的一点就是,如果路径没有找到文件,就会自动创建这个文件.</p><h2 id="and-or"><a href="#and-or" class="headerlink" title="and-or"></a>and-or</h2><pre><code>c = bool and a or b </code></pre><p>通常情况下,只要bool值为真则a,为假则b<br>但如果当a本身就为假,比如0,’’.这时,几时bool为真也会取b,所以为了避免这种情况,应当将a->[a] , b->[b],将元素变成列表,即使是[‘’]或者[0]这是为真的,但是对应的,表达式返回的也是一个列表,去列表第一个元素即可,因此表达式也要相应改变一下:</p><pre><code>c = (bool and [a] or [b])[0]</code></pre><h2 id="随机数"><a href="#随机数" class="headerlink" title="随机数"></a>随机数</h2><p>首先,使用之前都要import random模块:</p><pre><code>import random</code></pre><p>调用时是调用类方法的的形式:</p><pre><code>num = random.randint(1,100)</code></pre><p>常用的方法;</p><pre><code>random.random() #生成一个0到1之间的随机浮点数,包括0但不包括1,也就是[0.0, 1.0)。random.uniform(a, b) #生成a、b之间的随机浮点数。不过与randint不同的是,a、b无需是整数,也不用考虑大小。random.choice(seq) #从序列中随机选取一个元素。seq需要是一个序列,比如list、元组、字符串。random.randrange(start, stop, step) #生成一个从start到stop(不包括stop),间隔为step的一个随机数。start、stop、step都要为整数,且start<stop。random.sample(population, k) #从population序列中,随机获取k个元素,生成一个新序列。sample不改变原来序列。random.shuffle(x) #把序列x中的元素顺序打乱。shuffle直接改变原有的序列。</code></pre><h2 id="列表解析"><a href="#列表解析" class="headerlink" title="列表解析"></a>列表解析</h2><p>Python的列表(list)就是数组,支持表达式赋值<br>比如从另一个list中去部分元素:</p><pre><code>ist_1 = [1, 2, 3, 5, 8, 13, 22]list_2 = [i for i in list_1 if i % 2 == 0] #从list_1中取出模2的数print list_2输出[2, 8, 22]</code></pre><h1 id="正则表达式"><a href="#正则表达式" class="headerlink" title="正则表达式"></a>正则表达式</h1><h2 id="常用特殊符号"><a href="#常用特殊符号" class="headerlink" title="常用特殊符号"></a>常用特殊符号</h2><p>可以看我之前写的一篇博文:<a href="http://sherlockz.github.io/2016/05/19/python-re-symbol/">python正则表达式的部分特殊符号</a></p><h2 id="值得注意的地方"><a href="#值得注意的地方" class="headerlink" title="值得注意的地方"></a>值得注意的地方</h2><p><code>Hi和hi</code>: 默认情况下,正则表达式是严格区分大小写的.<br><code>[0-9]</code>: [0123456789]可以写成[0-9]这种形式,同理,类似的还有[a-zA-Z].<br><code>()</code>: ()在正则表达式里也有着特殊的含义, 所以要匹配字符”(“, 需要用”\(“.</p><h2 id="懒惰匹配和贪婪匹配"><a href="#懒惰匹配和贪婪匹配" class="headerlink" title="懒惰匹配和贪婪匹配"></a>懒惰匹配和贪婪匹配</h2><pre><code>".*" 和 ".*?"</code></pre><p><code>“*”</code>在匹配时,会匹配尽可能长的结果。如果你想让他匹配到最短的就停止,需要用<code>“.*?”</code>。如<code>“I.*?e”</code>,就会得到第二种结果。这种匹配方式被称为懒惰匹配,而原本尽可能长的方式被称为贪婪匹配。</p><h1 id="函数的参数传递"><a href="#函数的参数传递" class="headerlink" title="函数的参数传递"></a>函数的参数传递</h1><p>基本方式:</p><p><code>fun1(a,b)</code> : 调用时提供参数的位置进行匹配,要求实参与行参的数量相等,默认按位置匹配参数。调用时,少参数或者多参数都会引起错误。</p><pre><code>def func(arg1, arg2): print arg1, arg2func(3, 7)</code></pre><p>Python 还提供了其他一些更灵活的参数传递方式,如:</p><p><code>func2(a=1, b=2, c=3)</code> : 有默认值,当没有提供足够的参数时, 会用默认值作为参数的值.提供的参数会按顺序先匹配前面位置的参数, 后面未匹配到的参数使用默认值.</p><pre><code>def func(arg1=1, arg2=2, arg3=3):print arg1, arg2, arg3func(2, 3, 4)func(5, 6)func(7) 输出为2 3 45 6 37 2 3</code></pre><p>当然,也可以直接用形参去指定某个参数,但是,<code>没有指定参数名的参数</code>必须在<code>所有</code> <code>指定参数名的参数</code>前面, 且<code>参数不能重复</code>.</p><pre><code>func(arg2=8)func(arg3=9, arg1=10)func(11, arg3=12)</code></pre><p><br><br><code>func3(*args)</code> : 这种方式的厉害之处在于, 它可以接受任意数量的参数.</p><pre><code>def calcSum(*args):sum = 0for i in args: sum += iprint sumcalcSum(1,2,3)calcSum(123,456)calcSum()输出:65790</code></pre><p>在变量前加上星号前缀(*),调用时的<code>参数会存储在一个 tuple(元组)对象</code>中,赋值给形参。在函数内部,需要对参数进行处理时,只要对这个 tuple 类型的形参(这里是 args)进行操作就可以了。因此,函数在定义时并不需要指明参数个数,就可以处理任意参数个数的情况。</p><p>不过有一点需要注意,tuple 是有序的,所以 args 中元素的顺序受到赋值时的影响。如:</p><pre><code>def printAll(*args): for i in args: print i, printprintAll(1,2,3)printAll(3,2,1)输出:1 2 33 2 1</code></pre><p>虽然3个参数在总体上是相同的,但由于调用的顺序不一样,结果也是不同的。</p><p><code>func4(**kargs)</code> : 既可以按参数名传递参数, 不受位置的限制, 又可以像 tuple 传递一样不受数量限制.因为func(**kargs) 则是把参数以键值对<code>字典</code>的形式传入。字典是无序的,所以在输出的时候,并不一定按照提供参数的顺序。同样在调用时,参数的顺序无所谓,只要对应合适的形参名就可以了。于是,采用这种参数传递的方法,可以不受参数数量、位置的限制。</p><pre><code>def printAll(**kargs): for k in kargs: print k, ':', kargs[k]printAll(a=1, b=2, c=3)printAll(x=4, y=5)输出:a : 1c : 3b : 2y : 5x : 4</code></pre><p>当然,Python能做到的不仅如此,以上几种方式不满意,你还可以混着用.</p><pre><code>def func(x, y=5, *a, **b): print x, y, a, bfunc(1)func(1,2)func(1,2,3)func(1,2,3,4)func(x=1)func(x=1,y=1)func(x=1,y=1,a=1)func(x=1,y=1,a=1,b=1)func(1,y=1)func(1,2,3,4,a=1)func(1,2,3,4,k=1,t=2,o=3)输出:1 5 () {}1 2 () {}1 2 (3,) {}1 2 (3, 4) {}1 5 () {}1 1 () {}1 1 () {'a': 1}1 1 () {'a': 1, 'b': 1}1 1 () {}1 2 (3, 4) {'a': 1}1 2 (3, 4) {'k': 1, 't': 2, 'o': 3}</code></pre><p>在混合使用时,首先要注意函数的写法,必须遵守:</p><ul><li>带有默认值的形参(arg=)须在无默认值的形参(arg)之后;</li><li>元组参数(*args)须在带有默认值的形参(arg=)之后;</li><li>字典参数(**kargs)须在元组参数(*args)之后。</li></ul><p>可以省略某种类型的参数,但仍需保证此顺序规则。调用时也需要遵守:</p><ul><li>指定参数名称的参数要在无指定参数名称的参数之后;</li><li>不可以重复传递,即按顺序提供某参数之后,又指定名称传递。</li></ul><p>而在函数被调用时,参数的传递过程为:</p><ol><li>按顺序把无指定参数的实参赋值给形参;</li><li>把指定参数名称(arg=v)的实参赋值给对应的形参;</li><li>将多余的无指定参数的实参打包成一个 tuple 传递给元组参数(*args);</li><li>将多余的指定参数名的实参打包成一个 dict 传递给字典参数(**kargs)。</li></ol><h1 id="lambda"><a href="#lambda" class="headerlink" title="lambda"></a>lambda</h1><p>lambda是一种匿名函数,而且是一个极度简单的单行函数.</p><p>语法格式:</p><pre><code>lambda 参数列表: 表达式</code></pre><p>例子:</p><pre><code>def sum(a, b, c): return a + b + c ->sum = lambda a, b, c: a + b + c </code></pre><h1 id="map-和reduce"><a href="#map-和reduce" class="headerlink" title="map()和reduce()"></a>map()和reduce()</h1><p>首先来说两个例子:</p><pre><code>1. 假设有一个数列,如何把其中每一个元素都翻倍?2. 假设有两个数列,如何求和?</code></pre><p>第一个问题:</p><pre><code>lst_1 = (1,2,3,4,5,6)lst_2 = map(lambda x: x * 2, lst_1)print lst_2</code></pre><p>第二个问题:</p><pre><code>print reduce((lambda x, y: x + y), xrange(1, 101))</code></pre><p>教程里是这么解释的:</p><pre><code>"map 可以看作是把一个序列根据某种规则,映射到另一个序列。reduce 做的事情就是把一个序列根据某种规则,归纳为一个输出。" </code></pre><p>先看看函数的调用格式:</p><pre><code>map: map(函数,序列) #第一个参数是一个函数, 之后的参数是序列, 可以是 list、tuple.reduce: reduce(函数,序列) #第一个参数也是一个函数, 之后的参数是序列, 可以是 list、tuple.</code></pre><p>**它们的的区别就在与函数参数的功能不同,map的参数函数必须是一个<code>一元操作函数</code>,reduce的参数函数必须是一个<code>二元操作函数</code>**所以通常map返回的是一个序列,二reduce返回的是一个运算结果.</p><p>所以,如果你要处理一个序列以获得新的序列,就用map; 如果想根据一个数列计算出某个结果,那就用reduce吧~~~</p><p>ps: Python3.0之后, reduce已经被移出内置函数, 使用 reduce 需要先通过 from functools import reduce 引入. </p><h1 id="range-和xrange"><a href="#range-和xrange" class="headerlink" title="range()和xrange()"></a>range()和xrange()</h1><p>上面的总结reduce的时候提到了xrange()这个函数,开始我也只是奇怪,并没有去纠结它和range()有什么不同,但是某一次我试着打印xrange(1,11),发现输出也是xrange(1,11):<br><img src="/2016/05/26/python-base/xrange.png" alt="xrange"></p><p>而打印range(1,11),输出的是一个列表:<br><img src="/2016/05/26/python-base/range.png" alt="range"></p><p>其实<code>range()</code>和<code>xrange()</code>的定义是一样的:</p><pre><code>range(start=0,stop,step)xrange(start=0,stop,step)</code></pre><ul><li>start表示列表开始的值,默认为“0”。</li><li>stop 表示列表结束的值,该参数不可缺少</li><li>参数step表示步长,默认值为“1”。<br><img src="/2016/05/26/python-base/contrast.png" alt="contrast"></li></ul><p>不同的是:range()返回一个递增或递减的数字列表, xrange 是一个类, 返回的是一个xrange对象. 所以打印出来才会是像xrange(1,11)这样的结果. </p><p>xrange()的优点在于:使用xrange()进行遍历, 每次遍历只返回一个值. range()返回的是一个列表, 一次性计算并返回所有的值. 因此, xrange()的执行效率要高于range().</p><h1 id="多线程"><a href="#多线程" class="headerlink" title="多线程"></a>多线程</h1><p>Python到处都体现着<code>简单</code>这两个字.<br>开辟多线程只需一行代码:</p><pre><code>start_new_thread(function, args[, kwargs])</code></pre><ul><li>function 是开发者定义的线程函数,</li><li>args 是传递给线程函数的参数,必须是tuple类型,</li><li>kwargs 是可选参数。</li></ul><p>不用手动管理线程, 通常情况, 线程在 function 执行完毕后结束.</p><p>然而Python的多线程并不完善,这里推荐两篇文章,有兴趣可以去研读一下:</p><p><a href="http://my.oschina.net/leejun2005/blog/179265#OSC_h3_1">理解 Python 中的多线程</a><br><a href="http://www.tuicool.com/articles/7zIra2r">Python 的 GIL 是什么鬼,多线程性能究竟如何</a></p>]]></content>
<summary type="html"><p><img src="/2016/05/26/python-base/python_humor.png" alt="python"></p></summary>
<category term="技术" scheme="https://sherlozk.github.io/categories/%E6%8A%80%E6%9C%AF/"/>
<category term="Python" scheme="https://sherlozk.github.io/tags/Python/"/>
</entry>
<entry>
<title>终端的常用命令</title>
<link href="https://sherlozk.github.io/2016/05/25/cmd/"/>
<id>https://sherlozk.github.io/2016/05/25/cmd/</id>
<published>2016-05-25T14:13:33.000Z</published>
<updated>2022-11-19T03:08:53.175Z</updated>
<content type="html"><![CDATA[<p><img src="/2016/05/25/cmd/cmdimg.jpg" alt="cmd"></p>]]></content>
<summary type="html"><p><img src="/2016/05/25/cmd/cmdimg.jpg" alt="cmd"></p>
</summary>
<category term="技术" scheme="https://sherlozk.github.io/categories/%E6%8A%80%E6%9C%AF/"/>
<category term="Linux" scheme="https://sherlozk.github.io/tags/Linux/"/>
</entry>
<entry>
<title>MySQL.apache2.tomcat自动启动设置方法</title>
<link href="https://sherlozk.github.io/2016/05/23/service/"/>
<id>https://sherlozk.github.io/2016/05/23/service/</id>
<published>2016-05-23T07:40:19.000Z</published>
<updated>2022-11-19T03:11:26.184Z</updated>
<content type="html"><![CDATA[<p>每次重启服务器都要重新启动三个服务:<code>mySQL</code> <code>apache2</code> <code>tomcat</code></p><p><code>mySQL</code>he <code>apache2</code>这两个服务可以直接用sysv-rc-donf<br>先安装:</p><blockquote><p>sudo apt-get install sysv-rc-conf</p></blockquote><blockquote><p>sysv-rc-conf</p></blockquote><span id="more"></span><p>如下图中找到<code>mySQL</code> <code>apache2</code>把后面的[ ]全部置空(通过空格键)<br><img src="/2016/05/23/service/src1.png" alt="src"><br><img src="/2016/05/23/service/src2.png" alt="src"></p><p><code>tomcat</code>服务并没有出现在服务列表里面,而一般的方法都是进入tomcat的bin文件夹里面开启startup.sh,所以可以把它添加到启动列表里面,方法也很简单:</p><blockquote><p>vim /etc/rc.local</p></blockquote><p>在最后的<code>exit 0</code>上面添加startup.sh的地址<br><img src="/2016/05/23/service/startup.png" alt="startup"></p><p>完成~~</p><p>现在来试试重启服务器,看看是不是真的可以开机启动这三个服务了.<br>直接输入服务器IP看看会不会出现默认页面(apache2服务):<br><img src="/2016/05/23/service/apache.png" alt="apache"><br>测试8080端口(tomcat服务):<br><img src="/2016/05/23/service/tomcat.png" alt="tomcat"><br>测试mySQL(我用了一个登录的例子来测试,方法不一,如果能登录成功,说明已经开启mySQL服务了):<br><img src="/2016/05/23/service/mysql.png" alt="mysql"></p>]]></content>
<summary type="html"><p>每次重启服务器都要重新启动三个服务:<code>mySQL</code> <code>apache2</code> <code>tomcat</code></p>
<p><code>mySQL</code>he <code>apache2</code>这两个服务可以直接用sysv-rc-donf<br>先安装:</p>
<blockquote>
<p>sudo apt-get install sysv-rc-conf</p>
</blockquote>
<blockquote>
<p>sysv-rc-conf</p>
</blockquote></summary>
<category term="技术" scheme="https://sherlozk.github.io/categories/%E6%8A%80%E6%9C%AF/"/>
<category term="Linux" scheme="https://sherlozk.github.io/tags/Linux/"/>
</entry>
<entry>
<title>Python正则表达式的部分特殊符号</title>
<link href="https://sherlozk.github.io/2016/05/19/python-re-symbol/"/>
<id>https://sherlozk.github.io/2016/05/19/python-re-symbol/</id>
<published>2016-05-19T11:44:20.000Z</published>
<updated>2022-11-19T03:11:07.413Z</updated>
<content type="html"><![CDATA[<p><code>\w</code> - 匹配字母或数字或下划线或汉字(3.x版本可以匹配汉字,但2.x版本不可以)<br><code>\s</code> - 匹配任意的空白符<br><code>\b</code> - 在正则表达式中表示单词的开头或结尾, 空格、标点、换行都算是单词的分割. 而“\b”自身又不会匹配任何字符, 它代表的只是一个位置.<br><code>\d</code> - 表示一个数字<br><code>^</code> - 匹配字符串的开始<br><code>$</code> - 匹配字符串的结束<br><code>[ ]</code> - 使用方括号,在正则表达式中, []表示满足括号中任一字符. 比如“[hi]”, 它就不是匹配“hi”了, 而是匹配“h”或者“i”.</p><span id="more"></span><p><code>\S</code>其实就是<code>\s</code>的反义,任意不是空白符的字符。同理,还有:</p><p><code>\W</code> - 匹配任意不是字母,数字,下划线,汉字的字符<br><code>\D</code> - 匹配任意非数字的字符<br><code>\B</code> - 匹配不是单词开头或结束的位置</p><p><code>[a]</code>的反义是<code>[^a]</code>,表示除a以外的任意字符。<code>[^abcd]</code>就是除abcd以外的任意字符。</p><p><code>*</code>、<code>+</code>、<code>{}</code>用来表示字符的重复。其他重复的方式还有:</p><p><code>*</code> - 表示任意数量连续字符,这种被称为通配符。<strong>但是在正则表达式中,’*’只表示数量,不表示字符.</strong><br><code>+</code> - 与’*’类似,表示1或更多的数量<br><code>?</code> - 表示任意一个字符, 重复零次或一次<br><code>{}</code> - 代替’+’,表示特定数量,限定长度<br> <code>{n,}</code> - 重复n次或更多次<br> <code>{n,m}</code> - 重复n到m次</p><p>正则表达式不只是用来从一大段文字中抓取信息,很多时候也被用来判断输入的文本是否符合规范,或进行分类。来点例子看看:<br><code>^\w{4,12}$</code><br>这个表示一段4到12位的字符,包括字母或数字或下划线或汉字,可以用来作为用户注册时检测用户名的规则。(但汉字在python2.x里面可能会有问题)</p><p><code>\d{15,18}</code><br>表示15到18位的数字,可以用来检测身份证号码</p><p><code>^1\d*x?</code><br>以1开头的一串数字,数字结尾有字母x,也可以没有。有的话就带上x。</p><p>另外再说一下之前提到的转义字符<code>\</code>。如果我们确实要匹配<code>.</code>或者<code>*</code>字符本身,而不是要它们所代表的元字符,那就需要用<code>\.</code>或<code>\*</code>。<code>\</code>本身也需要用<code>\\</code>。<br>比如<code>"\d+\.\d+"</code>可以匹配出123.456这样的结果。</p><p><code>\bs\S*?e\b</code>从下面一段文本中,匹配出所有s开头,e结尾的单词。</p><p><code>site sea sue sweet see case sse ssee loses</code></p><p><code>r</code>,是raw的意思,它表示对字符串不进行转义。为什么要加这个?你可以试试print <code>"\bhi"</code>和<code>r"\bhi"</code>的区别。</p><blockquote><p>>>> print “\bhi”<br>hi<br>>>> print r”\bhi”<br>\bhi</p></blockquote>]]></content>
<summary type="html"><p><code>\w</code> - 匹配字母或数字或下划线或汉字(3.x版本可以匹配汉字,但2.x版本不可以)<br><code>\s</code> - 匹配任意的空白符<br><code>\b</code> - 在正则表达式中表示单词的开头或结尾, 空格、标点、换行都算是单词的分割. 而“\b”自身又不会匹配任何字符, 它代表的只是一个位置.<br><code>\d</code> - 表示一个数字<br><code>^</code> - 匹配字符串的开始<br><code>$</code> - 匹配字符串的结束<br><code>[ ]</code> - 使用方括号,在正则表达式中, []表示满足括号中任一字符. 比如“[hi]”, 它就不是匹配“hi”了, 而是匹配“h”或者“i”.</p></summary>
<category term="技术" scheme="https://sherlozk.github.io/categories/%E6%8A%80%E6%9C%AF/"/>
<category term="Python" scheme="https://sherlozk.github.io/tags/Python/"/>
</entry>
</feed>