由于网站要实现图片和文字的混排的上传
所以在网上找了富文本编辑器的插件,发现CKEditor这款还不错的插件

我用的是4.5.1这个版本,的确很好用,只是引用了一个js文件就可以实现。


  1. 将网上下的CKEditor包解压放在根目录下
  2. 在页面上引用CKEditor的核心包ckeditor.js
<script src="ckeditor/ckeditor.js" type="text/javascript"></script>;
  1. 在页面添加一个输入框textarea

    <textarea name="individual" id="individual" runat="server"></textarea>
    <script type="text/javascript">
     CKEDITOR.replace('individual');
     </script>
    

只要通过上面上面几个步骤就能实现富文本编辑器,但是点开图片上传功能,发现只有上传url的功能,并不能本地上传图片,百度了一下发现由于安全性问题CKEditor没有上传功能,只有安上CKFinder才能实现上传功能,于是我在官网下了ckfinder__aspnet_2.5.0.1,同CKEditor一样引用JS文件(只要引用ckfinder.js)

<script src="ckfinder/ckfinder.js" type="text/javascript"></script>
  • 由于我是MVC的网站基于.net4.5,在官方给的包里面有一个asp.net网站实例放在_source文件夹里面,里面有一个基于.net2的示例网站,一运行就报找不到 System.Web.UI.Design这个命名空间的错,所以我把它从项目中排除,把项目中bin中debug文件夹下的CKFinder.dll复制出来,引用到我自己的项目中。

    接下来要配置CKEditor来让CKFinder引用进来,在CKEditor文件夹下config.js在CKEDITOR.editorConfig = function (config) {};方法中添加如下代码:

    config.filebrowserImageBrowseUrl = ‘ckfinder/ckfinder.html?Type=Images’;
    config.filebrowserFlashBrowseUrl = ‘ckfinder/ckfinder.html?Type=Flash’;
    config.filebrowserUploadUrl = 'ckfinder/core/connector/aspx/connector.aspx?command=QuickUpload&type=Files';
    
    config.filebrowserImageUploadUrl = ‘ckfinder/core/connector/aspx/connector.aspx?command=QuickUpload&type=Images’;
    config.filebrowserFlashUploadUrl = ‘ckfinder/core/connector/aspx/connector.aspx?command=QuickUpload&type=Flash’;
    config.filebrowserWindowWidth = ‘800’; //“浏览服务器”弹出框的size设置
    config.filebrowserWindowHeight = ‘500’;

注意在配置Url的时候要修改成相对于网站本地网站磁盘文件路径,比如说,你的网址是http://example.com.cn ,你把ckfinder文件夹放在Admin下的Editor文件夹,那么所有url要改成下面类似的格式

config.filebrowserImageBrowseUrl ='/Admin/Editor/ckfinder/ckfinder.html?Type=Images';

否则会报404错误,

最后一步是修改一个函数让所有人能看到服务器上传文件夹里面的文件,在ckfinder文件夹下面的cofig.ascx文件,找到 CheckAuthentication函数将返回值改成true

当然如果你想修改上传文件的地址,你可以在上面方法里面找到SetConfig()方法,找到BaseUrl,修改为你想上传的地址,

—–

PS:

我是在在VS里面进行调试的,由于VS的IIS在调试的时候不允许对磁盘文件的路由地址访问,就是CKFinder通过ckfinder.html这个html来实现上传图片的功能,但是这个在调试的时候VS无法访问这个文件,所以一直报404错误,可以修改IIS来允许IIS访问磁盘文件,步骤如下:

  1. 右键点击IIS Express,选择显示所有应用程序
  2. 找到运行网站的配置,进入applicationhost.config文件夹
  3. ctrl+f 寻找UrlRoutingModule
  4. 将preCodition设置为空字符

    <add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" preCondition="" />
    

如果你是用MVC进行表单传值的话,你必须在post方法上面添加 [ValidateInput(false)] 属性,如果不这样的话就会报下面的错

