shengxinjing / fe-advanced-interview Goto Github PK
View Code? Open in Web Editor NEW前端进阶面试指南
Home Page: https://shengxinjing.cn/
License: MIT License
前端进阶面试指南
Home Page: https://shengxinjing.cn/
License: MIT License
这两天微博上看到左耳朵耗子吐槽了一下node社区的left-pad的代码,原po链接
我也思考了一下 怎么用实现一个left-pad比较合适,上图代码确实比较搓
leftpad功能,就是字符串前面拼指定字符到固定长度,比如
leftpad('hello',20,'1'),就要返回'111111111111111hello'
function leftpad(str, len, ch) {
if (!ch && ch !== 0) ch = ' ';
var len = len - str.length;
return Array(len).join(ch) + str;
}
function leftpad(str, len, ch) {
if (!ch && ch !== 0) ch = ' ';
var len = len - str.length;
return Array.prototype.join.call({
length:len
},ch)+str;
}
如果把Array.prototype.join缓存到外部变量里,多次使用速度更快
var _join = Array.prototype.join
function leftpad(str, len, ch) {
if (!ch && ch !== 0) ch = ' ';
var len = len - str.length;
return _join.call({
length:len
},ch)+str;
}
上面复杂度都是O(N)的,既然核心思路是把字符串重复n次,可以用二分法,比如把s,重复20次,拼在str前面,大概过程如下
total = ''
ch = 's'
20是偶数
ch变成ss
长度变成10
10是偶数
ch变成ssss
长度变成5
5是奇数
total += ch(total变成ssss)
ch变成ssssssss(8个)
长度变成2
2是偶数
ch继续变成(ssssssssssssssss)(16个s)
长度变成1
total= total+ch(4个加16个)
结束代码 拼接str 返回
代码如下
function leftpad(str, len, ch) {
if (!ch && ch !== 0) ch = ' ';
var len = len - str.length,
total = ''
while (true) {
// 如果len是基数,total上就加一个ch
if (len % 2 == 1) total += ch;
if (len == 1) return total + str;;
// 每次ch都变成chch
ch += ch;
//长度减半
len = parseInt(len / 2);
}
}
最后写完这些,看了耗子大神微博贴的代码,突然想起求余和除以二取整,可以用 按位与len&1 和右移len>>1代替,囧,还是代码写的太少,没想到
function leftpad(str, len, ch) {
if (!ch && ch !== 0) ch = ' ';
var len = len - str.length,
total = ''
while (true) {
// 如果len是基数,total上就加一个ch
if (len & 1 == 1) total += ch;
if (len == 1) return total + str;;
// 每次ch都变成chch
ch += ch;
//长度减半
len = len>>1;
}
}
最后再优化一下循环
function leftpad(str, len, ch) {
if (!ch && ch !== 0) ch = ' ';
var len = len - str.length,
total = ''
while (len) {
// 如果len是基数,total上就加一个ch
(len & 1) && (total += ch)
// 每次ch都变成chch
ch += ch;
//长度减半
len = len >> 1;
}
return total + str
}
大家可以尝试用python实现一下(不要用自带的rjust),本文仅提供一个思路,很小的一个功能函数,可能还会有很多更好的优化和实现,欢迎大家多指教写代码过程中还是要多思考,共勉
喜欢请star
资深程序员,擅长javascript,python
http://shengxinjing.cn/blockchain/
资深程序员,擅长javascript,python
http://shengxinjing.cn/fe/interview.html#d7bf
资深程序员,擅长javascript,python
今天群里看到有人问关于python多线程写文件的问题,联想到这是reboot的架构师班的入学题,我想了一下,感觉坑和考察的点还挺多,可以当成一个面试题来问,简单说一下我的想法和思路吧,涉及的代码和注释在github 跪求star
本文需要一定的python基础,希望大家对下面几个知识点有所了解
python文件处理,open write
简单了解http协议头信息
os,sys模块
threading模块多线程
requests模块发请求
题目既然是多线程下载,首先要解决的就是下载问题,为了方便测试,我们先不用QQ安装包这么大的,直接用pc大大英明神武又很内涵的头像举例,大概是这个样子(http://51reboot.com/blogimg/pc.jpg)
python的requests模块很好的封装了http请求,我们选择用它来发送http的get请求,然后写入本地文件即可(关于requests和http,以及python处理文件的具体介绍,可以百度或者持续关注,后面我会写),思路既然清楚了,代码就呼之欲出了
# 简单粗暴的下载
import requests
res=requests.get('http://51reboot.com/blogimg/pc.jpg')
with open('pc.jpg','w') as f:
f.write(res.content)
运行完上面的代码,文件夹下面多了个pc.jpg 就是你想要的图片了
上面代码功能太少了,注意 ,我们的要求是多线程下载,这种简单粗暴的下载完全不符合要求,所谓多线程,你可以理解为仓库里有很多很多袋奥利奥饼干,老板让我去都搬到公司来放好,而且要按照原顺序放好
上面的代码,大概就是我一个人去仓库,把所有奥利奥一次性拿回来,大概流程如下(图不清戳大)
我们如果要完成题目多线程的要求,首先就要把任务拆解,拆成几个子任务,子任务之间可以并行执行,并且执行结果可以汇总成最终结果
为了完成这个任务,我们首先要知道数据到底有多大,然后把数据分块去取就OK啦,我们要对http协议有一个很好的了解
思路清晰了,代码也就呼之欲出了,我们先测试一下range头信息
http头信息中的Range信息,用于请求头中,指定第一个字节的位置和最后一个字节的位置,如1-12,如果省略第二个数,就认为取到最后,比如36-
# range测试代码
import requests
# http头信息,指定获取前15000个字节
headers={'Range':'Bytes=0-15000','Accept-Encoding':'*'}
res=requests.get('http://51reboot.com/blogimg/pc.jpg',headers=headers)
with open('pc.jpg','w') as f:
f.write(res.content)
我们得到了头像的前15000个字节,如下图,目测range是对的
继续丰富我们的代码
import requests
# 下载器的类
class downloader:
# 构造函数
def __init__(self):
# 要下载的数据连接
self.url='http://51reboot.com/blogimg/pc.jpg'
# 要开的线程数
self.num=8
# 存储文件的名字,从url最后面取
self.name=self.url.split('/')[-1]
# head方法去请求url
r = requests.head(self.url)
# headers中取出数据的长度
self.total = int(r.headers['Content-Length'])
print type('total is %s' % (self.total))
def get_range(self):
ranges=[]
# 比如total是50,线程数是4个。offset就是12
offset = int(self.total/self.num)
for i in range(self.num):
if i==self.num-1:
# 最后一个线程,不指定结束位置,取到最后
ranges.append((i*offset,''))
else:
# 每个线程取得区间
ranges.append((i*offset,(i+1)*offset))
# range大概是[(0,12),(12,24),(25,36),(36,'')]
return ranges
def run(self):
f = open(self.name,'w')
for ran in self.get_range():
# 拼出Range参数 获取分片数据
r = requests.get(self.url,headers={'Range':'Bytes=%s-%s' % ran,'Accept-Encoding':'*'})
# seek到相应位置
f.seek(ran[0])
# 写数据
f.write(r.content)
f.close()
if __name__=='__main__':
down = downloader()
down.run()
多线程和多进程是啥在这就不多说了,要说明白还得专门写个文章,大家知道threading模块是专门解决多线程的问题就OK了,大概的使用方法如下,更详细的请百度或者关注后续文章
import requests
import threading
class downloader:
def __init__(self):
self.url='http://51reboot.com/blogimg/pc.jpg'
self.num=8
self.name=self.url.split('/')[-1]
r = requests.head(self.url)
self.total = int(r.headers['Content-Length'])
print 'total is %s' % (self.total)
def get_range(self):
ranges=[]
offset = int(self.total/self.num)
for i in range(self.num):
if i==self.num-1:
ranges.append((i*offset,''))
else:
ranges.append((i*offset,(i+1)*offset))
return ranges
def download(self,start,end):
headers={'Range':'Bytes=%s-%s' % (start,end),'Accept-Encoding':'*'}
res = requests.get(self.url,headers=headers)
print '%s:%s download success'%(start,end)
self.fd.seek(start)
self.fd.write(res.content)
def run(self):
self.fd = open(self.name,'w')
thread_list = []
n = 0
for ran in self.get_range():
start,end = ran
print 'thread %d start:%s,end:%s'%(n,start,end)
n+=1
thread = threading.Thread(target=self.download,args=(start,end))
thread.start()
thread_list.append(thread)
for i in thread_list:
i.join()
print 'download %s load success'%(self.name)
self.fd.close()
if __name__=='__main__':
down = downloader()
down.run()
执行python downloader效果如下
total is 21520
thread 0 start:0,end:2690
thread 1 start:2690,end:5380
thread 2 start:5380,end:8070
thread 3 start:8070,end:10760
thread 4 start:10760,end:13450
thread 5 start:13450,end:16140
thread 6 start:16140,end:18830
thread 7 start:18830,end:
0:2690 is end
2690:5380 is end
13450:16140 is end
10760:13450 is end
5380:8070 is end
8070:10760 is end
18830: is end
16140:18830 is end
download pc.jpg load success
run函数做了修改,加了多线程的东西,加了一个download函数专门用来下载数据块,这俩函数详细解释如下
def download(self,start,end):
#拼接Range字段,accept字段支持所有编码
headers={'Range':'Bytes=%s-%s' % (start,end),'Accept-Encoding':'*'}
res = requests.get(self.url,headers=headers)
print '%s:%s download success'%(start,end)
#seek到start位置
self.fd.seek(start)
self.fd.write(res.content)
def run(self):
# 保存文件打开对象
self.fd = open(self.name,'w')
thread_list = []
#一个数字,用来标记打印每个线程
n = 0
for ran in self.get_range():
start,end = ran
#打印信息
print 'thread %d start:%s,end:%s'%(n,start,end)
n+=1
#创建线程 传参,处理函数为download
thread = threading.Thread(target=self.download,args=(start,end))
#启动
thread.start()
thread_list.append(thread)
for i in thread_list:
# 设置等待
i.join()
print 'download %s load success'%(self.name)
#关闭文件
self.fd.close()
持续可以优化的点
大概就是这样了,我也是正在学习python,文章代表我个人看法,有错误不可避免,欢迎大家指正,共同学习,本文完整代码在github,跪求大家star
刚才看到一个issue,感觉挺逗的,决定Reboot其实也可以搞一个类似的试试,生成文案用,PC语录1,欢迎大家补充
def pc_say(name,bad):
return '''
不是很推荐%s这种比较奇葩的。因为%s的能力不是很好。虽然它后来又有一些改进方案来缓解这个问题,但注意,只是缓解。
''' %(name,bad)
print pc_say('mongo','写入')
print pc_say('js','兼容')
不是很推荐mongo这种比较奇葩的。因为写入的能力不是很好。虽然它后来又有一些改进方案来缓解这个问题,但注意,只是缓解。
不是很推荐js这种比较奇葩的。因为兼容的能力不是很好。虽然它后来又有一些改进方案来缓解这个问题,但注意,只是缓解。
http://shengxinjing.cn/fe/interview.html#23b9
资深程序员,擅长javascript,python
资深程序员,擅长javascript,python
资深程序员,擅长javascript,python
本文需要有一定的python和前端基础,如果没基础的,请关注我后续的基础教程系列博客:snail: :mushroom:
项目地址,可以看到具体的代码,喜欢请加个星星
录制中间网出问题了,重启了一下,所以有两部分 可以听到哥有磁性的声音
本文的目的在于,尽可能用简单的代码,让大家了解内存监控的原理
主题思路
其实所有的监控项,包括内存数据,都是从文件中读取的,大家执行以下 cat /proc/meminfo就可以看到关于内存的信息,我们关注的是前四行,总内存,空闲内存,缓冲和缓存大小
计算内存占用量公式:
(总内存-空闲内存-缓冲-缓存)/1024Mb
代码呼之欲出 monitor.py
用with打开文件,可以自动关闭,比直接open优雅那么一丢丢
def getMem():
with open('/proc/meminfo') as f:
total = int(f.readline().split()[1])
free = int(f.readline().split()[1])
buffers = int(f.readline().split()[1])
cache = int(f.readline().split()[1])
mem_use = total-free-buffers-cache
print mem_use/1024
while True:
time.sleep(1)
getMem()
执行文件 python monitor.py,每一秒打印一条内存信息
[woniu@teach memory]$ python mointor.py
2920
2919
2919
2919
2919
我们可以写个很搓的测试代码,占用一点内存,看看数据会不会变
执行下面代码,能看到内存使用量明显多了几M
# test.py
s = 'akdsakjhdjkashdjkhasjkdhasjkdhkjashdaskjhfoopnnm,ioqouiew'*100000
for i in s:
for j in s:
s.count(j)
获取内存数据done!
新建表格,我们需要两个字段,内存和时间 sql呼之欲出,简单粗暴
create memory(memory int,time int)
我们的 monitor.py就不能只打印内存信息了,要存储数据库啦,引入mysql模块,代码如下
import time
import MySQLdb as mysql
db = mysql.connect(user="reboot",passwd="reboot123",db="memory",host="localhost")
db.autocommit(True)
cur = db.cursor()
def getMem():
with open('/proc/meminfo') as f:
total = int(f.readline().split()[1])
free = int(f.readline().split()[1])
buffers = int(f.readline().split()[1])
cache = int(f.readline().split()[1])
mem_use = total-free-buffers-cache
t = int(time.time())
sql = 'insert into memory (memory,time) value (%s,%s)'%(mem_use/1024,t)
cur.execute(sql)
print mem_use/1024
#print 'ok'
while True:
time.sleep(1)
getMem()
比之前的多了拼接sql和执行的步骤,具体过程见视频,大家到数据库里执行一下下面的sql,就能看到我们辛辛苦苦获取的内存数据啦
select * from memory
我们的数据库里数据越来越多,怎么展示呢
我们需要flask
我们看下文件结构
.
├── flask_web.py web后端代码
├── mointor.py 监控数据获取
├── static 静态文件,第三方图表库
│ ├── exporting.js
│ ├── highstock.js
│ └── jquery.js
├── templates
│ └── index.html 展示前端页面
└── test.py 占用内存的测试代码
flask_web就是我们的web服务代码,template下面的html,就是前端展示的文件,static下面是第三方库
flask_web的代码如下
from flask import Flask,render_template,request
import MySQLdb as mysql
con = mysql.connect(user='reboot',passwd='reboot123',host='localhost',db='memory')
con.autocommit(True)
cur = con.cursor()
app = Flask(__name__)
import json
@app.route('/')
def index():
return render_template('index.html')
@app.route('/data')
def data():
sql = 'select * from memory'
cur.execute(sql)
arr = []
for i in cur.fetchall():
arr.append([i[1]*1000,i[0]])
return json.dumps(arr)
if __name__=='__main__':
app.run(host='0.0.0.0',port=9092,debug=True)
前端index.html
highstock的demo页面,copy过来,具体过程见视频
<html>
<head>
<title>51reboot</title>
</head>
<body>
hello world
<div id="container" style="height: 400px; min-width: 310px"></div>
<script src='/static/jquery.js'></script>
<script src='/static/highstock.js'></script>
<script src='/static/exporting.js'></script>
<script>
$(function () {
// 使用当前时区,否则东八区会差八个小时
Highcharts.setOptions({
global: {
useUTC: false
}
});
$.getJSON('/data', function (data) {
// Create the chart
$('#container').highcharts('StockChart', {
rangeSelector : {
selected : 1
},
title : {
text : '内存数据'
},
series : [{
name : '本机内存',
data : data,
tooltip: {
valueDecimals: 2
}
}]
});
});
});
</script>
</body>
</html>
具体观察数据结构的过程,见视频和demo链接,我们做的 就是把数据库里的数据,拼接成前端画图需要的数据,展现出来
这时候前端就能看到图表啦
python
tmp_time = 0
@app.route('/data')
def data():
global tmp_time
if tmp_time>0:
sql = 'select * from memory where time>%s' % (tmp_time/1000)
else:
sql = 'select * from memory'
cur.execute(sql)
arr = []
for i in cur.fetchall():
arr.append([i[1]*1000,i[0]])
if len(arr)>0:
tmp_time = arr[-1][0]
return json.dumps(arr)
前端,3秒查一次增量数据
$.getJSON('/data', function (data) {
// Create the chart
$('#container').highcharts('StockChart', {
chart:{
events:{
load:function(){
var series = this.series[0]
setInterval(function(){
$.getJSON('/data',function(res){
$.each(res,function(i,v){
series.addPoint(v)
})
})
},3000)
}
}
},
rangeSelector : {
selected : 1
},
title : {
text : 'AAPL Stock Price'
},
series : [{
name : 'AAPL',
data : data,
tooltip: {
valueDecimals: 2
}
}]
});
});
done!两个文件都搞定,double kill!
效果
代码没有特别注意细节,希望大家喜欢
如果您觉得有我写的东西对你帮助,可以打赏点钱给我支付宝支持我,谢谢
运维人员都不喜欢搞CMDB,因为有很多前端的内容,但CMDB却在运维圈占有重要的地位,开发CMDB就是各种增删改查,之后我有个想法,做一个写配置文件就自动生成页面的CMDB, 请支持我的woniu-cmdb,喜欢请star
此项目不仅限于cmdb,各种管理系统,都可以用此项目配置,改成学生老师啥的,就变成了学校内部的mis系统,我会一直维护这个项目,大家有新需求请提issue
命令只有两个
python woniu-build.py init # 初始化数据库+根据配置生成文件
python woniu-build.py 仅根绝配置生成文件
db_config = {
'host':'localhost',
'user':'root',
'passwd':"",
'db':'cmdb'
}
page_config = {
# menu是一个list,包含所有的页面信息
"menu":[{
//页面的名字,和数据库表一致
"name": 'user',
// 显示的页面标题
"title": '用户管理',
# 页面里具体的字段,如果有两个字段,配置两个即可,包含name和title
"data": [{
"name": 'username',
"title": '用户名'
},{
"name":'password',
"title":'密码'
}]
}}]
}
menu:下面具体介绍,页面具体的字段
favicon:页面标签的小logo 默认用reboot的
title:页面标签的标题,默认是woniu-cmdb
brand_name:项目左上角显示文字,默认是woniu-cmdb
{
name:名字和数据库表名一直
titile:中文
modal_detail:是否用模态窗展示详情(有隐藏字段没展示)
具体字段数据
data:[
{
name:
title:
type:类型,默认input text
value:select直接从value里渲染,不发ajax和preload,如果没有type,就是input里的value属性
select_type:获取数据的action_type的值,和对应的name字段一致
toname:preload数据里,完成id到name得转换显示,select默认true
hide:默认false,true的话隐藏此字段
option_val list的显示字段 默认id
option_name list的显示字段 默认name
}
]
}
todolist:
本项目python依赖flask和mysqldb模块,直接pip安装一下即可
如果您觉得有我写的东西对你帮助,可以打赏点钱给我支付宝支付宝[email protected]或者扫二维码
资深程序员,擅长javascript,python
本文需要有一定的python和前端基础,如果没基础的,请关注我后续的基础教程系列博客
本文所有的demo,都是浏览器下展示的
同步发布在博客
项目地址,跪求右上角star ⭐ 🌟 🌠
基于python,前端基于echarts,力求用简单的代码说明原理
61.159.140.123 - - [23/Aug/2014:00:01:42 +0800] "GET /favicon.ico HTTP/1.1" 404 \ "-" "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36 LBBROWSER" "-"
61.159.140.123 - - [23/Aug/2014:00:01:42 +0800] "GET /favicon.ico HTTP/1.1" 404 \ "-" "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36 LBBROWSER" "-"
61.159.140.123 - - [23/Aug/2014:00:01:42 +0800] "GET /favicon.ico HTTP/1.1" 404 \ "-" "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36 LBBROWSER" "-"
61.159.140.123 - - [23/Aug/2014:00:01:42 +0800] "GET /favicon.ico HTTP/1.1" 404 \ "-" "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36 LBBROWSER" "-"
66.249.64.5 - - [23/Aug/2014:00:02:16 +0800] "GET /data/uploads/2013/0519/09/small_51982ba18e012.jpg HTTP/1.1" 200 \ "-" "Googlebot-Image/1.0" "-"
66.249.64.10 - - [23/Aug/2014:00:02:54 +0800] "GET /data/uploads/2013/0319/08/middle_5147b116e93b4.jpg HTTP/1.1" 200 \ "-" "Googlebot-Image/1.0" "-"
# coding=utf-8
f = open('www_access_20140823.log')
res = {}
for l in f:
arr = l.split(' ')
# 获取ip url 和status
ip = arr[0]
url = arr[6]
status = arr[8]
# ip url 和status当key,每次统计+1
res[(ip,url,status)] = res.get((ip,url,status),0)+1
# 生成一个临时的list
res_list = [(k[0],k[1],k[2],v) for k,v in res.items()]
# 按照统计数量排序,打印前10
for k in sorted(res_list,key=lambda x:x[3],reverse=True)[:10]:
print k
('222.86.153.12', '/images/cursor_minify.cur', '404', 60)
('222.86.153.12', '/images/cursor_zoom.cur', '404', 32)
('58.253.6.133', '/images/cursor_minify.cur', '404', 32)
('111.85.34.165', '/%3Ca%20href=', '404', 28)
('58.253.6.133', '/images/cursor_zoom.cur', '404', 27)
('218.29.111.117', '/images/cursor_zoom.cur', '404', 27)
('218.29.111.117', '/images/cursor_minify.cur', '404', 26)
('117.63.146.40', '/public/js/common.js?20110824', '200', 19)
('117.63.146.40', '/favicon.ico', '404', 18)
('117.63.146.40', '/public/js/weibo.js?20110824', '200', 16)
生成list之后,拼接sql,存入数据库
import MySQLdb as mysql
con = mysql.connect(user='root',\
passwd='',\
db='log',\
host='localhost')
con.autocommit(True)
cur = con.cursor()
# 处理文件省略
for s in res_list:
sql = 'insert log values ("%s","%s",%s,%s)' % s
try:
# 入库
cur.execute(sql)
except Exception, e:
pass
from flask import Flask,request,render_template
app = Flask(__name__)
import MySQLdb as mysql
con = mysql.connect(user='xx',\
passwd='xx',\
db='xx')
cur = con.cursor()
@app.route('/')
def index():
table = '<table border="1">'
cur.execute('select * from log order by value desc limit 20; ')
for c in cur.fetchall():
table += '<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>'%c
table +='</table>'
return table
if __name__ == '__main__':
app.run(host='0.0.0.0',port=9092)
select status,sum(value) from log group by status
+--------+------------+
| status | sum(value) |
+--------+------------+
| 200 | 15529 |
| 206 | 6 |
| 301 | 2 |
| 304 | 3549 |
| 403 | 1 |
| 404 | 847 |
+--------+------------+
6 rows in set (0.02 sec)
import urllib2
import json
key = 'q5mTrTGzCSVq5QmGpI9y18Bo'
ipurl = 'http://api.map.baidu.com/location/ip?ak='+key+'&coor=bd09ll&ip='
sqlarr = []
def getGeo(ip):
try:
u = urllib2.urlopen(ipurl+ip)
page = json.load(u)
if 'content' in page:
point = page['content'].get('point')
print 'ip %s has geoX %s and geoY %s' % (ip,point['x'],point['y'])
except:
print 'error'
getGeo('202.198.16.3')
# ip 202.198.16.3 has geoX 125.31364243 and geoY 43.89833761
如果您觉得有我写的东西对你帮助,可以打赏点钱给我支付宝支持我,谢谢
http://shengxinjing.cn/vue/vuex.html
资深程序员,擅长javascript,python
开发时间很短,如有bug 请轻喷,有需求提issue,我会维护这个项目
├── README.md 说明文档
├── javascript 网页版本
│ └── index.html
└── python python版本
├── build.py 编译生成文件
└── config.py 你需要修改的配置文件
本文集中了我的前两篇文章日志可视化和配置文件生成cmdb,最终做出来的,分为网页和python两个版本,喜欢的跪求star
config={
'title':'去过的地方',
'subtitle':'北京 昆明 西北 呼和浩特',
'foot':[
'北京 昆明 丽江 香格里拉 丽江 昆明 北京',
'霍营地铁站 布达拉宫',
'北京 北戴河 北京',
'北京 兰州 敦煌 张掖 祁连 西宁 青海湖 茶卡盐湖 西宁 银川 呼和浩特 北京'
]
}
config={
'title':'去过的地方',
'subtitle':'北京 昆明 西北 呼和浩特',
'color':True,
'foot':[
'北京 昆明 丽江 香格里拉 丽江 昆明 北京',
'霍营地铁站 布达拉宫',
'北京 北戴河 北京',
'北京 兰州 敦煌 张掖 祁连 西宁 青海湖 茶卡盐湖 西宁 银川 呼和浩特 北京'
]
}
config={
'title':'北京去过的地方',
'subtitle':'走啊走',
'color':True,
'region':'北京',
'foot':[
'北京交通大学 霍营地铁站 古北水镇',
'北京交通大学 八达岭 北京交通大学',
'北京交通大学 妙峰山 潭柘寺'
]
}
如果您觉得有我写的东西对你帮助,可以打赏点钱给我支付宝支付宝[email protected]或者扫二维码(10元以下),凑钱买圣诞礼物 ~~~
http://shengxinjing.cn/vue/router.html#06e0
资深程序员,擅长javascript,python
http://shengxinjing.cn/vue/communicate.html
资深程序员,擅长javascript,python
可以再github网站上直接点击,把代码添加的桌面软件中
也可以再左上角添加项目,比如actual_07_homework,如果没有,说明还没有权限,联系我开通
项目初始化之后,写代码,修改文件之后,窗口内会看到修改的内容
输入提交的信息,点击commit
commit完之后,代码改动保存在了本地,点击一下右上角的sync 即可同步到github上,如果别人对代码有修改,你点一下sync,就可以把最新的代码同步到本地
如果您觉得有我写的东西对你帮助,可以打赏点钱给我支付宝支持我,谢谢
blog
博客范围:web开发 算法 运维开发
写了两篇觉得还不错,希望能坚持下去
http://shengxinjing.cn/fe/qa.html#43b6
资深程序员,擅长javascript,python
这篇文章最初是因为reboot的群里,有人去面试,笔试题有这个题,不知道怎么做,什么思路,就发群里大家讨论
我想了一下,简单说一下我的想法吧,当然,也有很好用的pyinotify模块专门监听文件变化,不过我更想介绍的,是解决的思路,毕竟作为面试官,还是想看到一下解决问题的思路,而且我觉得这一题的难点不在于监控文件增量,而在于怎么打印最后面10行
希望大家读这篇文章前,对python基础、处理文件和常用模块有一个简单的了解,知道下面几个名词是啥
open('a.txt')
file.seek
file.tell
time.sleep()
下面思路限于我个人知识,免不了有错误和考虑不周的,希望大家有更好的方法提出来,我随时优化代码,题目的感觉没啥太多的坑,下面让天真烂漫的蜗牛教大家用python实现
其实思路也不难啦
思路如下:
代码呼之欲出
with open('test.txt') as f:
f.seek(0,2)
while True:
last_pos = f.tell()
line = f.readline()
if line:
print line
效果图如下
代码说明
优化点
实例代码如下
# coding=utf-8
import sys
import time
class Tail():
def __init__(self,file_name,callback=sys.stdout.write):
self.file_name = file_name
self.callback = callback
def follow(self):
try:
with open(self.file_name) as f:
f.seek(0,2)
while True:
line = f.readline()
if line:
self.callback(line)
time.sleep(1)
except Exception,e:
print '打开文件失败,囧,看看文件是不是不存在,或者权限有问题'
print e
使用方法:
# 使用默认的sys.stdout.write打印到屏幕
py_tail = Tail('test.txt')
py_tail.follow()
# 自己定义处理函数
def test_tail(line):
print 'xx'+line+'xx'
py_tail1 = Tail('test.txt', test_tail)
py_tail1.follow()
咦 等等,tail -f 默认还会打印最后10行,这个好像才是这个题目的难点所在,众所周知,python里读文件指针,只能移动到固定位置,不能判断是哪一行,囧,先实现简单的,逐渐加强吧
现在这个代码,大概能拿6分啦,我们还有一个功能没做,那就是打印最后n行,默认是10行,现在把这个功能加上,加一个函数就行啦
我们知道,readlines可以获取所有内容,并且分行,代码呼之欲出,获取list最后10行很简单有么有,切片妥妥的
# 演示代码,说明核心逻辑,完整代码在下面
last_lines = f.readlines()[-10:]
for line in last_lines:
self.callback(line)
此时代码变成这样了
import sys
import time
class Tail():
def __init__(self,file_name,callback=sys.stdout.write):
self.file_name = file_name
self.callback = callback
def follow(self,n=10):
try:
with open(self.file_name) as f:
self._file = f
self.showLastLine(n)
self._file.seek(0,2)
while True:
line = self._file.readline()
if line:
self.callback(line)
except Exception,e:
print '打开文件失败,囧,看看文件是不是不存在,或者权限有问题'
print e
def showLastLine(self, n):
last_lines = self._file.readlines()[-n:]
for line in last_lines:
self.callback(line)
但是如果文件特别大呢,特别是日志文件,很容易几个G,我们只需要最后几行,全部读出来内存受不了,所以我们要继续优化showLastLine函数,我觉得这才是这题的难点所在
大概的思路如下
逻辑清晰以后,代码就呼之欲出啦
# coding=utf-8
import sys
import time
class Tail():
def __init__(self,file_name,callback=sys.stdout.write):
self.file_name = file_name
self.callback = callback
def follow(self,n=10):
try:
with open(self.file_name) as f:
self._file = f
self._file.seek(0,2)
self.file_length = self._file.tell()
self.showLastLine(n)
while True:
line = self._file.readline()
if line:
self.callback(line)
time.sleep(1)
except Exception,e:
print '打开文件失败,囧,看看文件是不是不存在,或者权限有问题'
print e
def showLastLine(self, n):
len_line = 100
read_len = len_line*n
while True:
if read_len>self.file_length:
self._file.seek(0)
last_lines = self._file.read().split('\n')[-n:]
break
self._file.seek(-read_len, 2)
last_words = self._file.read(read_len)
count = last_words.count('\n')
if count>=n:
last_lines = last_words.split('\n')[-n:]
break
else:
if count==0:
len_perline = read_len
else:
len_perline = read_len/count
read_len = len_perline * n
for line in last_lines:
self.callback(line+'\n')
if __name__ == '__main__':
py_tail = Tail('test.txt')
py_tail.follow()
加上注释的版本
# coding=utf-8
import sys
import time
class Tail():
def __init__(self,file_name,callback=sys.stdout.write):
self.file_name = file_name
self.callback = callback
def follow(self,n=10):
try:
# 打开文件
with open(self.file_name) as f:
self._file = f
self._file.seek(0,2)
# 存储文件的字符长度
self.file_length = self._file.tell()
# 打印最后10行
self.showLastLine(n)
# 持续读文件 打印增量
while True:
line = self._file.readline()
if line:
self.callback(line)
time.sleep(1)
except Exception,e:
print '打开文件失败,囧,看看文件是不是不存在,或者权限有问题'
print e
def showLastLine(self, n):
# 一行大概100个吧 这个数改成1或者1000都行
len_line = 100
# n默认是10,也可以follow的参数传进来
read_len = len_line*n
# 用last_lines存储最后要处理的内容
while True:
# 如果要读取的1000个字符,大于之前存储的文件长度
# 读完文件,直接break
if read_len>self.file_length:
self._file.seek(0)
last_lines = self._file.read().split('\n')[-n:]
break
# 先读1000个 然后判断1000个字符里换行符的数量
self._file.seek(-read_len, 2)
last_words = self._file.read(read_len)
# count是换行符的数量
count = last_words.count('\n')
if count>=n:
# 换行符数量大于10 很好处理,直接读取
last_lines = last_words.split('\n')[-n:]
break
# 换行符不够10个
else:
# break
#不够十行
# 如果一个换行符也没有,那么我们就认为一行大概是100个
if count==0:
len_perline = read_len
# 如果有4个换行符,我们认为每行大概有250个字符
else:
len_perline = read_len/count
# 要读取的长度变为2500,继续重新判断
read_len = len_perline * n
for line in last_lines:
self.callback(line+'\n')
if __name__ == '__main__':
py_tail = Tail('test.txt')
py_tail.follow(20)
效果如下,终于可以打印最后几行了,大家可以试一下,无论日志每行只有1个,还是每行有300个字符,都是可以打印最后10行的
能做到这个地步,一般的面试官就直接被你搞定了,这个代码大概8分吧,如果还想再进一步,还有一些可以优化的地方,代码放github上了,有兴趣的可以拿去研究
待优化:留给大家作为扩展
最后大杀器 如果写出来这个,基本面试官会直接
import os
def tail(file_name):
os.system('tail -f '+file_name)
tail('log.log')
以上就是我对这个题的想法,实际开发中想监控文件变化,其实还是pyinotify好用,跪求大家star
最后做个小广告,欢迎大家关注公共号,高品质运维开发,我们每周五晚上还会做线上公开课,加QQ368573673报名即可,都是关于linux,运维,python和前端的相关内容
如果您觉得有我写的东西对你帮助,可以打赏点钱给我支付宝支付宝[email protected]或者扫二维码
http://shengxinjing.cn/blockchain/build-block-with-nodejs.html
资深程序员,擅长javascript,python
今年重读了一下浪潮之巅外加吴军博士的新书《硅谷之谜》,浪潮之巅封面上的一段话我很喜欢,大概意思如下
近一百多年来,总有些公司幸运的站在了技术革新的浪潮之上,一旦站在浪潮之上,即使不做任何事,也可以存活几十年,外人看来,公司和公司内的职员都是时间的幸运儿,特别是对于一个人来说,一生赶上这样的一次浪潮就足够了
这句话,应该是 站在风口,猪也会飞的原始版本吧
# 浪潮之巅
整本书都在通过介绍著名IT公司的兴衰来介绍it史,但是读完又感觉收获更大的,是作者对这些历史读到的见解,每个互联网人都应该了解it的兴衰史,我们只有了解了历史,才能看清未来
本书首先介绍了各大it公司的兴衰史,包括att
http://shengxinjing.cn/fe/qa.html
资深程序员,擅长javascript,python
据说右上角先给个star再看,能掌握博客代码的100% 哈哈
我是北交大的,所以就拿自己学校练手吧 知行论坛,大家学会方法后,爬什么都很easy啦,用简单的代码,说明简单爬虫的原理即可
基本所有学校论坛,都有一个十大模块,我们学校也不例外,也是我比较关注的,我们就写个脚本爬一下十大列表吧
图里红色方块,就是我关心的部分,在写爬虫之前,我们先来普及一下基础知识,我们看到的网站,是红红绿绿,挺好看的,但是代码看来,其实就是一大串字符构成,比如我们新建一个文件,zhixing.html,注意,一定要用.html结尾,用文本编辑器打开,输入以下内容
name:<input type="text">
<input type="button" value="click me">
然后双击,浏览器会打开这个页面,你就会看到一个输入框,和一个按钮,见下图,这就是最简单的html
所以我们用代码去抓各种网站,代码其实看到的,就是一堆html标签,我们需要做的,就是从标签里面解析出我们想要的内容,并且输出
我们需要python的requests模块来发送请求,用pyquery来解析数据
# coding=utf-8
import requests
url = 'http://zhixing.bjtu.edu.cn/portal.php'
r = requests.get(url)
print r.text
这几行代码抓取的内容,应该和大家在浏览器里 右键->查看源代码看到的东西是一样的
复杂的html结构,各种层级嵌套,如果想自己写一个解析html的工具,估计还没学会编程就直接狗带了,我们一定要善于使用现有的工具,比如我很喜欢的pyquery,
- pyquery是python的一个模块,使用jquery的语法解析html文档
身为一个前端工程师,对pyquery简直毫无抵抗力,看代码之前,给大家再普及一下,我们在chrome里右键->审查元素(或者点F12),就可以看到浏览器的元素层级结构,具体见下图,我们通过html元素的id或者class属性找到元素即可
先看下F12页面,比如我们查看头部的banner广告
再看十大对应的标签位置
找到了两个模块的id,聚焦的id是portal_block_654,十大的是portal_block_617
我们找到了具体的标签,通俗易懂的方式就是,网页里面,id是portal_block_617和标签下面的li标签,下面的a标签就是
- 找id的语法,是#,class是小数点. 标签就是标签名,这是juqey的基本语法,这些基础内容可以直接百度
# coding=utf-8
import requests
from pyquery import PyQuery as pq
url = 'http://zhixing.bjtu.edu.cn/portal.php'
r = requests.get(url)
p = pq(r.text).find('#portal_block_617 li>a')
for d in p:
print pq(d).text()
效果如图 不动戳大
我们已经成功拿到标题啦,如果想加上今日聚焦,今日聚焦和十大的标签结构有点不太一样,是table包起来的,所以只需要稍微改一下下,代码如下,主要是find的地方不太一样
# coding=utf-8
import requests
from pyquery import PyQuery as pq
url = 'http://zhixing.bjtu.edu.cn/portal.php'
r = requests.get(url)
p = pq(r.text).find('#portal_block_654 table a')
for d in p:
print pq(d).text()
bingo,稍微扩展一下上面的代码,把每个十大的连接地址拿出来(今日聚焦的自己扩展吧)
# coding=utf-8
import requests
from pyquery import PyQuery as pq
url = 'http://zhixing.bjtu.edu.cn/portal.php'
r = requests.get(url)
p = pq(r.text).find('#portal_block_617 li>a')
for d in p:
print pq(d).text()
print 'http://zhixing.bjtu.edu.cn/'+pq(d).attr('href')
效果如下
最终结果
今天舍友推荐了首神曲,超越《忐忑》
http://zhixing.bjtu.edu.cn/thread-976923-1-1.html
咱们交大部分人素质真心不敢恭维
http://zhixing.bjtu.edu.cn/thread-976951-1-1.html
大摆长裙如何愉快滴坐下
http://zhixing.bjtu.edu.cn/thread-976887-1-1.html
积分增长这么慢,何日才能升级啊。。。
http://zhixing.bjtu.edu.cn/thread-976954-1-1.html
求推介高清电影论坛
http://zhixing.bjtu.edu.cn/thread-976901-1-1.html
我双十一的包裹终于到北京辣~\(≧▽≦)/~
http://zhixing.bjtu.edu.cn/thread-976912-1-1.html
【论】别人家的学校~
http://zhixing.bjtu.edu.cn/thread-976966-1-1.html
我觉得知行应该搞一个板块叫过往的十大
http://zhixing.bjtu.edu.cn/thread-976946-1-1.html
我觉得在宿舍拖凳子声音应该小点
http://zhixing.bjtu.edu.cn/thread-976928-1-1.html
免费的论文查重网站
http://zhixing.bjtu.edu.cn/thread-976970-1-1.html
今天的第一部分先单这里,我们已经拿到了连接地址,就可以继续去抓帖子的具体地址,还可以根据用户选择,去查看不同帖子的内容,甚至还可以发帖和恢复,但是有一个问题,那就是
- 知行的帖子查看是需要登录的,我们现在直接抓,只会抓到让你登录的信息
我们需要一个东西叫做cookie,我们的登录信息都存放在cookie里面,我们抓取网页的时候,带上登录信息,就像咱们的一卡通一样,不带卡进宿舍楼,就会被拦着,带着一卡通就可以畅通无阻啦,我们就需要带着登录的cookie去抓十大的具体信息就OK拉
后续教程:
以上,都是在命令行里执行的 我写代码的间隙,执行一下命令,就可以看下母校的十大,关注一下学校最近的状况,不耽误时间哦
如果您觉得有我写的东西对你帮助,可以打赏点钱给我支付宝支付宝[email protected]或者扫二维码
http://shengxinjing.cn/books/2020.html
资深程序员,擅长javascript,python
资深程序员,擅长javascript,python
最近在写公司内部服务器集群的监控工具 HTTP状态码也做了个饼图
正当我觉得这玩意在逼格甚高的时候
主程说我在浪费时间
FUCK
愿天堂没有程序员
http://shengxinjing.cn/wheel/weibo.html
资深程序员,擅长javascript,python
厨房
客厅
厕所
主卧
次卧
阳台
装饰品
http://shengxinjing.cn/books/2019.html
资深程序员,擅长javascript,python
资深程序员,擅长javascript,python
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.