“/”应用程序中的服务器错误。
从客户端(content="<p>sdfsdafwewo shdfh...")中检测到有潜在危险的 Request.Form 值。

###

最近学了两个python库,一个负责管理线程,一个负责管理进程,原来一直写的都
是些单线程的程序,虽然web也关于并发和多涉及到线程,但都是框架管理的,学习>过后发现了解线程和进程对python的web开发也有一定帮助。下面先谈谈这对python对线程和进程的支持再谈谈对这两个库的应用。

python对线程的支持并不是非常好,所以你可以在很多文章上批评python的多线程的弊端,但是为什么python对多线程支持不好呢,为什么其他语言比如

静态语言没有这个弊端呢。

首先我们要知道python是一种解释性语言,每段代码都需要解释器编译运行,解释器有很多种最主要的是CPython,其他还有IronPythonJython,官方的是CPython解释器,我们一般说对多线程支持不好的就是说的CPython解释器(用的人最多就省略成python解释器),python解释器为什么对多线程支持不好呢,是因为GIL的存在,当然这个存在就是因为这门语言的的特性产生的。

GIL是什么呢,下面是官方的解释

In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.)

就是GIL是python的互斥锁,简单的理解就是代码会锁住python解释器。理解代码的锁定是什么必须要先了解什么是多线程

多线程表示一个主线程,多个子线程,主线程是程序执行时系统自动给你申请的一个线程,而子线程我们可以理解为一个代码块,我们可以充分利用硬件的支持比如说多核,让一个CPU执行主线程,其他CPU执行子线程,通过操作系统的虚拟内存技术让所有线程共享相同代码空间达到提高代码效率的作用,我们可以通俗的把一个进程比作一辆火车,车厢头为主线程,每节车厢为子线程,只要你车厢(子线程)越多,你运的货物也越多,但是也要考虑硬件的方面,

了解完多线程是什么我们就可以解释GIL对多核CPU工作性能的影响了,在单核CPU里面,主线程在释放GIL的时候,把CPU让给子线程,子线程代码块得到GIL,然后执行,这样就能充分利用CPU,这个GIL对单核性能的发挥没有影响,能得到100%的利用,但是在多核的的时候就有问题了,假如主线程的代码一直需要解释器来执行,
比如说下面

GIL.acquire()
try:
    while True:
        do_something()
finally:
    GIL.release()

主线程代码对GIL的锁定和解开只间隔很小的一个系统时间,子线程在其他CPU核心得到GIL解开后CPU的调度命令后才能被唤醒,但是当唤醒后,主线程的代码又锁了GIL,然后只能等待主线程下次调度命令,但是到了切换时间又切换回去到待调整状态,一直处于唤醒,等待的恶性循环,多核的功能完全没有发挥出来而且还比单核更加差,所以python因为GIL的存在对密集型的线程支持不佳,但是假如主线程是在执行想web这样等待用户输入,而不是每分每秒都在使用解释器执行代码,多线程的优势就能发挥出来。

解决方案

GIL作为解释器的一个Bug一样的存在,我们也有一定的解决方法,开线程,和用Ctype绕过解释器是我们一般的解决方法,你想了解更多可以看这个
接下来主要解绍用multiprocessing来绕过多线程的瓶颈

线程锁和进程锁

为了实现线程安全,我们也要借助锁的存在,我们先用下面的代码来验证一下多线程对于线程安全的问题。我们声明一个线程锁 threading.Lock(),
class Counter(object):
    def __init__(self, start=0):
    self.lock = threading.Lock()
    self.value = start

def increment(self):
    logging.debug('Waiting for lock')

    self.lock.acquire()
    try:
        if self.value < 8:

模拟负载

            logging.debug('Acquired lock')
            self.value = self.value + 1

    finally:
        self.lock.release()
def worker(c):
    for i in range(2):
        pause = random.random()
        logging.debug('Sleeping %0.02f', pause)
        time.sleep(pause)
        c.increment()
    logging.debug('Done')
counter = Counter()
for i in range(20):
    t = threading.Thread(target=worker,args=(counter,))
    t.start()
main_thread = threading.currentThread()
for t in threading.enumerate():
    if t is not main_thread:

保护线程

得到value值

我们运行之后得到counter.value值为8,这很好理解因为我们限制了它的大小小于8时才自增1,但是如果我们把锁去掉呢,我们把self.lock.acquire()``self.lock.release()都注释掉,得到的结果却是一个21,而且每次运行的结果都可能不一样,由于线程在实现自增的时候有一定的时间(time.sleep(2)),所以当多个进程执行的时候当他们从堆栈上取到counter.value值都为7时,这时候他们都满足
counter.value小于8,所以都执行了自增,在系统负载2秒之间(time.sleep(2))有多少个线程执行就会逃过我们给他的限制,这样就造成了线程的不安全,但是我们给他加上锁之后,无论开多少个线程,最终结果都是8。在python里面我们线程锁和进程锁我们可以看做是同一种东西。

ps:当同一线程相互争夺锁时,失败的会进出线程队列等待锁解开。

线程进程工作方式

单行

单行主要通过锁来实现,线程通过锁threading.Lock()对象创造锁,进程通过multiprocessing.Lock()对象创建进程锁,单行操作一般都是对共享数据修改的一种保护。

并行

并行操作是一般是对数据的一种共享,一般不对公共数据涉及修改,我们可以创造很多线程和进程一起并行操作,也可以限制线程和进程的并行数量,两种方式选择主要是判断代码类型是I/O密集还是线程密集型的。如何限制并行数量我们可以通过threading.Semaphore(sizenum)(进程为multiprocessing.Semaphore(sizenum))我们可以控制对共享的线程数量。进程提供了一个进程池的类型(multiprocessing.Pool),我们可以创建一个维护了一定程的进程池,但是他同时并行的数量并没有控制,只是帮我们创建了这个进程池,每个进程并不是只执行一个任务,可能执行多个方法通过一个进程.

单行混合并行

单行和并行混合我们可以通过在代码中设置锁来实现,当然python给我们提供了两种对象来实现单行和并行的控制,线程的是threading.Event()threading.Condition(),进程的是multiprocessing.Event()multiprocessing.Condition() 两种对象都是提供了一种命令指令,但是Event对象可以用来判断命令是否下达而做出相应的反应,而Condition对象更倾向于当命令下达后才执行并行的操作。

线程和进程通信方式

当我们想让线程和进程共同执行一些固定的任务,我们就需要线程和进程之间能够通信,线程和进程通信我们使用队列(Queue),进程和线程的Queue有点差异,就是进程Queue传递的对象必须pickle化,而且为了能够使用join()(保护进程)task_done(通知任务完成),我们一般使用JoinableQueue
代替Queue在进程中。

Queue对象之间通过putget通信,我们把任务put上去,Queue自动分配给当前的线程或进程,
这样就能实现对任务的流水作业话。

引用

12/26/2015 10:50:21 PM GIL维基资料

GIL博文

最近在学利用python进行数据分析,发现pandas这个库对数据的处理非常灵活,
而且号称python的maltlab的.其在图形绘制也非常方便

在Windows下安装python关于科学计算的库非常不方便,还好有一些公司做好了关于python计算库在Windows安装的安装文件,这里我推荐anaconda,我推荐安装32位包,因为64位有很多库不支持.

pandas内部是使用numpy做存贮结构的,所以我们必须先了解一下numpy

先谈谈我对机器学习的理解

什么是机器学习?

我们人类有从婴儿开始就开始学习,父母教我们穿衣吃饭、老师教我们读书写字,我们开始能辨别好人坏人,开始通过自己的经验来判断新事物。

机器学习很简单,就像人一样,我们教机器通过我们教的来判断新的事物,或者在从新的事物里面学习处理新的事物。

这看起来很复杂的样子,但是从我们神经网络来看,我们可以把学习当做建立一个神经元连接,通过输入的信号得到一个输出的信号.我们只要简单的把输入的信号分类就可以了.通过无数个分类我们就可以建立复杂的神经系统,进而实现’学习’这个功能.

如何分类?

涉及到分类,假如输入的信号种类只要两种,我们就可以简单用if-else来实现分类功能,但是有时候输入信号种类个个都有细微的差别,只是遵循某种规律,这时候我们不能用简单的if-else来进行分类了,下面我就按照书的顺序来解释各种强大的分类方法.

K-近邻算法 ( k-Nearest Neighbor )

作为本书的第一个机器学习算法,K-NN算是我感觉原理最简单的一个了.

假设我们有两个点, 红点为(-1, -1)分为红类, 绿点为(1, 1)分为绿类
图片1

接下来我一个点(0, -1),这个点应该分为红还是绿呢,我们添加两条辅助线

蓝点离红点距离为1,蓝点离绿点距离为2.2,我们很轻松的可以知道这个点应该分为红类.
现在我们进一步推广,当有很多种类点的时候,当我们二维扩展到N维,给一个点a我们只要选取距离a最近的K个种类,我们就基本能判断他属于这K个种类的,这就是K-近邻的原理了.

K-近邻算法是最简单最有效的算法了,但是他也有缺点,比如他必须保存所有训练样本的数据,当训练样本很大的时候就会占用很多内存空间,我们后面会学到的KVM只取支持向量的训练样本来计算可以减少很多占用内存

而且K-近邻算法对训练数据集都要计算距离值,实际使用可能会非常耗时,我们后面学到的logistic回归能很好解决这个问题.

######### 总而言之,KNN作为小样本时非常简单粗暴,但是他无法给出任何数据的基本结构信息.接下来我们要学习用概率测量解决分类问题,这个算法能解决这个问题

(决策树)[]

管线命令 (pipe):

  1. 撷取命令: cut, grep
  2. 排序命令: sort, wc, uniq
  3. 双向重导向: tee
  4. 字符转换命令: tr, col, join, paste, expand
  5. 分割命令: split
  6. 参数代换: xargs

    分割文档

长长的一大片文档有时我们并不愿意看到全部内容,我们只想关注
部分内容的时候了可以考虑使用分割文档命令

cut是一个很好的分割文档工具

vi常用命令

  1. [Ctrl] + [u] 屏幕『向上』移动半页
    • 光标移动到非空格符的下一列
    • 光标移动到非空格符的上一列
  2. n那个 n 表示『数字』,例如 20 。按下数字后会向右移动这一行的n 个字符。例如 20<spac移动 20 个字符距离。
  3. 0 这是数字『0 』:移动到这一行的最前面字符
  4. $ 移动到这一行的最后面字符处(常用)
  5. H 光标移动到这个屏幕的最上方那一行
  6. M 光标移动到这个屏幕的中央那一行
  7. L 光标移动到这个屏幕的最下方那一行
  8. G 移动到这个档案的最后一行(常用)
  9. nG n 为数字。移动到这个档案的第 n 行。例如 2档案的第 20 行(可配合 :set nu)
  10. gg 移动到这个档案的第一行,相当于 1G 啊! (
    n n 为数字。光标向下移动 n 行(常用)

基于python2

scrapy是一款非常轻量级的爬虫框架,但是由于它隐藏了太多关于网络请求的细节,所以我们有时候会遭遇到一下很尴尬的bug,当然这主要是因为碰到一些不规范的网站。

python的编码转码网上有很多文章,如果你不了解这个你可以参考下面了解。

Ned Batchelder 关于python unicode和str的理解,通俗易懂

关于scrapy 入门

关于 encode的认识

通过上面我们可以很好的理解python的转码译码,在这里我想谈一下我自己对其的认识吧,我一开始接触的c语言序列的基本上都是强类型,比如C里面假如我想写一个函数每个传人的参数都得是有类型的,但是python弱化了类型这一点,python也是面对对象的,但是他的对象就是鸡同鸭讲,照猫画虎就能运行,弱类型适合动态语言,我们不确定下一行代码输入的是什么,自从学python起,一直感觉python对类型一直不严格,这样就给了我一种错觉,只要长得差不多就能一样的比划,比如在两个string,'中国',u'中国',看起来差不多但是如果你把u'中国'存入文件中就会出错(假如你没定义编码规则)
UnicodeEncodeError: 'ascii' codec can't encode characters in position 344-351: ordinal not in range(128)
unicode字符编码错误,要想理解这个要对unicode字符集和unicode编码有一定的理解,推荐你读一下这篇博客字符编码的知识,python内部使用unicode字符集存贮所以的编码的字符,为什么要用unicode字符集举个栗子吧:

A是米国的程序员,他使用asicc编码的文件上传了一封邮件,
B是中国的程序员他使用gbk编码的文件上传了一封邮件,
现在C要用程序同时处理A和B的邮件,有两种解决方法他把A的文件译码再编码成B的gbk,或者将B的文件译码成asicc但是中文无法处理,那么只能使用第一种方法将A的文件编码成gbk,但是改天D又来啦,他是俄国人,天啊噜gbk可能没有把俄语编进去,那肿么办,我们迫切需要一种编码可以把所以的字符放进去,所以unicode出现了,Unicode中将字符集按照一定的类别划分到0~16这17个层面(Planes)中,每个层面中拥有216=65536个字符码,因此Unicode总共拥有的字符码,也即是Unicode的字符空间总共有17*65536=1114112,一共有1114112这么多的字符可以用,这下我们不用担心了吧,太好了这下不用愁了,

python 内部使用unicode字符集作为一个译码中转站,因为他编码了所以的字符集,只要你能在自己编码方案上找到自己的字,我就能在unicode字符集找到你的位置,所以使用unicode可以很好的解决多种编码方案产生的问题(比如gbk,utf-8)
当然其他编码方案如果想使用unicode解码成其他的必须同unicode有一一对应关系,不过现在主流的编码方案如gbk,gb2312,utf-8都是unicode系的。

了解了这些基础知识就可以知道了为什么存贮u'中国'存不进文件里面去了,因为unicode并不提供给当今字符解析器的方法,就是\u234e一个16进制数字,屏幕上不知道他对应什么图形,所以python系统要求存进文件的必须是字节流,也就是可以unicode是一种更高级的字符流,这个字符流能存贮当今世界所以定义的字符,但是他只是一个规定字符集合,我们只需要把发现的字符放进去占据一个位置,但是我们不需要考虑屏幕是否认识这个字符,这个字符的存贮由编码方案负责,如utf-8这些,假如没有字符编码方案可以存贮这些,我们虽然在unicode上有这个字符但是我们无法print出来,所以我们必须将unicode转换成普通字符流,有人就会问了,假如我真的没有找到一个合适编码方案可以存贮所有语言,我们可以将他编码成unicode—escape类型,这里我们不多讲。

这就可以解释我们大部分碰到的错误unicodedecodeerror和unicodeencodeerror错误,都是因为字符编码方案不了解造成的,网上很多说碰到这种错误就encode,decode搞一下就行但是不弄清楚这背后的知识就会犯迷糊。

接下来我谈谈我遇到的错误吧,在爬取http://yjsy.ncu.edu.cn/yjs_showmsg.asp?id=2770这个页面时(这是一个不规范的页面没有设置charset),因为每个spider调用的

response.xpath('//xpath').extract()    

选择器返回的是一个unicode编码的字符集,但是他是接受的是一个字符流,spider可能调用了
response.body.decode(response.encoding)进行转码,但是这个response.encoding有时候会判断错误,比如将我一个gbk编码的文件判断成cp1253,这个时候假如我把他解码成encode成其他编码方式的话,我们就会得到乱码,那怎么纠正呢,我们可以这样干
先将得到的列表中每个content取出来,然后使用content.encode(resonse.encoding)转码成原始字符流,现在你可以将它用正确的编码转换成unicode了

下面是我github上的关于这个scrapy的项目,在coding_pitch.py文件里面就是对于这个乱码的处理

南昌大学教务处公告爬取


re

Python对正则匹配的库是re,re是基于Perl所用的正则表达式,并有
一定的改进.

正则本质就是搜索所需的文本,正则里面有三种搜索方式

  1. 第一种是知道文本内容直接使用普通字符搜索出来,比如要从abcdefg中搜到cd
  2. 第二种就是模糊查询,比如我想从英文中找一个数字,一般借助特殊符号(.+*?)或者转义符号(\w\d等)
  3. 第三种就是结合前两种,比如我记得一个单词的前两个字母想把那个单词搜出来.

这里不介绍正则基本知识,你想知道可以点这里

ps: 由于在python里面也是用反斜杠做转义字符,所以比如\\\b这两个特殊字符必须用\\\\\\b来代替.但是python提供了一个元字符支持re模块,只要字符前面加上r比如r' regex '就能不关闭python的转义.

正则里面我觉得很重要的一个概念就是组概念,当我们的文本比较复杂的时候将其分成多个小组是利于我们正则的后期维护和改进

正则里面使用一个括号来表示组比如(a)(b)就分成了两个组

re函数里面searchfinall都支持组查询,而且findall方法假如里面有组分布会只显示组成员.

re库支持搜索选项,这几个选项对于正则有时候非常有用

DOTALL [简S]-------------允许点字符匹配换行符
IGNORECASE [简I] --------忽悠大小写
LOCALE  [简L]  ----------支持本地化字符
MULTILINE [简M] ---------多行,每行都支持锚点
UNICODE [简U]  ----------支持Unicode,\w也可以是Unicode了
VERBOSE  [简X]  --------------神器,会无视代码中的注释空格和换行

我们也可以在正则的组里面使用这些搜索选项,只要用上面的简称的小写比如(?is)就可以在组里面使用这些规则.


正则里面还有一些比较有趣的函数,同string里面的translate函数,sub函数可以替换找到的变量
bold = re.compile(r’*{2}(.?)\{2}’)
bold.sub(r’\1‘, ‘this foo and ok‘)

\1代表第一组变量也就是foo和ok
输出为'this <b>foo</b> and <b>ok</b>'我们使用成功用加粗了foo和ok,同translate不同这个方法不需要知道要替换的是什么.


正则的断言

我们可以使用一些特殊的符号来执行一些程序判断选择,比如说判断是否特殊字符,如果有
的话就不匹配,这就是断言

断言有两种一种是前向,一种是后向

前向是指判断语句在前面,这种就相当于一个if语句,而后向是匹配后判断,由于已经匹配好了文字所以
匹配的字符必须是固定长度的(不能使用*.?).

前向就是在判断后面匹配的表达式必须与规定相同,比如一个邮箱地址我们要匹配可以用<>包起来的,但是不匹配只要一个的我们就可以在前面加上这个^(?=(<.*>$)|([^<].*[^>]$))通过使用?=来断言后面必须是用<>包起来或者没有<>,我们使用前向断言可以通过正则直接过滤掉不符合的(当然你可以用多个简单正则来做但是效率没有这个高),还有否定前向就是通过?!来声明.
相对应后向断言就是很简单了,直接在匹配后面使用一个?<=(肯定后向)或?<!(否定后向),不过要注意这个是判断前面匹配是否满足的.

断言只是限定我们想选的文本的范围,他并不会被选择.
断言的一个有趣的应用就是选择字符间的空格,我们知道python其实假设每个字符间都一个空格(这就是我们有时候会选出一些空字符出来的原因),这个空格不是我们自己打上去的.

举个例子

两个字符串a1a 1,第一个我们称为A,第二个我们称他为B,假如我们想把数字和字母分出来,对于B来说,很简单因为数字和字母之间有一个空格,我们可以直接使用字符自带的split就行,但是对于A来说,就不那么简单了.

字母a和数字1中间没有字符,我们必须把字母和数字之间的”空格”给选择出来,这时候就可以用到断言了.

r = re.compile(r'(?<=[a-z])(?=\d)') 

这个r就可以字母和数字直接的隐形空格给选择出来了

遗憾的是由于python的正则并不把隐形的空格当做字符,所以我们不能简单的使用正则的r.split方法(选择字符分割)直接将字符串分解开.

我们就得写几步

第一先把空格换成 $$$(或其他)

>>> s = r.sub('$$$', 'a1')
>>> print(s)
'a$$$1'

然后在分割

>>> s.split('$$$')
>>> ['a', '1']

成功分割好了,当然这个只能处理字母在前数字在后的”隐形空格”,只要加一个"|"在把前向改成后向,后向改成前向就可以选择任意字母和数字直接的”隐形空格”了.


正则的变量

我们可以使用?P来声明一个组(用括号,当然其实我们每使用一个括号re自动帮我们将组取一个
名,依次从1-n

有时候我们可以要求上面的匹配组,下面也要相应匹配组,我们就可以通过两种方法来引用这个变量,假如你没有使用<?P<name>来声明组你只能通过\n来引用,n是这个变量的序号,第二种是通过(?P=name)
来引用这个变量,name为你自己定义的组的名字

re还提供了一种机制来让你修正你的正则,简单来说就是能判断一个组存不存在来约束匹配,语法为

(?(id)yes-expression|no-expression)

id为组的编号或者name.

泛型对于解决面对对象编程的算法设计可以提高其运算速度,但是对于引用类型来说还是没什么差别,因为引用类型只是指针的地址的调用,简单来说泛型还是挺好理解的,但是对于泛型、非泛型、继承和接口的融合就有些迷惑了。

比如说这种接口
public interface IEnumeratot<T>:IDisposable,IEnumerator,ICompare<T>

这个泛型接口继承了两个非泛型接口,和一个泛型接口。


我一开始理解泛型就是一个个模型,只要我们把类型一个参数赋给他,他就能生成一个标准的类型,他缺少的只是一个参数而已,我们引用的时候感觉就像我们引用一个“全体方法”,把参数赋给类型后就可以一直调用类中的方法了,但是对于接口的继承如何理解?

对于泛型类的继承,继承的类必须实现泛型的参数或者保留泛型的参数,比如下面

  public class A<T>
{
    public T tt;
}
public class C<T> : A<T>
{
    public T tt;
}

或者是这样

  public class A<T>
{
    public T tt;
}
public class C : A<string>
{
    C cc;
}

对于泛型继承非泛型类,比如下面

public class A 
{
     A aa;
 }

 public class B<T>:A
{
    B<T> bb;
}

基类是非泛型,而继承的是泛型类,我感觉这种构造就是让泛型类多了一种包容性,比如下面的链表实现的代码,让基类是非泛型,而继承是泛型,就能让链表可以连起很多种类型的数据,而本身的类型安全没有丢失。

public class Node
{
        pretected Node next;
          public Node(Node next){
            this.next=next;
            }
}
public class TypeNode<T>:Node
{
    public T data ;
    public TypeNode(T data):this(data ,null){
        }
    public TypeNode(T data,Node next):base(next){
        this.data=data;
        }

    }

泛型的约束

申请了一个阿里的15体验的云服务器,同自己玩的虚拟机还是有点不同的。

1.用户名和密码

找了半天没有找到那个是用户名,试了实例的id,没有用,最后终于在登录帮助名里面找到了,用户名竟然是root!!!!,我用的是Ubuntu系统,说好的Ubuntu不提供root权限的呢,阿里还真会改造Linux系统
,但是我觉得用root登录还是不安全,我觉得新建一个用户吧

在root权限下

useradd -s /bin/bash -r -m yourname

解释一下,本来直接useradd yourname 就可以新建一个yourname账号,但是如果用你新建的用户登录的话,你无法使用Tab键和上下左右键 ,你键入 echo $SHELL,会发现是/bin/sh,因为Ubuntu默认创建账号使用/bin/sh,假如你没加这个可以删掉 用这个userdel yourname重新来一遍,搞完这个只是创建了一个普通用户,你使用不了sudo获取最高权限,这个怎么办,很简单在/etc/sudoers里面找到
root ALL=(ALL:ALL) ALL

模仿它加上自己的账号名就可以

学了鸟哥的书前面基础后,突然想在Linux下用gcc玩C语言,然后了解到了Vim这个神一样的编译器,接下来经过超长时间虐心的安装无数插件无数依赖包,突然有种打自己一顿的感觉,还好终于把Vim装的和VS差不多了,接下来我介绍我安装Vim的经验吧。

我虚拟机下的Linux原来是红旗6的,但是我改了一下yum的包源成CentOS的并且全部update一下后就神奇的变成了CentOS6,虽然他们两个是同一家公司,但是总给我一种由盗版成了正版的感觉。。

闲话不多说,刚开始装第一个插件是Ctags

刚开始装的时候我是在X-Windows里面的这里下载再转回shell敲

$ tar -xzvf ctags-5.6.tar.gz
$ cd ctags-5.6
$ make

make install

后来我发现不用这么复杂直接在X-Window下面复制到Terminal里面就可以了,毕竟后面安装的代码都是十几行,根本扛不住。。。。

装完三个感觉整个我太苦逼,简直是辛苦啊,不是说好的敲几下键盘就可以吗。
后来了解到linux有几个软件可以帮忙安装而且解决依赖性,比如ubuntu的apt-get,redhat的rmp,yum,但是问题来了,我的黄狗(yum:yellow dog)怎么没用啊,全都装不上,一查错误提示,原来我没用被授权,原来RedHat更新软件是要收钱的,但是CentOS不用,然后我就用了CentOS的源,当时我就想只想装个git,然后就没用然后了。终于全部更新完了,yum也可以用了,用yum果然爽多了,直接

yum install git    

git就装好了。如果想知道怎么改源,点击这里


好了装好了源接下来装软件就敲一敲代码就行,不用去网上找包下载再安装。

装好一些热门的插件后,我碰到了第一个最难装的插件YouCompleteMe(YCM),装完之后感觉就是神器一般的存在,但是装完之前一直在感慨,我去怎么没个卵用啊,而且装llvm(安装YCM必须先安装的软件)时,我用编译安装方法因为一些库的缺失一直报错,后面采用二进制安装方法安装完llvm才成功按上YCM。
推荐新手采用二进制安装方法安装llvm,想知道怎么安装YCM这些点击这里

安上一些实用的插件后,最后安装调试神器gdb,有三种方式使vim可以调试程序(与gdb一起工作),

vimgdb,pyclewn,clewn,三个我都试了,第一个要编译一下vim,我失败了,第二个要用python2.4+支持,我python2.6然并卵,每次都说不支持python2,第三个是用c写的,终于成功,安装非常简单,
点击这里了解详情.
装完了,我终于可以使用clewn打开gdb和gvim,然并卵,我写的程序并不能在gdb里面运行,一直说无法识别。。。。。。搜了一下原来是编译的时候的问题,用gcc编译的程序要带上-g参数才能在gdb里面用(我还以为我clewn没装好)
例如

gcc -g test.c -o test

加上-g参数就行了。终于我的vim可以用了。。。。。。