时空主站

嗨,我是时空,一名来自中国的开发者。

也许很多人知道,最近有个挺火的项目,叫996.icu。这个项目主要是介绍了软件界常见的”996”加班模式,列举了相关法规和相关报道,并提出了抗议。域名的意义是,加班996,生病ICU。

这个网站在网上的讨论很热烈。我也算是在软件行业里混过一段时间。所以蹭个热点,说说个人看法。

一些基本知识

在说看法之前,首先我想把什么是正常工作,什么是996,以及关于996的一些事实情况(至少是我知道的情况)讲清楚了。否则后面讨论容易跑偏。

谈到996,首先我们需要说国家规定的理想工作状态。工作时间是一周五天,周休两天,每天八小时。当然,规定并没有说明八小时的涵盖内容,因此分为两种。一种解读是,工作八小时,午饭休息一到一个半小时,不计在内。因此9点上班的话,对应6点或者6点半下班。但是有很多福利不错的单位,午饭一小时涵盖在八小时内。因此就变成了朝九晚五的工作模式。很难想象吧。被我们嘲讽当作社畜象征的朝九晚五,其实几乎是最好的工作制。

关于加班,国家的规定是。一天最多可以加班3小时,一周最多加班8小时,一个月最多36小时(一个月4.5周计)。

关于加班工资,国家规定是。延长劳动时间,支付不低于正常工资的150%。休息日安排工作又不能补休,支付不低于正常工资的200%。法定假日安排工作,支付不低于正常工资的300%。当然,对所有公司来说,不低于都会被默认视为等于。

996和一些变种打法

讲清楚正常工作,和加班的法律规定,下面我们就可以对比一下IT圈的实际打法了。996的原始意义是,每天9点上班至9点下班,每周工作6天。按照全部工作时间计算可以看到,周工作时间是72小时。如果扣除午餐晚餐各一小时,工作时间是60小时。横竖都是超过最长工作时间的。当然,名义上叫996,实际上有不同的弹性制做法。例如10-10-6,也就是比996晚上班一个小时,总工作时间不变。这类变形统称996,代表7x小时级加班。在此之上,还有时间更长的997。工作时间70小时,累计的话是84小时,代表8x小时级加班。以及最高级007,代表168小时级加班。由于一周只有168小时,所以007也是工作时间的极限,没办法再长。这个加班制也被戏称为”License to kill”。

当然,007并不是说我们就无需睡眠了。我们可以简单计算一下一个人的时间分配。假设这个人一天睡眠7小时,吃饭两个小时,洗澡上厕所等必要的浪费时间一小时。那么一天有10小时时间是必然处于无法工作中的。因此极限状态下的加班,最长限度工作时间也就是14*7=98小时。大概就100小时,这还建立在吃住在公司的情况下。再加班,就需要压缩吃饭和睡眠时间了。软件行业赶工的情况下,有周工作时间120小时的极限情况。当然,这种状态是注定无法持久的,结束后需要一定时间的放松调整。

996一个隐性前提

其实在讨论之前,我们需要讲一个隐性前提。996认为,工作的产出和工作时间呈正比。所以为了提升产出,我们需要加班。对于现在的软件行业来说,这是一个正常假定,然而并不是一个必然。产出正比于劳动时间,这是工业化的结果。我们的软件行业,正试图将所有软件的生产流程变成一个标准化的工业制度。

然而很多软件生产过程,或者工业化大生产中的几个特殊职位,依然是依靠几个人的直觉,天赋和经验。脑子想清楚比努力干重要,机遇和灵感比脑子想清楚重要。在这些东西面前,劳动时间并没有用,或者帮不上什么大忙,或者反而有害。在这些特殊的场景下,必然是不需要加班或者996的。然而不能因为这些特殊情况的存在,而否认当前软件行业是个工业化大生产的行业(或者说要逐步进入工业化大生产)。

从这个角度说,艺术式的软件开发,既不会产生996(因为没用),也对行业现状没有帮助。因为艺术式的软件开发,注定不能通过增加人手来获得更高的收益,因而不能吸纳劳动力。

分类讨论

讨论加班工资的时候,我们需要按照几个核心问题将情况分为几类。

  1. 员工是否在招聘前就知道可能面临加班,大致会是怎样一个加班量级。
  2. 员工是否能够收到加班的对价收益。(劳资溢价计入工资或加班后计入加班费都算,福利的话需要足额)
  3. 员工是否可以选择加班或不加班,而且基本不会因为这个选择受到除了加班费外的其他影响。

问题一讨论的是透明性,二讨论的是加班工资本身,三讨论的是公平性。一和二的关系非常紧密。有一就有二。很少有公司在招聘的时候大咧咧的说我们要加班,但是请你按照市面上不加班的工资和我们谈。而反过来,有二也很可能有一。很少有公司明明需要员工加班,也明明会足额给加班费,但是招聘的时候不说明白。因为给足加班费的情况下,唯一的问题就是员工本身适不适合(或者说想不想)加班了。隐瞒这个事实,招一堆不适合的人来再开除,也很少有公司这么无聊。这种情况下常见的是,公司自己也不知道下面会有一段惨烈的加班。

按照前两个问题,我将情况分为三类,最后再讨论第三个问题。一类是招聘的时候直接说明的公司。第二类愿意给足额或基本足额工资。第三类就是只有象征性补偿,或者只有福利,或者连福利都没有。为什么这么分三类呢?因为这三类的动机和影响各自不同。

招聘时讲明的公司

第一类的问题最小。在入职前告知,不侵犯知情权,也不套住人才。合同里说清楚996,并且把公平的对价补偿也写了进去。愿意来的可以来,不愿意的可以不来。很多朋友对这类的意见是,认真负责的好公司。

当然。严格来说,在合同里约定超过48小时每周的工作时间也是违法的。所以很多公司会有工作制告知,但是并不会体现在合同中。只要这个告知早于员工接受offer,那么就可以算为此类。

这类公司,加班的动机往往是程序行业的内在动机。可能很多圈外人不知道,软件行业的效率,和项目人数的平方成反比,随着参与人数的增加而迅速降低。这个观点是Frederick在人月神话中提出的核心观点。他的解释是,软件工作中,有一部分非常重要的工作就是沟通。程序员需要让彼此了解对方意思,才能写出一致无误的软件。而沟通很难群体进行,因而沟通的成本是和团队规模的平方成正比。因此实际践行中,一个团队的理想大小,会限制在10人上下。如果要再扩充规模,往往会虚胖。即团队内实际产生战斗力的人员仍然在10人上下,其余人就是围着干零活,感觉有力使不上。因此大型软件工作往往会采用横向或纵向的模式,来切分工作。例如将一个大型系统分为几个功能模块,约定好彼此的接口,分别开发,最后联调。这样做往往使得模块和模块间产生不少问题,甚至可能导致漏洞或逻辑问题而无法使用。然而从软件工程角度讲,仍然是一个非常好的提升总体速度的方法。当然,模块的切分也是有限的。模块越多,模块间的协调成本越高,总体质量变差的可能性越大。通过模块切分的方式,也无法实现无上限的加人。

那么,我们也就很容易理解这类加班的内在理由了。加班10%能解决的工作量,招人的话需要多招20%。这意味着更多的争抢,更大的人员流动性,更多的培训,磨合。而且加人还需要重新安排团队,更细化的切分项目,更频繁的沟通会议,更高的高级管理人员成本。就算能招到人,最后总成本上升可能远远超过20%。同理,996的加班高达50%-80%,如果招人的话,大概要招2-3倍以上的人,成本则可能高达3-4倍。就算按照加班工资实打实发,加班成本也就是2.2倍。作为管理者,有百分百的动机选择加班,而不是招人。对于他们来说,更实际的做法是入职的时候直接谈一个相对于不加班2-3倍之间的溢价package。员工也能赚钱,公司也能省钱。

愿意给予相当补偿的公司

第二类公司,起码还不算坑到底。如果是一般加班,老板的选择往往是强制结成年假,而不是支付加班工资。因为休一天损失100%工资,支付加班工资可要付150%-300%。然而996的加班往往太多。总不能一个部门半个房间都在休假吧。所以也有协商,部分结算,部分转为年假。

这类公司的理由往往是一般正常公司都有的,软件行业峰谷影响。例如,做2B的知道,年底的时候各种突击花钱,突击签单,争取用光预算。到了来年三月的时候,就变成了非常紧的项目压力了。然而到了下半年接近年底的时候,项目做的差不多验收了,往往单子又会跟不上。因此有些公司会选择以年为周期进行加班和调度。年初的时候多加一些班,到年底开始强制员工休假。如果休不了才进行加班结算。

特别注明一下。首先,这类年级调度加班调休的公司,如果超出每周8小时也是违法的。其次,公司原因导致的停工,其实不需要计算为假期。如果员工不同意使用自己假期,原则上单位需要照发工资和加班补贴。让不让员工干活随意,但是钱必须发。然而,很少有员工和单位这么对着干。

基本无补偿,甚至无福利的公司

这类公司是最常见的,其基本想法就是压低工资或是降薪,或者又干脆裁员。

压低工资很好理解,这似乎是劳动力密集型行业的通病。和员工谈的时候只谈工资,招进来就往最大工作时间利用。为了让员工不走,甚至会使用押金或迟发工资这类手段。这类事情员工通常得不到政府的救济。毕竟农民工欠薪这种事都要总理亲自去要。只是加班而已,似乎也就不那么严重了。一家两家如此做,多了之后,劳方也不管你们加班不加班了,总之就按照加班的开薪资。资方说不加班,劳方不敢信。一来二去,逆向选择,不加班的公司不好混,大家就都开始加班。而一旦成为行规之后,劳方更是不会相信什么不加班的事情。其实这个情况和招聘时讲明很类似,差别在于大家对加班与否的认知不统一。劳方认为我就是值这么多钱的,不加班就是这么高工资,所以公司没有加班费,属于违法违规。公司则认为,员工根本不值这么多钱。之所以给这么高溢价,就是因为要加班。最后就很容易发生纠纷。

我常见的情况是,如果是劳方市场,员工选择权更多,资方往往倾向于招聘时讲明白。因为一旦员工入职后发现劳方和事实相差太大,可以自行离职,不怕找不到工作。那么资方就要承担成本了。而如果是资方市场,通常就是大家都揣着明白装糊涂。反正入职后跑不了。

其实程序员现在也不笨,如果是大公司的话,一般也会找人打听打听是否在加班,面试的时候也会问。但是你可以看到各大公司对加班的态度,大部分对公开媒体的态度都是粉饰加班的,只强调要求员工全情投入。实质上我们知道这个说法也就是说说,不信你试试入职后问你老板,我的工作态度端正绝对全力投入,但是我就是不加班,行不行。

降薪和裁员是大家意见最大,也是反弹最大的。毕竟996相当于公司宣布降薪一半以上,不可能没反应。这个主要是因为软件行业景气状态有波动。仔细观察毕业生薪资就能发现,往往是一年出现天价,第二年就跌穿地板。软件行业的发展不全是线性的。一旦不景气,老板就有压缩成本的动力。压缩成本两个思路,一个是降薪,一个是裁员。然而降薪不好听,裁员补偿极高。怎么办呢?两个问题有同一个解决方法,996。如果同意的话,就是变相降薪了。不同意就无补偿离职了。这个思路有个尤其恶心的实现,就是年底的时候宣布996。这时候走人,连年终奖都省了。

实话说,这种手段叫杀人一千自伤八百。因为肯放弃年终奖和赔偿的主要理由,最主要是因为有一个不错的潜在机会。与其在这里熬着,还不知道拿不拿的到。不如在新工作的margin里找回来。相反,如果没有潜在机会,那不妨先混着。然而能很快找到不错的机会的人都是什么样的人?所以这种策略下最先走的,往往都是公司里的精英。反而是公司想开掉的平庸者更有动力留下来。

公平性的影响

所谓公平性,就是员工能够自己选择是否参与996。因为从道义上说,除了上面的第一种情况,员工和公司的合同只涵盖了40小时工作时间。国家标准也只涵盖了最多48小时每周的工作时间。除此之外的时间,员工并没有义务来工作,给没给够工资都没这个义务。加班费给够了,愿意来,是自愿选择。给够了,还是不愿意来,那是个人选择。不愿意来就给穿小鞋,在bonus和升迁上刁难,甚至威胁开除,这就是逼迫了。

然而现状是,很少有公司敢于说自己是公平的,甚至很少有公司意识到这个问题。大部分公司在考评的时候,都会有一项,工作积极性。其中不愿意加班的人,这项肯定不会太好看。然而细想的话,用员工下班后的表现,考评员工的工作积极性,这是个什么道理呢?

一个隐性的问题

996是不是好事?当然不是,对于大多数人都不是。首先第一点无可反驳的理由。这个行为违法。要相信国家,相信法律嘛。然而法律为什么规定40个小时呢?是不是太保守了?一周最高98个小时,干个70小时不过分嘛。

996对于个人来说,既是个机遇,也是个障碍。短期来说,会有一份非常不错的收入。而且996能为程序员提供很好的上升机会。因为学东西的速度是和工作时间相关的,而上升通道是和毕业年限反相关的。一个机会,同样水平的人,一个毕业一年,一个毕业五年,当然是先选择毕业一年的,因为潜力更大。996能够极大提升新手的机会。

然而当程序员进入30后,后遗症就接踵而至。首先,996会减少员工和家人朋友相处的时间。会做程序员的人本来普遍就不会特别善于交际。相处时间进一步减少后,就很少能有足够的时间谈恋爱。所以程序员单身狗的名号不是白来的。虽说结不结婚是个私事,然而从国家总体角度来看,这当然是个坏事。都没结婚,哪里来的二胎?靠女装么?其次,996普遍减少学习的时间。这使得程序员除了公司相关技术外,自己学的东西很少。软件行业又是个更新换代非常快的行业。最后,当技能没有更新的程序员,和愿意996的竞争者竞争的时候,往往就会被淘汰。因此常有个说法,程序员过了35做不下去。这对个人和行业来说都不是个好事。

所以总体来看,最好是30岁之前996,30岁之后正常工作。然而工作制度的事情并不是随个人想法而改变的。对于行业来说,如果规定至少150%工资,那就是150%。没有人会多给。对于行业来说,一周标准是50小时,那坚持工作40小时的程序员就会失业。之所以要确定行业标准是因为,企业一定会在标准范围内最大限度的利用资源。如果没有统一的行业强制标准,会出现这么一个现象。某个企业提出加班到一周50个小时,另一个企业提出加班到60小时。25岁的程序员说我可以接受70小时,22岁的说我可以接受80小时。这么搞下去的结局是地狱。

这个问题,即便是第一类公司,也没有解决。他们能做的只有,给员工足够的工资,足够的晋升平台。但是他们却不能说给行业做了个好榜样,甚至不是一个好的竞争者。他们的成功,无疑鼓励所有公司尽情加班,只要给钱。无疑限制了程序员行业只能牺牲年轻的肝,让他们在35之后面对茫茫的未来。同时也限制了软件行业的总供给,因为35后的都转行了。

因此,国家制定了各种规章。即便牺牲总体效率,也要限制无底线的加班竞争。而且这一点不仅限制没给够补偿的公司,也要限制给够了补偿的公司。然而很遗憾的,这些规章非但没有贯彻,甚至不被理解。

主人翁意识

很多老板提到主人翁意识,提到拼搏奋斗。拼搏奋斗当然是对的,然而主人翁意识就要看员工在整件事里的收益了。我们不常提,然而又无法避免的一件事是,员工和老板很多时候不是一条心的,甚至往往是对立的。员工不是企业的拥有者,不在企业的获益里有分成(或者说不是所有员工都有分成,有也不一定是对等分成)。要求一个不从整件事里获益的人,为这件事拼搏,本身并不合逻辑。这个问题更扩大点,国家也要求每个公民有主人翁意识。为啥各位大老板不能者多劳一点,把每年的盈利全上缴国家呢?主人翁意识的前提,就是员工从整件事里获得合理的收益。而合理收益的最直观体现,当然是加班费。连加班费都没给够的公司,我们又如何能相信公司能为员工提供足够的分成呢?

事实上,主人翁意识本身就是个悖论。如果员工真的有将企业当作自己的事业来干的干劲和能力,那么他为什么不自己创业呢?现在社会,技术不是壁垒,资本不是壁垒。创业可以说是最好的时机。从这点来推论,大部分在企业内就业的人,要么是不会将自己视为企业的主人,要么就是将要创业的。

有什么可以做的

最简单的,请慎重考虑996的度,以及对人生的影响。有些公司996是因为真的临时有问题。这种公司虽然短期内不合法,但是我仍然认为值得一去。然而多短,多大程度的加班才是可接受的。这个加班又会如何影响你的人生呢?请自行仔细考虑。起码,我不希望我的读者因为996而去世。如果你觉得无法容忍,最低限度的,你应该离开。

其次,我希望大家都提升效率,更有效的工作,并离开996的公司,参与规范工作的公司。这不仅对你有帮助,而且能够帮助整个行业。因为如果做事规矩的公司,因为你而可以对抗施行996的公司,那么会给市场上多一些选项,给其他人多一点希望。

最后,我们希望大家帮助劳动法落地实施。劳动法并不是一部闲的没事干的法律,尤其是劳动时间限制这块。我们有很多法律缺失,然而加班这块法律实际存在,却没有实际实施。所以希望大家在面对权益损害的时候,能够勇敢的合法对抗。至少国家现在也在宣传依法治国,那么实践一下总没错吧。即便不想亲自对着干,也可以考虑支持或给予方便。最低限度,希望大家理解支持并同情权益受损的人。毕竟这些人,可能是你的昨天或明天。

其他行业的一点情况

当然,最后我要说的是,程序员是一个特殊的群体。我们毕竟有高收入,可能无法,但是也可能可以弥补我们加班逝去的青春。我们有发声渠道。社会还能听见我们的声音。然而社会上还有很多行业,加班程度远远超过程序员。例如出租车司机。就我了解,这个行业的特性是做一休一,24小时交班。计算一下就知道,周工作84小时,追平997。但是这是司机的日常。一年365天,连春节都要上班,还得面对程序员写的打车软件的竞争。你看出租车司机有开11365.icu吐槽一下么?又例如医生。在住院医阶段,就是住在医院里的。这个位置的意思就是007,一周98小时。而且医生需要压缩睡眠,工作时长甚至超过007,持续还得持续一年。你又看谁说我们医生太辛苦了,我们把医院关了吧。或者医生收入太低了,我们把诊费和工资翻个倍吧。

我写这些例子,并不是说大家都是这么过来的,你们程序员贱人就是矫情。我是说我们有很多不公平的工作制度,在伤害个人,在伤害行业和整个社会的未来。我们在经济大发展的年代,一切都要跑步前进,没有停下好好看路,也就罢了。现在经济不景气,大家日子都不好过。在这个时候还疯狂加班,是想干什么呢?不如趁机把这些事情扳一扳。毕竟,很多事情再不扳,就来不及了。

转自Shell Xu

钢琴教学可分为启蒙,初级,中级和高级四个阶段,各阶段的学习内容通常包括基本练习,练习曲,复调作品,乐曲几大类。作为基础练习,目前仍然是车尔尼的作品最为系统,如《钢琴初步教程》599,《24首左手练习》718,《钢琴流畅练习曲》849,《钢琴快速练习曲》299,《钢琴练习曲50首》740等。

车尔尼

车尔尼是19世纪上半叶维也纳钢琴演奏学派的代表人物,他所写的大量练习曲是针对古典作品演奏技术,即“古典技术”训练的,主要是手指的颗粒、均匀、快速,对左手的训练较为薄弱,因此期间必须增加各类作曲家的练习曲目作为补充。这些教材可以根据学生的实际情况,有针对性地选择,穿插进行,不能一味的依次一条条的弹下去。

儿童的启蒙阶段

在目前较流行的儿童启蒙教材中大致可分为三种五线谱入门型,即高音谱表入门法,中央c入门法,多音入门法。传统的启蒙教材《拜尔钢琴基本教程》用的就是高音谱表入门法,目前在国内广泛应用的《汤普森浅易钢琴教程》1-5册用的是中央c入门法。

李斐岚,董钢锐编著的《幼儿钢琴教程》采用了以中央c为主的入门法,突破了以往此类教材的音域局限,从3指开始学重量落下,逐步扩大到其余各个手指的方法已被公认为最好的方法。

冯·德·魏尔德:《陶梅格·露丝》系列。这一幼儿系列较注意引起幼儿的兴趣,从小培养他们具有良好的乐感。

柯达依:《钢琴学校》第一册。

巴托克:《小宇宙》。这是一本非常重要的入门课本。因为它的五指位置包括种种非大调或小调结构的五指排列,有助于学生及早接触黑键及各种调式,含有许多复调模仿级对位因素。

齐格勒:《钢琴教本》第一册。该书的特点是先听后弹,耳朵领先。

勋格勒:《钢琴教室》第一册。

狄贝阿里:《二十八首联弹练习曲》中较容易的,越早让学生进行四首联弹训练,越有利于养成用耳朵听辨声部与互相配合的习惯。

钢琴教学的初级阶段

一、钢琴教学初级阶段的练习曲。这个阶段的学习内容相当于从车尔尼的《钢琴初步教程》599到车尔尼的《钢琴流畅练习曲》849。

1、车尔尼的《钢琴初步教程》599

这是我国钢琴初级阶段教学运用极为广泛的教材之一。全书可分为三个部分。第一部分是57首以前这部分是巩固手型,训练手指独立性的练习,要求弹奏放松、自然、练习速度以慢速中速为主。

第二部分是5879首。58首到70首是训练快速练习,这里的快速是针对前阶段而言的,在学生奏法正确的基础上要求初步加快速度。71首79首是带装饰音与不带装饰音的旋律练习。练习时先慢速,把节奏弹准确,弹平均,双手对齐,在稍微加快。这部分又是旋律练习要注意乐谱上的表情术语及力度术语,注意分句、呼吸、旋律的连贯与歌唱。

第三部分是80首到结束,是综合性的练习,无论音型、节奏型、音阶琶音类和双音练习都比前面丰富,练习时不仅要弹准确,还要注意音乐起伏,做出力度,速度变化。

2、车尔尼《钢琴简易练习曲》139

这是从599过渡到849的教材之一。139的教材编排不如599集中,可以选择与599不同类型的曲目。在599到50首左右就可以交叉使用。

3、车尔尼《24首钢琴左手练习曲》718

这是全部为左手而写的练习,内容有音阶,琶音型、五指型、分解和弦等,学习顺序要灵活掌握,对于手小的学生,可以先学音阶型或五指型的曲目,八度练习暂缓。

4、车尔尼《160首八小节钢琴练习曲》821

本教材不仅技术类型较丰富,而且难度跨度大,包含了从初级程度到高级程度的练习。每一首只有8小节,技术课题非常集中,尤其适合一些其他课业多,练习时间较少的学生使用。

5、莱蒙《钢琴练习曲》37(又译为“勒穆瓦纳”)

全书共50首,由浅入深。他的特点是较多的左手练习,双手比较均衡。可以弥补车尔尼练习曲中偏重右手的不足之处。

6、布格缪勒《简易练习曲25首》100,《练习18首》109

这两集练习曲都是有标题的小曲,形象鲜明,手法简洁,旋律优美动听,音乐性较强,非常适合初学者学习。在599到50首左右时,可以逐步选用作品100的曲目。

作品109在技术上比作100难,篇幅也大些。在进入车尔尼的849后可以选用。

7、车尔尼的《流畅练习曲》849

从599到849是初级阶段的前期进入到初级阶段的后期。849共30首,每一首的技术内容基本上不重复。在选用时要具体分析每首练习的难点,考虑技术的“过渡”问题。

849与599,139不同之处是每首曲子有了速度标记,根据849教材的标题,应当做到演奏流畅,尽可能按要求的速度去弹,为下一步中级阶段做准备。

8、赫尔契伯格编《趣味钢琴技巧》

从预备到第五册,由浅入深。编者从几十位作曲家的作品中选择了一些旋律优美而短小的练习曲,每首都有标题,并在标题下给予明确的提示和要求。技术类型比较全面,又由于作曲家各自不同的风格特点,因此,曲目丰富多彩,是一套生动有效的教材。

二、初级阶段的复调

复调作品的特点,是左右手不同声部、不同旋律同时进行,在节奏、重音、力度、句法、旋律、起伏等方面既有内在的联系,又各自独立。对于训练大脑的多维思维,节奏感、左右手的独立性等都是很好的教材。

1、巴赫《初步钢琴曲集》

这本教材共28首,是巴赫为其妻子安娜·玛格达蕾娜写的练习小曲,是初级阶段必学的内容。

2、巴赫《小前奏曲与赋格曲》

这是适用于巴赫《钢琴初步曲集》与《创意曲集》之间的教材。

3、中国作品

中国作品中有一些较简单的复调作品,采用了广大群众熟悉的民歌作为主题,比如王震亚的《沂蒙山小调》,陈静荠的《浏阳河》,黎英海的《盼红军》、《花鼓调》、陈铭志的《子弟兵与老百姓》等。

三、钢琴初级阶段的乐曲

1、汤普森《现代钢琴教程》

这是一套内容极为丰富的教材,由浅入深共五册。除了简易的钢琴曲外,还精选了大量歌剧、芭蕾舞剧、交响乐等世界名曲的改编曲,其中包括巴赫、莫扎特、贝多芬、肖邦、李斯特等著名音乐家的经典作品。在每首作品前,或介绍作曲家,或介绍乐曲特点,或讲解音乐常识,或指出练习要点,使学生在学每一首曲子的同时,获得更多的知识,提高音乐素养。由于乐曲形象生动,深受学生的喜爱。

2、威尔《世界儿童钢琴名曲集》

这是一本深受国内外儿童喜爱的曲集,其中都是简化了的世界名曲,在训练技术的同时,培养学生对音乐的感受和表达能力。

3、《钢琴小奏鸣曲大全》

小奏鸣曲是我国钢琴教学中运用较为广泛的内容之一。这本大全收入了海顿、克列门第、库劳、莫扎特、杜舍克、贝多芬、迪亚贝里、卡巴列夫斯基、格季凯等大师的小奏鸣曲共48首。程度相当于599后部,849前部到299。

4、《少年儿童钢琴曲选》(1949~1979)

1979年,为了展现建国30周年我国音乐创作的成就,中国音乐家协会在国庆前选编出版了两套中国作品集,这本《曲选》是器乐作品选中的少年儿童钢琴曲专辑。

5、中国风格《儿童钢琴曲选》

由人民音乐出版社编辑部编的,这本曲集共91首,是适合启蒙阶段程度的小曲。

6、少年儿童《钢琴四首联弹曲集》

四首联弹是一种非常好的教学形式,可以训练学生的听觉与合作能力。并且因为四只手弹比两只手弹更丰富,能提高学生的学习兴趣与练习的积极性。本曲集所选的19首曲子,都是以儿童熟悉的歌曲及乐曲改编而成的。

乐曲的范围很广,相应的教材也很多,如柴可夫斯基的《少年钢琴曲集》舒曼的《儿童组曲》《少年曲集》。全音乐谱出版社出版的《您喜爱的钢琴百曲集》第1、2集(共6集),中央音乐学院钢琴系编的《少年儿童外国钢琴曲选》第1、2集,(共6集),都可以广泛的选用。

钢琴教学的中级阶段

钢琴教学的中级阶段是一个比较长的阶段,其中车尔尼《钢琴快速练习曲》作品299程度与车尔尼《钢琴练习曲50首》作品740程度可视为两个台阶。从车尔尼《钢琴流畅练习曲》作品849过渡到299,再从299进入740,中间必须穿插根中各样的练习曲目作为铺垫,特别是740,技术课题很多,无论难度、速度、都不是那么容易掌握的。

这个阶段要进一步加强手指的独立性与灵活性,使手指能快速、均匀、有力地跑动;学习不同的触键,增强指尖的灵敏度,弹奏出不同层次的音色变化;进一步训练手指、手腕、手臂三者的相互配合,逐步掌握各种专门技术。随着技术的提高,曲目范围的扩大,要学习掌握不同时期作品的风格,重视音乐表达能力的培养,在演奏中恰当的运用这些技术来表现音乐。

一、钢琴教学中级阶段的练习曲

1、车尔尼《25首钢琴小手练习曲》748

对于达到中级程度,而手还小的学生来说,这本教材非常适宜,因为曲目中双音最大的跨度为7度。比起车尔尼其他一些简易练习曲来748增加了左手练习,音乐性也更强。

2、车尔尼《钢琴手指灵巧的初步练习曲》636

这是一本比较机械的练习曲集。共24首,每首的篇幅较小可以把它作为进入299的快速练习准备。

3、车尔尼《160首八小节钢琴练习曲》821

初级阶段中已作了介绍,此阶段可在第2、3集里适当选用。

4、车尔尼《钢琴快速练习曲》299

这是本阶段主要教材之一。全书共40首,分为4集,每集10首。学习时应当根据学生的实际情况和不同阶段又针对地加以选择,不要机械的一条条顺着弹。练习299时要在读谱仔细、方法正确、弹奏熟练的基础上加快速度,力求手指颗粒感、清晰度、均匀有力的基本技术都有明显提高。同时,特别要注意训练快速跑动中有意识地训练手腕、手臂与手指的配合。

5、克拉莫《60首钢琴练习曲》

本书注重同时发展左右手的技术,每一首篇幅不大,技术类型集中,包括各种单音、双音、保留音的练习。全书一半以上都是以一种技术类型,分别在左右手训练,为左右手的手指独立性、颗粒性、准确性、灵敏性和加强手指力量的训练提供了更大的可能。

6、车尔尼《钢琴练习曲50首》740

这是中级阶段最高的程度,这本练习曲包含了各种基本技术和专门技巧。在练740时,除了正确熟练等基本要求外,速度上一定要突破。这是,一首练习曲不是短期能完成的往往需要一至两个月或更长的时间。要讲究练习方法,高标准,反复练,使技术有一个飞跃。如果能够将740中的各种技术类型的练习曲都演奏得很出色的话,就能顺利地进入高级阶段的学习。

二、钢琴教学的中级阶段复调作品

以巴赫的《创意曲集》为主,同时可以学习《法国组曲》以及部分《平均律钢琴曲集》作品。

1、《创意曲集》

分二声部与三声部,各15首。学习二声部创意曲,可以选音符与节奏都稍微简单些得先练,如1、4、8、13。

比起二声部三声部的要难得多。首先要抓住主题,脑子里对三个声部的进行要很清楚,然后手指要控制好不同声部的力度对比和音色对比。可以先练6、2、7、1、15。

2、《法国组曲》

这是巴赫于1720年~1724年间完成的古组曲题材的作品,共六组。他的基本结构是由四首速度和节拍不同的舞曲,按一定顺序组成,这四种舞曲是阿列曼德、库朗特、萨拉班德和基格,他们的调性统一每一组前三首的顺序是固定不变的,而在萨拉班德与基格之间,则可插入“小步舞曲”、“旋律”、“加伏特”等其他舞曲或乐曲。这六组中唯一不同的是第四组,在阿列曼德前面多了一首前奏曲。据说“法国组曲”这个名称不是巴赫本人所提,是因为作品的典雅风格类似法国风味而得。

3、巴赫《平均律钢琴曲集》

平均律是一种调律方法,简而言之,就是将8度音程平均分为十二个半音。在巴赫的时代,所使用的调律法基本上是纯律,这很不利于转调,虽然平均律的调律法曾被提出,但直到巴赫才真正运用。《平均律钢琴曲集》的标题用巴赫亲自题的。

《平均律钢琴曲集》共两集,每集24首,是根据十二个半音的规律,由c大调开始,依次以半音进行,每个音用大,小调分别写一字前奏曲与赋格曲,故24首。这部巨著式复调音乐创作的顶峰,被誉为钢琴音乐的“圣经”——“旧约圣经”。

三、钢琴教学的中级阶段的乐曲

1、莫扎特《钢琴奏鸣曲集》

莫扎特一生创作出版了20首钢琴奏鸣曲,在他全部钢琴音乐创作中占有重要位置,也是钢琴教学中学习古典时期音乐的重要教材。

莫扎特的钢琴奏鸣曲多数由三个乐章组成,因各乐章的程度并不完全一致,学习可以先练习技术上音乐上较容易的乐章,最后再练习全部乐章,学习大型作品的整体把握。

2、肖邦的《圆舞曲集》

肖邦的圆舞曲创作从他的学生时代开始,一直延续到生命的最后。圆舞曲在肖邦的全部创作中,虽然不像波洛涅兹舞曲和玛祖卡舞曲那样重要,但却以通俗、华丽的织体,优美、动人的旋律和激昂、振奋的节奏深受钢琴音乐爱好者的青睐。

3、肖邦的《夜曲》

肖邦的夜曲是一种独放异彩的钢琴体裁,情感充实、思想丰富、织体多彩,是肖邦如诗的音乐的集中体现。学习肖邦的夜曲是学会让钢琴歌唱的最有效的途径。

4、格里格《抒情钢琴小品》

挪威作曲家格里格的《抒情小品》共10集,人民音乐出版社的曲集是从这十集里精选出来的。

5、门德尔松《无词歌》

门德尔松共写了49首无词歌,书中收入的是48首,分为8集,每集6首。乐曲充满了清新、优雅抒情的气息,时而轻快活泼、时而优美秀丽,时而哀怨忧伤。这是一本丰富多彩的、如歌谣般的钢琴曲集。

6、舒伯特的《即兴曲》

舒伯特的《即兴曲》共8首。作品90和作品142各4首。他的作品旋律流畅,和声运用自由,转调特别多,常用的有作品90之2、之4。作品142之2、之3、之4。

7、柴科夫斯基《四季》

这是作曲家受彼得堡一家杂志之约,按月为该杂志刊登的12首诗所写的钢琴独奏曲。每一首都有标题,最著名的是六月—–《船歌》和十一月—–《雪橇》。

中级程度前期可选学四月—–《松雪草》、五月——《清静之夜》、三月——《云雀之歌》,后期可选六月—–《船歌》、十月——《秋之歌》、十一月——-《雪橇》几首。

8、贝多芬《钢琴奏鸣曲集》

贝多芬共写了32首钢琴奏鸣曲,被称为钢琴音乐的“新约全书”,是学习古典音乐的必修教材。贝多芬的钢琴奏鸣曲分为3个时期,1794年1800年创作的13首是第一时期,为早期风格;1801年1804年是第二时期,也是作曲家创作的全盛时期,共有14首;最后五首尾晚期作品。中级阶段主要学习第一时期的奏鸣曲,也可以选用一些第二时期的作品练习。

9、魏廷格主编的《中国钢琴名曲曲库》

中国钢琴作品创作的历史虽不长,但是从我国第一首钢琴曲——赵元任先生1915年创作的《和平进行曲》到现在,特别是70年代以后,出现了大批好作品。由魏廷格、李明俊、许明共同编订,由我国著名音乐家贺绿汀提名这套“曲库”是我国第一套比较系统,内容丰富的中国钢琴曲集,一共4卷,95部作品(其中包括组曲),可供独奏选用的140多首。

以上介绍的是专辑,另外,在一些综合曲选中也有不少好的曲目,如人民音乐出版社编的《外国钢琴曲选》(一)中,贝多芬的《六首变奏曲》,博姆《喷泉》;《曲选》(二)中亨德尔的《快乐的铁匠》;曲选(三)中莫扎特的《d小调幻想曲》以及德彪西钢琴曲选中的《阿拉伯风格曲》二首及《月光》。

钢琴教学的高级阶段

如果说钢琴的初级阶段我们侧重于手指技能的训练,学习和掌握各类基本弹奏技术,那么,随着学习的深入,程度的提高,更要注意培养对音乐的理解能力和表达能力。高级阶段的重点应当是在音乐的表现上,从乐曲中学技术,用技术表现音乐。

这个阶段应广泛地接促各种风格、各种类型的作品,扩大曲目范围。

一、钢琴教学高级阶段的练习曲

1、莫什科夫斯基《钢琴技巧练习曲》作品72

N·莫什科夫斯基(1854~1925)波兰钢琴家、作曲家、教育家。

经过中级阶段大量的、比较机械的、纯技术性的训练以后,要进入高级阶段,莫什科夫斯基《钢琴技巧练习曲15首》作品72是一个非常好的过渡。它既有相应的难度,又有较强的音乐性,在练习这些曲子是,尤其要注意句子的长线条进行,音乐起伏、均匀而流畅。

2、莫谢莱斯《钢琴练习曲24首》作品70

L·莫谢莱斯(1794~1870)捷克-德国钢琴家、作曲家、杰出的钢琴教师。

莫谢莱斯《钢琴练习曲24首》作品70乐谱的序言里指出:”作者的初衷不在培养完美的机械的技能,而在倾诉演奏之想象,使他善于处理明、暗细腻层次。。。。”,并且指出四个要点“指触”的说明;连奏奏法;遵照节拍演奏;练习的正确方法。

24首练习的技术课题,几乎都是不同的,最后一首还采用了序曲与赋格的写法。作者在每首练习前都注明了具体的要求和方法。练习之前应当仔细的阅读,学习过程中努力去领会和掌握。

3、克列门蒂《钢琴练习曲选29首》

M·克列门蒂(1752~1832)意大利作曲家、钢琴家。

克列门蒂《钢琴练习曲选29首》又称“名手之道”,“艺术津梁”、“通向艺术的阶梯”。是波兰作曲家陶西格从克列门蒂的100首练习曲中精选的。

作为高难度的练习,克列门蒂的这一教材值得推荐。虽然有不少仍然是技巧性的,但是,对于进一步提高手指对各种技术的适应能力是有效的。特别要指出的是:

①乐谱上每一首练习都标明指法,演奏时应当很好地去研究,有些指法对于手的自然状态或习惯用法来说比较别扭,但作为练习必须这样做。

②在第1、3、4、5、8、9、15、17、18、19、20、26、28、29中,作者都注明了具体的练习方法和要求,应当严格去做。

4、凯斯勒《钢琴练习曲15首》作品20

J·凯斯勒(1800~1870)德国钢琴家。凯斯勒《钢琴练习曲15首》作品20是从他全部练习曲中精选出来的重要部分,是训练高难度技巧的专门练习。

5、车尔尼《160首8小节钢琴练习曲》作品821的第三集(部分)第四集

6、肖邦《练习曲集》作品10、作品25

7、李斯特音乐会练习曲两首之一《森林的呼啸》,之二《侏儒舞》,三首音乐会练习曲之3《叹息》等。

8、李斯特《帕格尼尼练习曲》

9、拉哈马尼诺夫《钢琴音画练习曲》作品33、作品39

10、德彪西《12首练习曲》

11、斯克里亚宾《钢琴练习曲》

二、钢琴教学高级阶段的复调

1、巴赫《英国组曲》

与《法国组曲》的标题一样,《英国组曲》并非巴赫所订名,有人说是受英国人之托而写,为英国人而作,应此得名,也有说是受英国作曲家的影响,以他们的模式来创作的。

《英国组曲》也是属于“古组曲”体裁,由四种基本舞曲组成,在萨拉班德和几个之间仍然可插入各种舞曲。“英国组曲”与“法国组曲”最大的区别在于“英国组曲”的开头都有一篇篇幅较大的前奏曲,而且,在组曲中占了重要地位,确切的说《英国组曲》应当称为“带前奏曲的组曲”,共有六组。

2、巴赫《帕蒂塔》(又称德国组曲,古组曲)

《帕蒂塔》与《法国组曲》《英国组曲》一样,都是巴赫键盘音乐中的精品,且更为成熟,具有明显的意大利风格。这套组曲中歌曲的编排不像另外两套那样固定,比较自由。不局限于舞曲,还用了随想曲、诙谐曲、幽默曲等。一共7组,第7组是明显的法国风格,仍以法国舞曲组成。现在一般乐谱只有6组。

3、巴赫《平均律钢琴曲集》

三、钢琴教学的高级阶段的乐曲

到了这个阶段,应该说是可以自由驰骋在钢琴音乐的天地。钢琴音乐文库犹如浩瀚大海,取之不尽,用之不竭,掌握高级技巧后,学习的曲目就无限广阔。如:

贝多芬的《钢琴奏鸣曲》中的后期作品。

莫扎特《钢琴奏鸣曲》K475、K498等。

《波兰舞曲》第一首作品26之1;第8首作品71之1;第14首#g小调和第16首降G大调等。

《谐谑曲》第2首作品31,第3首作品39等。

李斯特作品《爱之梦》第3首,根据阿拉比耶夫的原作改编的《夜莺》,《威尼斯与那波里》第3首《塔兰泰拉》等,《匈牙利狂想曲》第11首。

舒曼《童年情景》,《蝴蝶》等。

德彪西《前奏曲》

中国作品:

黎英海《夕阳箫鼓》

谢 耿《霓裳羽衣曲》

王建中《百鸟朝凤》

权吉好《组曲—-“长短”的组合》

王立三《东山魁夷画意》

赵晓生《太极》等等。

手指练习

1、李斐岚编著《儿童钢琴手指练习》

李斐岚(1947~)中央音乐学院副教授,钢琴演奏家、教育家。

《儿童钢琴手指练习》是专为儿童而写的练习。本书充分考虑到儿童的特点,左右的练习都在两个8度以内,篇幅比较小,要求精确,易于儿童掌握。

2、哈农《钢琴练指法》

C·L·哈农(1820~1900)法国风琴家,钢琴家。

哈农《钢琴练指法》是我国使用极为普遍的手指练习教材。

3、什密特《钢琴五指练习》作品16

G·A·什密特(1821~1902)德国钢琴家、作曲家。

这本手指练习,分不带保留音和带保留音五指练习。

以上三本教材适合于启蒙阶段,初级阶段即中级阶段使用。

4、科尔托《钢琴技术的合理原则》

5、马格丽特·朗《钢琴技巧练习》

6、李嘉禄《钢琴基本技术练习》

声明:该文观点仅代表作者本人,搜狐号系信息发布平台,搜狐仅提供信息存储空间服务。

下载nodejs版本切换工具nvm

curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash

安装特定版本的nodejs

设置国内node安装文件镜像库

nvm npm_mirror https://npm.taobao.org/mirrors/npm/
nvm node_mirror https://npm.taobao.org/mirrors/node/

安装nodejs

可以安装多个nodejs版本

nvm install 10.14.2

现在可以使用npm和node命令了

镜像切换工具nrm

npm install -g nrm --registry=https://registry.npm.taobao.org
nrm ls
nrm use taobao

Vue环境搭建

新版vue-cli

npm install -g @vue/cli
vue create hello-world

拉取 2.x 模板 (旧版本)

Vue CLI 3 和旧版使用了相同的 vue 命令,所以 Vue CLI 2 (vue-cli) 被覆盖了。如果你仍然需要使用旧版本的 vue init 功能,你可以全局安装一个桥接工具:

npm install -g @vue/cli-init
vue init webpack my-project

这个问题是由于Mysql 8.0的认证方式不是mysql_native_password,需要手动修改

方法一

修改需要登录的用户

ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'YourPassword';
FLUSH PRIVILEGES;

方法二

全局修改,将默认的身份认证插件改为mysql_native_password。

vim /etc/my.cnf

在[mysqld]中添加

default_authentication_plugin=mysql_native_password

重启MySQL

service mysqld restart

最近快播的段子都快传疯了。很多网友也在那里分析快播有没有罪,辩护有没有问题之类的。其实结果对我来说一点都不重要。快播有没有罪,这个留给法庭审判。我今天要说的是庭审所暴露出来的一些问题,一些我日常中经常看到的问题。

我们先看两个段子(其实基本是庭审里的事实):

审判长问:文件加了密,你为什么不解密呢?

张克东:如果达不到一定的码率,快播软件就会启动缓存服务器开始加速,达到了码率,就会自动断开。
法官:软件它为什么会知道?它是机器人么?

也许有些人看到段子1,会喷国家审查啊什么的。其实这不是审查,而是无知。审查一定是懂行的,无知才会要人解密。至于段子2,也有类似的问题——码率的衡量问题在大多数程序员,甚至包括懂一点技术的用户那里都不是秘密。问“软件为什么知道”,实际上非常无知。

甚至同样的问题,也存在于某些网友的评论中。例如这篇。我引用这个评论段落:

我看到不少网友以此取笑法官如何外行。其实法官非常聪明,不管张克东如何回复,都回避不开“软件如何知道”,也就是系统如何识别片源问题,只要这个回答了,你快播就摆脱不了你”清楚”播放哪些视频的问题。

如果懂一点技术的话,应该明白一点。对软件而言,码率一定是知道的。而内容,则未必。或者换个对不了解技术的同学们更友好一点说法。对浏览器而言,网页上的图片的大小,格式,都是知道的对吧?浏览器也可以用来浏览黄色网站对吧?那问题也一样,软件它为什么会知道?

红衣主教,是不是该轮到你出庭聊一聊了?

同样,所有聚焦点在“软件应当可以监管浏览内容”的人,同时也面临这么个问题,浏览器是否可以监管黄色网页?作为处理数据流的软件,肯定是了解被处理数据的格式,大小,码率等参数的。那么处理软件是否应当对被处理的数据的内容合法性负责呢?如果是的话,那毫无疑问,所有黄色信息都必然经过了Intel和AMD两家生产的CPU,而且绝大多数也经过了Windows。

实际上,整个庭审中通篇都是法官和公诉人各种技术外行。例如用IP地址标识服务器,硬盘大小标识硬盘等等。如果在国外正常庭审,这证据其实就算废掉了。因为被告可以当场演示给你看改IP地址,你连服务器是不是他的都无法证明,还怎么构成证据链呢?当然,在中国来说,最后是否取信还是合议庭说了算的。

对于程序员来说,这个故事是不是很熟悉?是不是觉得和产品经理打交道很像?一样是对技术一无所知,却对一无所知不以为耻。我碰到过一个产品经理,自以为提了一个很犀利的需求——为什么我们不通过软件自动算出地址,还要客户自己填?

我听到这个问题的时候,估计表情和听到“为什么不解密”时的表情是一样的。心里一万头草泥马奔腾而过——合着不是你实现啊。尼马我怎么算?IP地址只能知道大致地区,还不一定准。GPS当时还不是每个浏览器都支持,就算支持也只能算出坐标,算不出地址。

很多时候,产品上的一点小小的改变,往往是技术上的翻天覆地。不信你考虑一个论坛系统,如果做到像腾讯那么大流量的时候会是什么样一个样子?再考虑一个交易系统,在同样一个量下是什么样子?前者应该还能做,后者应该已经没法弄了吧。为什么同样是个网站,同样的量,一个没事,一个挂了?

因为论坛系统是典型的无事务低写高读系统,因此可以使用分库分表,多层缓存的方法来增强性能。而交易系统是典型的事务,没法用这个手段。所以交易系统在海量压力下的技术难度,和论坛系统在海量压力下的技术难度,其水平和重点根本不是一类的。从这个意义上说,淘宝双11,很不容易。

而这种技术难度,产品经理是不知道的。所以他搞不好会给一个超大流量论坛提出一个很实在的需求——咱们弄一个内部交易系统吧,应该会变成不错的盈利点。呵呵。这个系统要么做出来会有各种各样的事务问题,要不然这个系统大量使用的时候,整个系统(做的好的话应该不会波及论坛)就会慢到无法接受。

当然,现实中一般是没有这么大论坛的。京东实打实在事务性能上吃过亏没错,但是那时候京东已经多大体量了?一个论坛,要引流过去那么大体量,自身得多大?但是这个例子本身说明技术难题的非直观性和在下判断时的必要性。如果你不了解这个技术本身会引发什么问题,你就无法得到合理的结论。

作为产品经理,或者要和技术打交道的一切人士。不懂技术并不可耻,可耻的是不懂技术又无视技术。既不去学,也不去问。装作技术不会对他产生影响,装作不懂技术并不影响他决策的权威性。

与此形成对比的,是微软垄断诉讼案中法官的表现。那个案子里,微软律师强调的是IE内核和windows系统的严密整合,导致两者几乎无法拆分。从软件架构角度来说,这没错。但是法官很明白里面的门槛——保留IE内核不代表保留IE。鼠标一点,IE直接从系统中干掉。虽然内核还在系统上,但是不额外安装IE的话,无法使用浏览器。如果没有后来的一些事的话,估计微软就难逃一劫了。

话题拉回到快播庭审的事情上来。快播有没有问题?排除色情非罪化(我支持内容分级审查和色情非罪化),我觉得是有的。就好像我们说**的时候,一般都会想到约炮。(由于某些因素,我不能写出这两个字来。但是如果你这两个字和我想的不一样,那你的问题就大了)如果快播没有放任色情传播的话,是很难做到这么大的,更不可能做到我们一说找XX内容就想到快播。这不能说因为检方派了几个猪队友,所以快播干净了。

但是这个问题是不是罪?这不好说。定罪这个事情是个专业性很强的活,我的法律知识不足以下这个判断。但是个人意见,我觉得应该无罪,至少不能拿缓存服务器上的色情信息来定罪。如果缓存服务器上有色情内容就可以定罪的话,那铁通小区宽带就可以直接去转型了。因为他们为了减少对电信的依赖,降低流量结算费。在小区里都是装了缓存服务器的,而且是强制透明缓存,想不做都不行。小区里有没有人看色情网站?他们为什么不人工看?

看,这又是一个不懂技术就不知道我在说什么的问题。对用户来说,可能铁通的感觉就是“不好用”。技术人员才知道,电信和铁通的流量结算价格问题,还有铁通(以及各种小ISP)的透明代理的存在。知道这个透明代理和快播的缓存是不是在技术上是等效的。

还有解密,可行不可行?对于快播来说,要解密应该是可以的。但是对于百度网盘来说,连解密都不行。我把我的一堆生活照在百度网盘上备了个份。为求安全,我用GPG做了加密,然后再用SHA256。我自己倒是问心无愧,但是对百度来说,里面到底是不是色情内容?我觉得百度要是还能解开,不妨把技术卖给CIA。

转自Shell Xu

1986年11月8日,有个叫Aaron Swartz的人在美国芝加哥伊利诺伊州出生。因为他父母创办了一个软件公司,所以,Aaron在3岁的时候就接触到了电脑,然后就着迷了。

我们先通过Aaron Swartz 的青少年时期来看一下他是怎么样的一个天才:

12岁的时候Aaron就创建了一个类似于Wikipedia式的网站(那时还没有Wikipedia),13岁的时候,Aaron赢得为年轻人而设,创作教育及协同非商业网站的ArsDigita Prize比赛首名。 奖品包括参观麻省理工学院及与网际网路界的知名人士见会。

14岁的时候,他就成为了RSS1.0的开发组的一员。(后来,他和 John Gruber一起开发了Markdown)

15岁的时候,进入W3C的 RDF 核心工作组,并写了RFC3870——这个文档描述了一个新的media type – “RDF/XML“,用于定义互联网上的“语义网络

17岁进入斯坦福大学,1年半后,18岁的时候因为受不了教条式的教育缀学,并通过Y Combinator公司的夏季创办人计划成立Infogami软件公司,在那里,他设想了一个Wiki平台来实现他的Internet Open Library——一个开放的网络图书馆。并写了著名的web.py 开发框架。但他觉得自己太年轻,还要有一个合伙人,于是Y Combinator建议他和Reddit合并。于是他在19岁的时候成了Reddit的创始人

虽然Reddit不挣钱,但是相当火,当他20岁的时候(2006年10月),他们把Reddit卖给了Condé Nast出版社,据说挣到了百万美金。然后,他去了这家出版社工作,受不了办公室的那种工作环境,2007年1月离职。

但是,你能想得到这么天才的一个人,于2013年1月11日自杀了么?那年他才26岁。

从前面Aaron的经历我们可以看到,他是一个特别喜欢Wiki的人,也是非常喜欢开放的人,但并不喜欢那些有CopyRight的东西,也也不喜欢那些循规蹈矩的东西,他喜欢质疑,他喜欢打破常规,他用生命坚持着互联网真正的开放精神。但是这样一来,必然会和守旧的世界相冲突。

他在YC搞的那个Internet Open Library(互联网开放图书馆)的项目,他就想把那些没有Copyright的书籍和学术期刊放在网上让全世界的人免费查阅。他就认为固体的图书馆遮蔽了知识的传播,互联网理应成为连接书籍,读者,作者,纸张与思想的最好载体,他非常痛恨任何一家巨型的机构独吞所有书籍的做法。他想把Public Access 变成 Public Domain。在他的青少年时期,他就在不懈地和一切限制信息自由交换和自由共享的做法做斗争。这是他认为的互联网精神,他同时也觉得这和美国民主自由的宪法的精神是一致的。

其中有一个例子是这样的,美国法院行政办公室有一个叫 PACER(Public Access to Court Electronic Records) 的政府服务。这个服务会把法庭记录的文件放在网上,如果你要看的话,一页要付费8美分(注意是每页,不是每个文档,美国政府说这只是成本式的收费),这个事他非常不能理解,他觉得这些文件本来就属于公众,没有CopyRight,为什么属于公众的东西还要收费。PACER这个服务每年可以为政府带来1.2亿美金的收入。

于是Aaron在2008年9月4日到20日,他22岁的时候,他用Perl在AWS上写了一个程序,从PACER上下载了270万的文档(2000万页,纽约时报里说他下载大约是总量的20%,但是也有人不到总量的1%)。于是FBI对他调查了两个多月,但最终没有对他起诉。(今天,PACER还在收费,不过你可以使用一个叫RECAP的Firefox插件来免费浏览当年Aaron下载的相关的法律文档)

2008年同年,Aaron创建了Watchdog.net – “the good government site with teeth” 专门用来收集和呈现和政客相关的数据(这个网站访问不到了,不过你可以在Aaron的blog上看一下他的想法)。然后,他还起草了*Guerrilla Open Access Manifesto*(中文版) 下面是节选

信息就是能源。但就像所有能源一样,有些人只想占为己有。世界上所有的科学和文化遗产,已在书籍和期刊上发布了数个世纪,正渐渐地被少数私有的公司数字化并上锁。想要阅读那些有着最著名研究成果的论文?你必须支付给如 Reed Elsevier 这样的出版商大把钱。

…… ……

我们要夺回信息,无论它们被存在何处,制作我们的副本并和全世界分享。我们要取到版权到期的东西并将它们归档,我们要买下秘密的资料库并将它们放到网上。我们要下载科学期刊并将它们上传到文件分享网络。我们要为游击队开放访问而战。

只要全世界有足够多的我们,那就不仅是传达了一个反对知识私有化的强有力信号,我们还将让它成为过去。你愿意和我们一起吗?

亚伦·斯沃茨 (Aaron Swartz) 2008 年 7 月,意大利 Eremo

Aaron觉得那些对人类有价值的科学和文化遗产属于全人类,美国大学每年会向那些出版学术期刊、论文的机构(比如 ISI,Jstor)支付许可费用,许可费用极高,他觉得这是这个时代的悲剧。于是完美主义的他产生了一种责任感。

2009年,他成立了Progressive Change Campaign Committee(进步改变运动委员会),2010年,他又创建了 Demand Progress (求进会)——利用互联网来组织群众与议会和政府对话。

也因为Aaron并不理解政府和这个时代的这些荒唐的行为,于是他开始学习各种政治上的东西去寻求突破,这让他在2010年到2011年,在哈佛大学Edmond J. Safra研究实验室以Lab Fellow的身份主导到了“制度腐败”课题的研究。也因为这个身份,Aaron在MIT做访问学者的时候有 JSTOR的帐号可以通过MIT的网络访问大量的学术期刊。

于是,他把他的laptop放到了地下室网络交换机的机房中,直接插上网线,然后全天后地下载那些JSTOR的学术期刊。(他利用了这些学术期刊的URL链接中的规律来下载所有的期刊),一开始JSTOR把他的帐号和IP封了,并报告给了警,美国的国家安全警察找到了那间楼道里的机房,然后让JSTOR不禁止他访问,并在那间机房里安了摄像头,钓鱼执法。然后等Aaron去换硬盘时录好像,2011年1月6日就把他给抓了。

那年Aaron才24岁。2011年7月11日,检查官以通信欺诈、计算机欺诈、非法获得信息,以及破坏被保护的罪名电脑来起诉他。可能会受到35年以上的牢狱之灾。这是相当重的罪名。你能想像得到为什么罪名会这么重吗?

事后,JSTOR发声明,说他们并不想起诉Aaron,起诉Aaron的是政府行为,而MIT方面虽然也放弃起诉,并也发表了相关的说明——保持中立。保持中立让MIT基本上名誉扫地,因为这种保持中立的行为违背于MIT一贯鼓吹的黑客文化,MIT成了千夫之指。

当然,美国政府的检查官坚持以重罪起诉他。当时,放在Aaron前有两条路:1)认罪,承认犯下重罪,35年的判决会变成3个月入狱+1年的居家监禁(不得使用电脑),2)不认罪,那就有可能接受35监禁年的最坏结果。Aaron选择了后者,而他的女友则选择了认罪。他的第一任女友后来非常的悔恨,面对国家机器,个体太渺小了。

在起诉期间,大家是否还记得美国那个臭名昭著的SOPA( Stop Online Piracy Act)法案?Aaron通过他的 Demand Progress 把民众们网聚起来,和政府做斗争,最终导致了整个社会都在反对SOPA,也导致了那些议员纷纷改变自己的想法,并导致了白宫最终放弃了这个法案。这是一次民主的胜利,与Aaron有密切的相关。(相信大家都还记得那时美国各大网站都在反对这个网络审查制度)

斯沃茨在2012年反对禁止网络盗版法案(SOPA)的抗议活动上发言 斯沃茨在2012年反对禁止网络盗版法案(SOPA)的抗议活动上发言

而在次年2012年9月,政府对Aaron进行了更为严厉的起诉,新加入了另外9条起诉,如果成立,Aaron最多获刑50年外加100万美金的罚款。同样,检察官给出了优惠条件,只要Aaron认罪,那就只起诉他6个月的监禁。Aaron再次拒绝。

看到这里,你觉得下载一些期刊,也没有挣钱,为什么要判他这么重呢?这后面有什么故事呢?这是不是更像是一种政治迫害呢(这段时间,好像这些消息并没有进入中国,我们的大多数人依然在使用百度在墙内活得很滋润,另外,这个事在美国那边的IT 圈闹得很大,但似乎也不见各个IT圈的老大们有没有什么表态)

不过,可以肯定的是,美国政府受够了像阿桑奇这样的人了,而Aaron让美国政府更为害怕在有规模有组织的事,所以一定少不了相关的政治迫害,天下政府一般黑。

之后,2013年1月11日,Aaron自杀了。大家觉得他是因为来自美国政府的长期恐吓的压力和以及长期的抑郁(理想主义者可能都会有或多或少的抑郁证)

这就是Aaron Swartz传奇的一生。他用他的生命捍卫了互联网的开放和自由。

87d31fea0996abbedb297c70b8b0b945_b

互联网之父,Tim Berners-Lee,在2012伦敦奥运会上的网络环节我们都见过这个人。世界上第一个web网站是1991年8月6日在CERN内的NeXT服务器上运行(今天这个网站依然可以访问:链接),Tim并被没有用这个发明挣钱,而是无偿地把WWW的构想和设计推广给了全世界。《时代》周刊评论他的时候用了这样的一条话:“与所有的推动人类进程的发明不同,这是一件纯粹个人的劳动成果”。

而Aaron最崇拜的人就是Tim,Tim也是Aaron的精神导师。

Aaron死了以后,Aaron朋友和合作者,哈佛大学法学院教授Laurence Lessig,回忆说,他当年和仅15岁的Aaron 有过一次谈话。Aaron问他:“您刚才讲到网络审查和管制的这些弊病,那您有没有什么实际的方案来解决这些问题呢?”Lessig有点尴尬地说:“没有。我是个学者,我只负责做研究,解决问题不关我的事儿。”Aaron接着问:“您是个学者,所以解决问题不关你的事儿。那,您作为一个公民,又该如何呢?”

有个男孩叫 Jack Andraka,来自巴尔的摩,14岁,阅读了 Aaron 自杀前推广的JSTOR 的免费学术论文,想出了一种提早检测胰腺癌的方法(一般胰腺癌被查出的时候就是你死的时候。)以此,他成功去了约翰霍普金斯大学做研究。Jack说——

“我之所以上了新闻,是因为我们的实验成功了,而这就是为什么 Aaron 做的事有那么重要……这个宇宙中的真理不是只有那些政策制定者曾经弄清楚过的,比如应该限速多少,它还包括那些能让你的孩子,不会因胰腺癌而死的研究。如果没有访问阅读权,那个能解决你的问题的人,可能就永远找不到答案。”

强烈推荐纪录片——《互联网之子

Aaron说的一句话让我挺有感触的——

相信你应该真的每时每刻都问自己,现在这世界有什么最重要的事是我能参与去做的?

如果你没在做那最重要的事,那又是为什么?

aaron_swartz__freedom_fighter_by_caq_qoq-d5rzbi8

延伸阅读偷了世界的程序员

(全文完)

来源:**酷 壳 – CoolShell**

很多朋友问我为什么不在微信公众号上写文章。我都没有直接回答,老实说,我也是扭扭捏捏的,才去开了个个人的微信的公众号,而且还只能搜索我博客这边的文章,我承认现在的阅读都在移动端,而且微信的公众号是国内移动端的文章流量及分享的入口,但是我还是更愿意使用blog这样的方式分享文章,最多也是在blog这边写好文章后,再去微信公众号那边贴一下。这个原因,不是因为我是一个老顽固,有习惯思维,而是,我不觉得微信公众号是一个好的信息传播和交流的平台。

我下面的言论仅仅代表我的个人观点,我不想强加给别人,我只是想说明一下为什么我不把我的blog迁移到微信公众号上。

首先,互联网是开放和共享的,不是封闭的。信息的传播更是需要开放的,大家可以看看互联网之子

  • 我希望我的文章能够被rss feed到各种阅读器中。
  • 我希望我的文章可以被搜索引擎所检索到。
  • 我希望我的文章能被别人整理,与其它人的文章放在一起互补。
  • 我希望我的文章能被修改,因为文章会有错误,也会需要时常更新。

然而,微信公众号都不能很好的支持。我希望我的文章能成为生态圈的里的一部份。所谓生态圈是相互融合,不是唯我独尊。这个和做开源软件的道理一样,开源软件不是把源代码开出来就好了,而是要去和已有的其它软件互相融合,互相兼容,互相支持,这本就是软件设计的真谛(参看《UNIX编程艺术》)。所以,我想,写文章也一样。

下面是我觉得文章传播的姿势。

文章传播的姿势

我希望我的文章是被检索的,这意味着,就算文章写过了好多年,它依然可以被检索到,而不是在社交圈上被大众转了3-4天后就完了,然后再也没有然后了

今天,我十多年前写的文章依然可以被检索到,依然对后来的新人有帮助。因为我的文章被搜索引擎检索了,我的文章被转载fork出去了,被人引用和标注,所以,可以长期被传播。

今天的酷壳(CoolShell.cn)已经很长时间没有更新了,然而里面的很多文章依然在被转发着,在被搜索着,在被重复阅读和被人推荐着,文章不断的被后来的人阅读。这就是被检索被共享被转载的好处。

同时,我并不希望成为某个平台写文章的苦力。在微信公众号下,你需要不断的更新才会积累起人气(订阅者),而文章的保鲜期非常有限,因为不能被检索,所以,你写的越多,你过去的文章也会被遗忘的越快。而微信公众平台让能写文章的人好像成为了这个平台的一个写作的奴隶,而不是让他们的文章中的内容和观点可以有长时间的影响力。换言之,在社交网络上,如果你要有影响力,你就要使劲写,需要更多的粉丝和订阅者。我个人认为这是违反了信息传播规律的。

最重要的是,我希望我的文章和观点是有讨论的,希望我的文章能被指正和批评,最好是引发讨论和思辨,这样才会让我们每一个人都可以在交流中成长。很多时候,文章本身并没有什么太大的价值,而引发的讨论和思辨才更有价值,这是我认为文章传播最正确的姿势。而微信的公众号在讨论方面人为的阻止或大大消弱了大家的沟通和讨论。虽然我承认有些讨论也是无效的,而且还有漫骂和跑题,但是我依然觉得讨论的利大于弊。

我私以为,信息的传播正确姿势,是被检索、讨论、引用、整理、补充和更新,而不是社交网络的转发、点赞、粉丝、订阅和打赏

换句话说,我关注的是的文章的长期价值,而不是短期的表象

关于文章的版权

很多人认为,封闭的平台有个比开放平台天然的优势,就是盗版和抄袭的问题,可以通过平台举报和惩罚对方。我以前也受到一些被抄袭和盗用的困扰,还曾经拿起来法律的武器维护自己的权利。

可能是我经历这样的事情比较早,所以,我今天在这个问题上不纠结了。

1、好的有价值的文章总是被人盗用抄袭的,这也算是对作者的一种认可吧。

2、我从来没有见过有人靠抄袭和盗用别人文章而成功的,无非就是收获几个赞罢了。

3、原创文章被人过抄袭和盗用,反而容易得到更多的关注。

微信公众号的原创保护也只是局限在微信上,微信之外的平台,它也无能为力,所以,对于我的文章会被转到很多地方的这种情况来看,微信公众号的原创保护也非常有限。

现在,我倒是不纠结有人会盗用和抄袭我的文章,因为,一方面,你可以有一些小伎俩来保护你的文章,比如在文章内容中放入一些你自己特有的标识,另一方面,我的文章被人盗用了抄袭的时候,总有一些网友能在盗用者那边指出来原文章是什么,并批评之。所以,还是应该把主要精力放在文章的内容和质量上,并让文章可以被检索和被更多的地方所引用,这样,你的文章才会得到最大的保护

另外,既然别人对我的文章有抄袭和盗用的需求,要不我就让别人干得更体面一些。所以,我的文章完全可以自由的转载,但不得用于商业目的,只需要注明作者和出处就好了

关于写文章挣钱

首先,如果你觉得写文章出书是可以挣钱的,那么你可以洗洗睡了,尤其是在中国,这几乎是不可能的。

当然,你要挣点小钱是可以有的,但是,你需要写软文中植入广告,或是消费热点主题,比如前段时间的魏则西事件,有的公众号被打赏了一些钱。

老实说,这对我来说完全无感,因为,我的逻辑是这样的:我觉得一个人有一定的影响力,其中有很大一部份原因来自他的独立性,如果我开始写软文了,相当于我把自己卖了

所以,我现在从来不通过写文章挣钱,因为我会写代码,我还是通过我的技能挣钱,通过给一些公司做咨询、培训、企业服务挣钱,老实说,靠自己的能力挣钱,比写文章挣钱挣得多多了,因为我觉得,像我写的这类的文章本来就是用来分享和传播的,不是用来挣钱的。写文章的目的是分享和影响,不是挣钱。

关于独立性,这里说两个花絮吧——

我在Amazon的时候,我和公司讲,我想在我的博客上写几篇关于亚马逊的文章,介绍亚马逊的技术和一些做事的方法,也算是个宣传,让我的团队也好招人,但是,我当时的老板和我说,你的博客之所以有影响力是因为你的独立性,不要写亚马逊的,这样会把你自己卖了,千万别这么做。

然而,我在Alibaba的时候,我的大老板要求我用我的博客和微博帮阿里云做营销,我非常委婉地拒绝了,结果,团队合作的价值观不达标了。呵呵。

P.S. 本来酷壳上是不做广告的,今天,酷壳上也广告,但是广告费是全部捐给Wikipedia的,广告主的钱是没有到我的手的。

微信公众号上的文章都有软文和广告植放,我觉得这不是我的调调,我害怕微信公众平台的整体格调影响了我的格调。就好像我认为我的网络被百度检索到了会我的网站的格调下降好几个档次。所以,我还是保持一定的距离吧。

这么说吧,我写文章不是为了挣钱,我也不认为写文章能挣到钱,我写文章就是为了分享和影响,我会借助社交网络,但不会寄宿在社交网络上,更不会被社交网络所绑架。

谢谢看我的唠叨!

(全文完)

来源:**酷 壳 – CoolShell**

C++讲义

第二章 变量和基本类型

wchar_t

在字符字面前加L就能够得到wchar_t类型的宽字符字面值

1
L'a'

const

const 限定符把一个对象转化为一个常量

1
const int bufSize = 512;

任何修改bufSize的尝试都会导致编译错误i,定义时必须初始化。

const默认为文件的局部变量

1
2
3
4
5
6
//file_1.cpp
int counter;

//file_2.cpp
extern int count;
++counter;

除特别声明,在全局作用域声明的const变量是定义该对象文件的局部变量,不能被别的文件访问。

1
2
3
4
5
6
//file_1.cpp
extern const int bufSize = fcn();

//file_2.cpp
extern const int bufSize; //在这里bufSize的声明同样是extern; 这样extern 标志着bufSize是一个声明,所以没有初始化式
a = bufSize; //使用文件1中的bufSize

引用

引用就是对象的另一个名字,在实际程序中主要用作函数的形式参数。

引用是一种复合类型,在变量名前“&”来定义,不能定义引用类型的引用,但是能定义任何其他类型的引用

1
2
3
4
int ival = 1024;
int &refVal = ival; //√
int &refVa2; //× 必须初始化
int &refVa3 = 10; //× 初始化必须是对象

const 引用

const引用是指向const对象的引用:

1
2
3
const int ival = 1024;
const int &refVal = ival;//√
int &ref2 = ival;//× 会导致能够对ival进行修改

可以读取但是不可以修改refVal,因此对refVal的赋值都是不合法的。

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
#include <iostream>
int main()
{
int i, &ri = i;
i = 5; ri = 10;
std::cout << i << " " << ri << std::endl; return 0; } ``` &gt; 52页</iostream>

const引用可以初始化为不同类型的的对象或者初始化为右值

​```c
int i = 1024;
const int &r = 1024;//对比前面
const int &r2 = r + i;//因为只读
</code></pre>

<h2>第三章 标准库类型</h2>

<h3>标准string类型</h3>

string类型支持长度可变的字符串

string s1(n,'c')//表示将s1初始化为字符‘c'的n个副本

<blockquote>
由于历史原因以及为了与C语言兼容,字符串字面值与标准库string类型不是同一类型。
</blockquote>

<pre><code class="c">int main()
{
string line;
while(getline(cin,line))//一个输入流一个string对象
cout << line << endl; return 0; //由于getline 函数返回时丢弃换行符,换行符将不会存储在string对象中 } ``` ### string对象的操作 - s.empty() //如果是空串返回true - s.size() //串字符的个数 不要把size的返回值赋给一个int变量(unsigned) ### string对象中字符的处理 ``#include&lt;cctype&gt;`` - isalnum(c) //c是字母或数字 返回true - isalpha(c) //c是字母 - isgraph(c) //c不是空格,但可打印 - tolower(c)//如果c是大写则返回其小写字母,否则返回c - toupper(c) ### 标准vector类型 &gt; P78

vector是同一种类型的对象的集合,每个对象都有一个对应的整数索引值。

一个容器中所有对象必须是同一种类型的

- `vector&lt;int&gt; ivec;` 和其他变量定义一样,定义vector对象要指定类型和一个变量列表(int 类型对象vector 变量名为ivec)
- `vector&lt;T&gt; v(n,i)` v包含n个值为i的元素
- `vector&lt;int&gt; fvec(10)`

### vector 对象的操作

- v.empty()
- v.size()
- v.push_back(t)

大家认为vector的下标操作可以添加元素,以下是错误

​```c
vector<int> ivec
for(vector<int>::size_type ix=0;ix=0;ix != 10; ++ix)
ivec[ix] = ix;
​```</int></int>

但是ivec是vector对象,而且下标只能用于获取已存在的元素

正确写法:

​```c
for(vector<int>::size_type ix=0;ix!=10;++ix)
ivec.push_back(ix);
​```</int>

> `const char*`与`char const*`效果一样,都是不允许修改指针指向的地址空间的值,即把值作为常量,而`char * const`则是不允许修改指针自身,不能再指向其他地方,把指针自己当作常量使用。需要注意的是,使用`char * const`定一个常量指针的时候一定记得赋初始值,否则再其他地方就没法赋值了。

​```c
#include <iostream>
using std::cout;
using std::endl;</iostream>

int main()
{
const char *st = "The expense of spirit\n";
int len = 0;
while (*st) { ++len; ++st; }
cout << len << ": " << st << endl;
return 0;
}
</code></pre>

<pre><code class="c">#include <iostream>
using std::cout;
using std::endl;</iostream>

const char *st = "The expense of spirit\n";

int main()
{
int len = 0;
const char *p = st;
while (*p) { ++len; ++p; }
cout << len << ": " << st << endl;
return 0;
}
</code></pre>

<pre><code class="c">#include <string>
using std::string;
#include <iostream>
using std::cout; using std::endl;</iostream></string>

int main()
{
string substr = "Hello";
string phrase = "Hello World";
string slang = "Hiya";

if (substr < phrase) cout << "substr is smaller" << endl; if (slang > substr) cout << "slang is greater" << endl; if (slang > phrase) cout << "slang is greater" << endl;

return 0;
}
</code></pre>

<pre><code class="c">#include <iostream>
#include <vector></vector></iostream>

using std::cout;
using std::endl;
using std::vector;

int main()
{
// empty vector
vector<int> ivec;
int val;
// give each element a new value
for (vector<int>::size_type ix = 0;
ix != 10; ++ix)
ivec.push_back(ix);</int></int>

cout << "ivec.size: " << ivec.size() << endl; // prints 10

// reset the elements in the vector to zero
for (vector<int>::size_type ix = 0; ix != ivec.size(); ++ix)
ivec[ix] = 0;</int>

// is there anything to print?
if (ivec.empty() == false) {
cout << "vector contents: " << endl;
// print each element separated by a newline
for (vector<int>::size_type ix = 0;
ix != ivec.size(); ++ix)
cout << ivec[ix] << endl;
}
return 0;
}
​```</int>

​```c
#include <vector>
#include <string>
#include <iostream></iostream></string></vector>

using std::vector;
using std::string;
using std::cin;
using std::cout;
using std::endl;

int main()
{
vector<int> ivec(10);</int>

// reset all the elements in ivec to 0
for (vector<int>::size_type ix = 0; ix != ivec.size(); ++ix)
ivec[ix] = 0;</int>

// print what we've got so far: should print 10 0's
for (vector<int>::size_type ix = 0; ix != ivec.size(); ++ix)
cout << ivec[ix] << " ";
cout << endl;</int>

// equivalent loop using iterators to reset all the elements in ivec to 0
for (vector<int>::iterator iter = ivec.begin();
iter != ivec.end(); ++iter)
*iter = 0; // set element to which iter refers to 0</int>

// print using iterators: should print 10 0's
for (vector<int>::iterator iter = ivec.begin();
iter != ivec.end(); ++iter)
cout << *iter << " "; // print the element to which iter refers
cout << endl;</int>

vector<int>::iterator iter = ivec.begin();
while (iter != ivec.end()) {
*iter = 0;
++iter;
}
return 0;
}
​```</int>

## 迭代器

> P84

除了使用下标来访问vector对象的元素外,标准库还提供了另外一种访问元素的方法。迭代器是一种检查容器内元素并遍历元素的数据类型。

1. 容器的iterator类型
vector&lt;int&gt;::iterator iter;//定义了一个名为iter的变量,它的数据类型是由vector&lt;int&gt;定义的iterator类型。

2. begin和end操作
end操作返回的迭代器并不指向vector中的任何实际元素,哨兵的作用。

3. 应用程序示例
​```c
for (vector<int>::iterator iter = ivec.begin();iter !=ivec.end(); ++iter)
*iter = 0;
​```</int>

4. const_iterator
该类型只能用于读取容器内的元素,但不能修改他的值。

const iteator的区别:
使用const_iterator类型时,得到一个迭代器,自身值是可以改变的,但不能用来改变其所指向的元素的值(可以自增但不能赋值)

const 的迭代器必须初始化迭代器,初始化后不能改变。

​```c
vector<int> nums(10);
const vector <int>::iterator cit = nums.begin();
*cit =1;
++cit;//×
​```</int></int>

const迭代器几乎没什么用,一旦初始化后只能用它来改写其指向的元素但不能指向别的元素。

## bitset

> P88

- `bitset&lt;32&gt; bitvec` 初始化32位,每位都是0
- `bitset&lt;n&gt; b(u)` b是unsigned long类型的一个副本
- `bitset&lt;n&gt; b(s)`
- `bitset b(s,pos,n)` b是s中从位置pos开始的n个位副本
- `string strval("1100")`
- `bitset &lt;32&gt; bitset4(strval)` 高阶置0,反向转化的
- `b.any()`
- `b.none()`
- `b.count()`
- `b.size()`
- `b[pos]`
- `b.set()` `b.set(pos)`
- `b.reset()`
- `b.flip()`

​```c
#include &lt;cstddef&gt;
#include &lt;iostream&gt;
#include &lt;string&gt;
using std::cout;
using std::endl;
using std::string;
using std::size_t;

#include &lt;bitset&gt;
using std::bitset;
int main()
{
bitset&lt;32&gt; bitvec; // 32 bits, all zero
bool is_set = bitvec.any(); // false, all bits are zero
bool is_not_set = bitvec.none(); // true, all bits are zero

cout &lt;&lt; "bitvec: " &lt;&lt; bitvec &lt;&lt; endl;

size_t sz = bitvec.size(); // returns 32

size_t bits_set = bitvec.count(); // returns number of bits that are on

// assign 1 to even numbered bits
for (int index = 0; index != 32; index += 2)
bitvec[index] = 1;

// equivalent loop using set operation
for (int index = 0; index != 32; index += 2)
bitvec.set(index);

unsigned i;

cout &lt;&lt; "bitvec: positions turned on:\n\t";
for (int index = 0; index != 32; ++index)
if (bitvec[index])
cout &lt;&lt; index &lt;&lt; " ";
cout &lt;&lt; endl;

// equivalent; turn off first bit
bitvec.reset(0);
bitvec[0] = 0;

bitvec.reset(); // set all the bits to 0.
bitvec.set(); // set all the bits to 1

bitvec.flip(0); // reverses value of first bit
bitvec[0].flip(); // also reverses the first bit
bitvec.flip(); // reverses value of all bits

// leaves bitvec unchanged; yields a copy of bitvec with all the bits reversed
bitvec = ~bitvec;

// bitvec1 is smaller than the initializer
bitset&lt;16&gt; bitvec1(0xffff); // bits 0 ... 15 are set to 1

// bitvec2 same size as initializer
bitset&lt;32&gt; bitvec2(0xffff); // bits 0 ... 15 are set to 1; 16 ... 31 are 0

// on a 32-bit machine, bits 0 to 31 initialized from 0xffff
bitset&lt;128&gt; bitvec3(0xffff); // bits 32 through 127 initialized to zero
cout &lt;&lt; "bitvec1: " &lt;&lt; bitvec1 &lt;&lt; endl;
cout &lt;&lt; "bitvec2: " &lt;&lt; bitvec2 &lt;&lt; endl;
cout &lt;&lt; "bitvec2[0] " &lt;&lt; bitvec2[0] &lt;&lt; endl;
cout &lt;&lt; "bitvec2[31] " &lt;&lt; bitvec2[31] &lt;&lt; endl;
cout &lt;&lt; "bitvec3: " &lt;&lt; bitvec3 &lt;&lt; endl;

string strval("1100");
bitset&lt;32&gt; bitvec4(strval);

cout &lt;&lt; "strval: " &lt;&lt; strval &lt;&lt; endl;
cout &lt;&lt; "bitvec4: " &lt;&lt; bitvec4 &lt;&lt; endl;

{
string str("1111111000000011001101");
bitset&lt;32&gt; bitvec5(str, 5, 4); // 4 bits starting at str[5], 1100
bitset&lt;32&gt; bitvec6(str, str.size() - 4); // use last 4 characters

cout &lt;&lt; "str: " &lt;&lt; str &lt;&lt; endl;
cout &lt;&lt; "bitvec5: " &lt;&lt; bitvec5 &lt;&lt; endl;

cout &lt;&lt; "str: " &lt;&lt; str &lt;&lt; endl;
cout &lt;&lt; "bitvec6: " &lt;&lt; bitvec6 &lt;&lt; endl;
}

{
unsigned long ulong = bitvec3.to_ulong();
cout &lt;&lt; "ulong = " &lt;&lt; ulong &lt;&lt; endl;
}

bitset&lt;32&gt; bitvec7 = bitvec2 &amp; bitvec4;

cout &lt;&lt; "bitvec7: " &lt;&lt; bitvec7 &lt;&lt; endl;

bitset&lt;32&gt; bitvec8 = bitvec2 | bitvec4;

cout &lt;&lt; "bitvec8: " &lt;&lt; bitvec8 &lt;&lt; endl;

cout &lt;&lt; "bitvec2: " &lt;&lt; bitvec2 &lt;&lt; endl;
return 0;
}

第四章 数组和指针

指针的定义和初始化

string* 错误的理解成一种数据类型

string* ps1,ps2 实际上是ps1是指针,ps2是一个普通的string对象

未初始化的指针是无效的,直到给该指针赋值后,才能使用它

赋值操作的约束

int型变量赋值给指针式非法的,尽管这个int型变量的值可能是0,允许把数值0或在编译时可获得0值的const量赋值给指针

1
2
3
4
5
6
7
int ival;
int zero = 0;
const int c_ival = 0;
int *pi = ival;(×)
pi = zero;(×)
pi = c_ival;
pi = 0;

从C继承下来的预处理变量:

1
int *pi = NULL;

void* 指针

C++ 提供了一种特殊的指针类型 void*,它可以保存任何类型对象的地址:

1
2
3
4
double obj = 3.14;
double *pd = &obj;
void *pv = &obj;
pv = pd;

void* 指针只支持几种有限的操作:与另一个指针进行比较;向函数传递void* 指针或从函数返回 void* 指针;给另一个 void* 指针赋值。

不允许使用void* 指针操纵它所指向的对象。

指针操作

指针和引用的比较

第一个区别在于引用总是指向某个对象:定义引用时没有初始化是错误的。第二个重要区别则是赋值行为的差异:给引用赋值修改的是该引用所关联的对象的值,而并不是使引用与另一个对象关联。引用一经初始化,就始终指向同一个特定对象。

指针和const限定符

介绍了指针和 const 限定符之间的两种交互类型:指向 const对象的指针和 const 指针。

指向const 对象的指针

我们使用指针来修改其所指对象的值。但是如果指针指向const 对象,则不允许用指针来改变其所指的 const 值。

为了保证这个特性,C++ 语言强制要求指向 const 对象的指针也必须具有 const 特性:

1
const double *cptr;

这里的 cptr 是一个指向 double 类型 const 对象的指针,const 限定了cptr 指针所指向的对象类型,而并非 cptr 本身。也就是说,cptr 本身并不是const。在定义时不需要对它进行初始化,如果需要的话,允许给 cptr 重新赋值,使其指向另一个 const 对象。但不能通过 cptr 修改其所指对象的值:

不能使用 void* 指针保存 const 对象的地址,而必须使用 const void* 类型的指针保存 const 对象的地址:

1
2
3
const int universe = 42;
const void *cpv = &universe; // ok
void *pv = &universe; // error

允许把非 const 对象的地址赋给指向 const 对象的指针,例如:

1
2
double dval = 3.14;
cptr = &dval;

但是同样必须遵循不能通过cptr修改对象的值

const指针

const指针指的是指针本身的值不能改变

指向const对象的const指针

1
2
const double pi = 3.14159;
const double *const pi_ptr = π

既不能修改 pi_ptr 所指向对象的值,也不允许修改该指针的指向。

指针和 typedef (讨论)

下面是一个几乎所有人刚开始时都会答错的问题。假设给出以下语句:

1
2
typedef string *pstring;
const pstring cstr;

请问 cstr 变量是什么类型?

简单的回答是 const pstring 类型的指针。

进一步问:const pstring 指针所表示的真实类型是什么?很多人都认为真正的类型是:

1
const string *cstr;

也就是说,const pstring 是一种指针,指向 string 类型的 const 对象,但这是错误的。

错误的原因在于将 typedef 当做文本扩展了。声明 const pstring 时,const 修饰的是 pstring 的类型,这是一个指针。因此,该声明语句应该是把cstr 定义为指向 string 类型对象的 const 指针,这个定义等价于:

1
string *const cstr;

创建动态数组

每一个程序在执行时都占用一块可用的内存空间,用于存放动态分配的对象,此内存空间称为程序的自由存储区或堆。

const 对象的动态数组

如果我们在自由存储区中创建的数组存储了内置类型的 const 对象,则必须为这个数组提供初始化:因为数组元素都是 const 对象,无法赋值。实现这个要求的唯一方法是对数组做值初始化:

1
2
const int *pci_bad = new const int[100];(×)
const int *pci_ok = new const int[100]();(√)

C++ 允许定义类类型的 const 数组,但该类类型必须提供默认构造函数:

1
const string *pcs = new const string[100];

已创建的常量元素不允许修改――因此这样的数组实际上用处不大。

第五章 表达式

箭头操作符

1
->` 相当于 `(*sp)

const对象的动态分配和回收

1
const int *pci = new const int (1024);

与其他常量一样,动态创建的const对象必须在创建时初始化,并且一经初始化,其值就不能修改。

对于类类型的const动态对象,如果该类提供了默认构造函数,则此类可以隐身初始化,但是内置类型对象以及未提供默认构造函数的类类型对象必须显示初始化。

有符号与无符号类型之间的转换

如果int类型足够表示所有的unsigned short型的值,则将unsigned short转换为int,否则将这两个操作数转换为 unsigned int.

同理 unsigned intlong转换为unsigned long.

Git讲义

讲义内容参考:http://www.cnblogs.com/wilber2013/p/4189920.html

Git简介

Git是一个分布式版本控制工具

集中式版本控制

一个系统中只有一个机器是服务端,其他机器全是客户端。

缺点:

  • 网络依赖性强,工作环境保持网络连接,如果网络断掉了,所有的客户端就无法工作了。

  • 安全性较弱,所有的代码以及版本信息保存在服务器中,一旦服务器挂掉了,代码和版本控制信息就丢失了。

分布式版本控制

没有服务端/客户端的概念,每台机器都是一个服务器。但是一般选一个机器作为中心服务器,方便大家交换更新。

优点:

  • 每台机器都是一台服务器,无需依赖网络就可以帮自己的更新提交到本地服务器,支持离线工作。当有网络环境的时候,就可以把更新推送给其他服务器。

  • 安全性高,每台机器都有代码以及版本信息的维护,所有即使某些机器挂掉了,代码依然是安全的。

Git下载

Git刚开始只能支持Linux和Unix环境,后来才慢慢的支持Windows系统。

主程序:Git

Windows下可视化界面:TortoiseGit

Git安装后的配置

安装完成后,一般都会对本机的Git进行一些基本的配置。下面的命令就是给Git环境配置全局的用户名和邮箱地址,这样每一个从这台机器上提交的更新都会标上这些用户信息。

  • 当第一次安装完Git时

设置全局名字:设置成自己在GitHub上的账号显示名

1
2
3
4
# 设置名字
$ git config --global user.name "你的名字"
# 设置邮箱
$ git config --global user.email "你的邮箱"
  • 当每一次创建项目时

设置项目中自己的名字:这里设置的名字是针对本项目的,本地设置的user.name要和服务器上的账号显示名一致

1
2
3
4
# 设置名字
$ git config user.name "你的名字"
# 设置邮箱(邮箱与全局相同可以不设置)
$ git config user.email "你的邮箱"

在git设置中注意关闭忽略大小写,这一项貌似设成全局的不管用,所以每个项目创建时都要设置一下

1
2
#关闭忽略大小写
git config core.ignorecase false

尽量使用UTF-8编码

Git基本概念

在Git中,每个版本库都叫做一个仓库(repository,缩写repo),每个仓库可以简单理解成一个目录,这个目录里面的所有文件都通过Git来实现版本管理,Git都能跟踪并记录在该目录中发生的所有更新。

我们现在建立一个仓库(repo),那么在建立仓库的这个目录中会有一个”.git”的文件夹。这个文件夹非常重要,所有的版本信息、更新记录,以及Git进行仓库管理的相关信息全都保存在这个文件夹里面。所以,不要修改/删除其中的文件,以免造成数据的丢失。

创建仓库

通过”Git Bash”命令行窗口进入到想要建立版本仓库的目录,通过git init就可以轻松的建立一个仓库。

添加

先编写一个文件reader.txt

通过git status可以查看WorkSpace的状态,看到输出显示reader.txt没有被Git跟踪,并且提示我们可以使用”git add …”把该文件添加到待提交区(暂存区)。

1
2
git add reader.txt
git add .

通过git status可以查看WorkSpace的状态,这时的更新已经从WorkSpace保存到了Stage中。

1
git commit -m "xxx"

xxx是对此操作的描述,这时的更新已经又从Stage保存到了Local Repo中。

更新

修改文件reader.txt

修改文件后,查看WorkSpace的状态,会发现提示文件有更新,但是更新只是在WorkSpace中,没有存到暂存区中。

同样,通过add、commit的操作,我们可以把文件的更新先存放到暂存区,然后从暂存区提交到repo中。

git diff

通过”git diff”来查看WorkSpace和Stage的diff情况,当我们把更新add到Stage中,diff就不会有任何输出了。

当然,我们也可以把WorkSpace中的状态跟repo中的状态进行diff,命令如下,关于HEAD,将在后面解释。

1
$ git diff HEAD~n

撤销更新

根据前面对基本概念的了解,更新可能存在三个地方,WorkSpace中、Stage中和repo中。下面就分别介绍一下怎么撤销这些更新。

  • 撤销WorkSpace中的更新

即:暂存区有内容,即有未commit的内容,此操作是将暂存区覆盖到WorkSpace,即将最后没add的内容取消

1
$ git checkout -- filename

注意,在使用这种方法撤销更新的时候一定要慎重,因为通过这种方式撤销后,更新将没有办法再被找回。

  • 撤销Stage中的更新

即:add完成,但是还没commit

1
$ git reset HEAD filename

此时,就把暂存区的更新移出到WorkSpace中。如果想继续撤销WorkSpace中的更新,请参考上面一步。

  • 撤销repo中的更新

即:commit操作完成

查看历史记录:通过下面的命令我们可以查看commit的历史记录

1
$ git log

撤销commit有两种方式:使用HEAD指针和使用commit id

HEAD指针指向当前分支中最新的提交:

1
2
$ git reset --hard HEAD^
$ git reset --hard 一长串数字

注:当前版本,我们使用”HEAD^”,那么再前一个版本可以使用”HEAD^^”,如果想回退到更早的提交,可以使用”HEADn”。(也就是,HEAD^=HEAD1,HEAD^^=HEAD~2)

  • 恢复撤销repo中的更新
1
$ git reflog

git reflog 查看记录在这个仓库中的所有分支所有更新记录,包括已经撤销的更新

注:在”.git/logs”文件夹内有一个HEAD文件和refs目录,这些就是记录reflog的地方

git log 查看当前分支中的commit记录

有了reflog,我们就可以查找到刚才撤销的提交,然后可以通过下面命令来恢复撤销操作。

1
2
$ git reset --hard HEAD@{1}
$ git reset --hard 一长串数字

–hard和–soft

–hard:撤销并删除相应的更新
–soft:撤销相应的更新,把这些更新的内容放的Stage中

删除文件

1
$ git rm

Git对象模型

Git对象

在Git系统中有四种类型的对象,所有的Git操作都是基于这四种类型的对象。

  • “blob”:这种对象用来保存文件的内容
  • “tree”:可以理解成一个对象关系树,它管理一些”tree”和 “blob”对象
  • “commit”:只指向一个”tree”,它用来标记项目某一个特定时间点的状态。它包括一些关于时间点的元数据,如时间戳、最近一次提交的作者、指向上次提交(初始commit没有这一项)
  • “tag”:给某个提交(commit) 增添一个标记

SHA1哈希值

在Git系统中,每个Git对象都有一个特殊的ID来代表这个对象,这个特殊的ID就是我们所说的SHA1哈希值。

SHA1哈希值是通过SHA1算法(SHA算法家族的一种)计算出来的哈希值,对于内容不同的对象,会有不同的SHA1哈希值。前面讲的根据commit id撤销更新的,这里的commit id就是一个SHA1哈希值。

Git对象模型实例

    1. 新建一个仓库,添加一个文件

通过git log --pretty=raw可以得到每个commit的SHA1哈希值,也可以得到这个commit对应的tree的哈希值。

所以,一个commit对象一般包含以下信息:代表commit的哈希值、指向tree 对象的哈希值、作者、提交者、注释

在Git对象模型的研究中,有一个很有用的命令git cat-file,可以通过这个命令查询特定对象的信息:

1
2
3
4
#通过一个对象的哈希值查看对象的类型(blob、tree、commit或tag)
$ git cat-file -t commit_id
#通过对象的哈希值可以查看这个对象的内容
$ git cat-file -p commit_id
  • 2.更新文件,添加新东西
1
2
$ git log --pretty=raw
$ git cat-file

.git目录

进入.git目录,通过ls -l查看.git目录中的文件和子目录

  • (D) hooks:这个目录存放一些shell脚本,可以设置特定的git命令后触发相应的脚本;在搭建gitweb系统或其他git托管系统会经常用到hook script
  • (D) info:包含仓库的一些信息
  • (D) logs:保存所有更新的引用记录
  • (D) objects:所有的Git对象都会存放在这个目录中,对象的SHA1哈希值的前两位是文件夹名称,后38位作为对象文件名
  • (D) refs:这个目录一般包括三个子文件夹:heads、remotes和tags,heads中的文件标识了项目中的各个分支指向的当前commit
  • (F) COMMIT_EDITMSG:保存最新的commit message,Git系统不会用到这个文件,只是给用户一个参考
  • (F) config:这个是Git仓库的配置文件
  • (F) description:仓库的描述信息,主要给gitweb等git托管系统使用
  • (F) index:这个文件就是我们前面文章提到的暂存区(stage),是一个二进制文件
  • (F) HEAD:这个文件包含了一个当前分支(branch)的引用,通过这个文件Git可以得到下一次commit的parent
  • (F) ORIG_HEAD:HEAD指针的前一个状态

Git引用

Git系统中的分支名、远程分支名、tag等都是指向某个commit的引用。比如master分支,origin/master远程分支,命名为V1.0.0.0的tag等都是引用,它们通过保存某个commit的SHA1哈希值指向某个commit。

  • HEAD

HEAD也是一个引用,一般情况下间接指向你当前所在的分支的最新的commit上。

HEAD跟Git中一般的引用不同,它并不包含某个commit的SHA1哈希值,而是包含当前所在的分支,所以HEAD直接指向当前所在的分支,然后间接指向当前所在分支的最新提交。

  • Git索引(index)

index(索引)显示一个存放了已排序的路径的二进制文件,并且每个路径都对应一个SHA1哈希值。

1
2
#显示index文件的内容
$ git ls-files --stage

从命令的输出可以看到,所有的记录都对应仓库中的文件(包含全路径)。通过git cat-file命令查看reader.txt对应的哈希值,可以看到这个哈希值就是代表reader.txt的blob对象。

现在我们更新之前的文件,加上新内容并通过”git add”添加到暂存区,这时发现index中之前对象的哈希值已经变化了。

通过这个例子,我们也可以理解diff操作应该会有怎样的输出了:

git diff:比较WorkSpace和stage,add之前有diff输出;add之后没有diff输出
git diff HEAD:比较WorkSpace和repo,add之前之后都有diff输出
git diff –cached:比较stage和repo,add之前没有diff输出;add之后有diff输出

对象的存储

前面提到所有的Git对象都会存放在”.git/objects”目录中,对象SHA1哈希值的前两位是文件夹名称,后38位作为对象文件名。

所以,我们前面提到的master上最新的commit对象的哈希值是”4ea6c317a67e73b0befcb83c36b915c1481f2efe”,那么这个对象会被存储在”.git/objects/4e/a6c317a67e73b0befcb83c36b915c1481f2efe”。进入objects目录后,我们确实找到了这个文件。
在Git系统中有两种对象存储的方式,松散对象存储和打包对象存储。

  • 松散对象(loose object)

松散对象存储就是前面提到的,每一个对象都被写入一个单独文件中,对象SHA1哈希值的前两位是文件夹名称,后38位作为对象文件名。

  • 打包对象(packed object)

对于松散存储,把每个文件的每个版本都作为一个单独的对象,它的效率比较低,而且浪费空间。所以就有了通过打包文件(packfile)的存储方式。

Git使用打包文件(packfile)去节省空间.。在这个格式中,,Git只会保存第二个文件中改变了的部分,然后用一个指针指向相似的那个文件。

一般Git系统会自动完成打包的工作,在已经发生过打包的Git仓库中,”.git/objects/pack”目录下会成对出现很多”pack-***.idx”和”pack-***.pack”文件。

Git 分支

在代码版本控制工具中,都会有branch的概念。刚开始建立版本仓库的时候,我们只有一个主分支(master branch),我们不可能把日常的新功能开发、代码优化以及bug修复等概念工作全都放在主分支上,这样会使主分支很难维护。这就是为什么会有branch。

分支的创建

1
2
3
4
git checkout -b dev //创建并切换到分支dev
#相当于:
$ git branch dev
$ git checkout dev

查看当前分支

1
$ git branch

注:checkout – 表示还原,checkout 表示切换。

可以git log查看一下

分支的删除

1
$ git branch -d dev

分支的合并

branch的创建是为了方便开发、修复bug以及保持master的稳定。但是最终branch上的内容还是要合并到master的,接下来就看看分支的合并。

1
2
#合并到master上
$ git merge dev

注意:一定要先切换回master分支git checkout master后再合并。

合并冲突

在branch的合并中,很多时候我们会遇到冲突,那么我们就需要手动解决冲突,然后再提交了。

为了演示冲突合并,我们回退master到上一次提交

1
$ git reset --hard HEAD~1

然后在master分支上修改reader.txt,之后add、commit

这时我们在合并master和dev上的内容,就会出现冲突。

在Git中,会用”<<<<<<<”,”=======”,”>>>>>>>”标记出冲突区域,我们需要根据这些符号找到所有的冲突并解决,之后再次add、commit即可。

查看日志

1
$ git log --graph --pretty=oneline --abbrev-commit
  • –graph:显示commit的历史,包括不同分支的提交
  • –pretty=oneline:只显示一行提交的信息; 多行:–pretty=raw
  • –abbrev-commit:以精简的方式显示commit的哈希值

stash

在branch的使用过程中,我们还会经常使用到stash和diff操作,下面分别进行介绍。

在Git中,stash是个很有用的命令,可以保存我们做到一半的工作,可以理解成一个未完成工作的保存区。

假如我们在dev branch做了一些更新,但是想做的事情还没有全部完成,不能提交,这时我们又要切换到master branch,此时Git会禁止branch切换。

这个时候我们就需要使用stash来保存未完成的工作了。

注:在要保存的分支上进行stash操作。

1
2
3
4
# 保存未提交的内容
$ git stash
# 查看所有stash内容
$ git stash list

此时,我们就可以在其他分支上继续做别的工作了。

当其他工作完成,想回来继续刚才的操作的时候,我们可以通过git stash apply还原被保存的工作状态。

1
2
3
4
# 查看dev分支的状态
$ git status
# 还原被保存的工作
$ git stash apply

stash空间就像是一个栈空间,每次通过stash保存等内容都会被压入stash栈。命令不仅仅是支持简单的list、apply操作,接下来我们看看更多的stash命令。

  • git stash save可以通过自定义的信息来描述一个stash
1
2
3
$ git stash save "Reupdate reader.txt"
#再次查看当前stash的内容
$ git stash list
  • git stash apply stash@{n}通过这个命令,我们可以选择stash栈中的stash,从而恢复到特定的状态;”git stash apply”使用栈顶来还原WorkSpace。
  • git stash pop就像”git stash apply”使用栈顶来还原WorkSpace,但是同时会从stash栈中删除当前的栈顶stash。

branch之间的diff

在前面的文章中我们通过diff比较过同一个分支上的内容在WrokSpace、stage和repo中的差别。

同样diff可以支持分支之间的比较。

1
git diff branchName`把当前branch跟branchName进行比较,也可以使用`git diff branchNameA branchNameB

git diff branchName -- fileName比较两个branch的fileName文件差异


Git 远程仓库

前面文章中出现的所有Git操作都是基于本地仓库的,但是日常工作中需要多人合作,不可能一直都在自己的代码仓库工作。所以,这里我们就开始介绍Git远程仓库。

在Git系统中,用户可以通过push/pull命令来推送/获取别的开发人员的更新,当时对于一个工作组来说,这种方式会效率比较低。所以,在一个Git系统中,都会有一个中心服务器,大家都通过中心服务器来推送/获取更新。

为了方便本篇例子的进行,我就使用多个目录来模拟多个用户以及中心服务器,这样就不用搭建Git服务器了。

中心服务器:C:\VM\CentralRepo

用户A:C:\VM\AWorkSpace

用户B:C:\VM\BWorkSpace

建立中心服务器(服务器文件)

前面通过git init来建立一个Git仓库,这里,我们使用git init --bare来建立中心仓库。

git init --bare方法创建的是一个裸仓库,之所以叫裸仓库是因为这个仓库只保存Git历史提交的版本信息,而不允许用户在上面进行各种git操作。

之所以有裸仓库,是因为用git init初始化的版本库,用户也可以在该目录下执行所有git方面的操作。但别的用户在将更新push上来的时候容易出现冲突。在这种情况下,最好就是使用”–bare”选项建立一个裸仓库。

Clone一个仓库 (A仓库的位置)

在Git中,我们有两种方式建立Git仓库:一个是通过git init建立一个新的仓库,另一个是通过git clone命令clone一个已有的Git仓库。

既然中心服务器,用户A就可以通过clone命令来复制一个Git仓库。

1
$ git clone /c/vm/CentralRepo/ #被clone的仓库的地址

这时,用户A就从中心服务器clone了一个空的仓库,接下来A就可以在这个本地仓库工作了。

更新的push和pull

现在A在本地仓库中添加了一个”calc.py”的文件,并且提交到了本地仓库。

1
2
$ git add calc.py
$ git commit -m "add calc file"

为了使其他用户可以得到这个更新,A需要把这个更新push到中心服务器上。

1
$ git push origin master

现在用户B可以通过clone方式获得心服务器上的仓库副本,通过”git log”显示A的更新已经自动被clone了下来。

1
2
3
$ git clone /c/vm/CentralRepo/
$ cd CentralRepo
$ git log

现在,B提交了一个新的更新,那么A就可以通过git pull从中心服务器得到这个更新

上游仓库(upstream repository)

在Git系统中,通常用”origin” 来表示上游仓库。我们可以通过 git branch -r命令查看上游仓库里所有的分支,再用 origin/name-of-upstream-branch 的名字来抓取(fetch)远程追踪分支里的内容。

中心服务器上建立两个新的”dev1”和”dev2”分支,通过git pull ,用户B就看到了这些上游分支。

–set-upstream-to

当我们在本地仓库建立一个branch的时候,我们的pull操作会遇到以下问题,提示我们这个branch没有任何的跟踪信息。仔细想想也是,我们应该把本地仓库中的分支与上游分支关联起来。这时,我们就可以根据Git的提示,使用”–set-upstream-to”命令进行关联了。

1
2
3
4
5
6
7
8
# 先在B上创建同名分支
$ git checkout -b dev1
# 拉取
$ git pull # 出错!
# 本地仓库中的分支与上游分支关联起来
$ git branch --set-upstream-to=origin/dev1 dev1
# 此时拉取就能成功了
$ git pull

还可以通过下面的命令创建关联分支

1
2
#一般,"localBranchName"名称跟"remoteBranchName"名称设置成一样。
$ git checkout -b localBranchName origin/remoteBranchName

patch

在Git中patch绝对是一个很有用的东西。假设在一个没有网络的环境中,A和B还要继续工作,这时B有一个更新,A需要基于这个更新进行下一步的工作。如果是集中式的代码版本工具,这种情况就没有办法工作了,但是在Git中,我们就可以通过patch的方式,把B的更新拷贝给A。

在Git中有两种patch的方式

  • 通过git diff生成一个标准的patch

  • 通过

    1
    git format-patch

    生成一个Git专用的patch。

  • 基于git diff的patch

假设现在B更新”calc.py”文件并且通过git diff生成了一个patch。

1
2
3
4
# B更新后,通过`git diff `查看B的更新
$ git diff
# 把diff的结果保存到一个文件中,生成一个patch
$ git diff &gt; BPatch

下面,我们把 BPatch这个文件拷贝到A的工作目录中,然后通过git apply应用这个patch,从而得到B的更新。

1
2
3
4
# A通过`git apply`命令应用BPatch得到了B的更新
$ git apply BPatch
# 通过`git diff`查看B的更新
$ git diff
  • 基于git format-patch的patch

B更新提交之后,使用”git format-patch”来生成patch。

1
$ git format-patch origin/master

注意git format-patch命令的参数”origin/master”,这个参数的含义是,找出当前master跟origin/master之间的差别,然后生成一个patch,比如:0001-update-calc.py.patch。

把patch文件拷贝到A的工作目录,则此我们通过”git am”命令来应用这个patch。

1
2
3
4
# 应用patch
$ git am 0001-update-calc.py.patch
# 再查看日志
$ git log
  • 两种patch方式的比较

patch兼容性:git diff生成的patch兼容性强。也就是说,如果别人的代码版本库不是Git,那么必须使用git diff生成的patch才能被别的版本控制系统接受。
patch合并提示:对于git diff生成的patch,你可以用git apply –check 查看patch是否能够干净顺利地应用到当前分支中;如果git format-patch生成的patch不能打到当前分支,git am会给出提示,帮你解决冲突,两者都有较好的提示功能。
patch信息管理:通过git format-patch生成的patch中有很多信息,比如开发者的名字,因此在应用patch时,这个名字会被记录进版本库,这样做是比较合理的。

.gitignore

项目中可能会经常生成一些Git系统不需要追踪(track)的文件,在编译生成过程中 产生的文件或是编程器生成的临时备份文件。我们可以在使用”git add”是避免添加这些文件到暂存区中,但是每次都这么做会比较麻烦。

所以,为了满足上面的需求,Git系统中有一个忽略特定文件的功能。我们可以在工作目录中添加一个叫”.gitignore”的文件,j将要忽略的内容写进文件,来告诉Git系统要忽略哪些文件。

注意:

  • 在windows环境中不支持文件名为”.gitignore”,所以可以把文件命名为”.gitignore.”
  • “.gitignore”文件只会对当前目录以及所有当前目录的子目录生效;也就是说如果我们把”.gitignore”文件移到”advance”文件夹中,那么过滤规则就是会对”advance”及其子目录生效了
  • 建议把”.gitignore”文件提交到仓库里,这样其他的开发人员也可以共享这套过滤规则

过滤语法

  • 斜杠”/“开头表示目录
  • 星号”*”通配多个字符
  • 问号”?”通配单个字符
  • 方括号”[]”包含单个字符的匹配列表
  • 叹号”!”表示不忽略匹配到的文件或目录
    下面举一些简单的例子:
  • foo/*:忽略目录 foo下的全部内容
  • *.[oa]:忽略所有.o和.a文件
  • !calc.o:不能忽略calc.o文件

远程仓库命令

git branch

  • git branch -r 列出远程分支
  • git branch -a 列出所有分支

git remote

  • git remote 列出所有远程主机
  • git remote -v列出所有远程主机,并显示远程主机地址

git push

push命令用来将本地分支的更新推送的远程仓库,该命令的格式如下:

1
git push <远程主机名> <本地分支名>:<远程分支名>

通过”git push”更新、创建、删除远程分支

  • git push origin dev1:dev1 将本地dev1分支推送到远程origin的dev1分支上
  • git push origin dev1 省略远程分支名时,推送到远程origin下的同名(dev1)分支上
  • git push origin dev0 如果远程分支不存在,则创建一个同名远程分支与本地分支相关联
  • git push origin :dev1 省略本地分支名时,表示删除指定的远程分支,因为这等同于推送一个空的本地分支到远程分支。等价于git push origin --delete dev1,只是删除了远程分支,本地还有~

git pull

pull命令的作用是取回远程某个分支的更新,然后合并到指定的本地分支,pull命令格式如下:

1
git pull <远程主机名> <远程分支名>:<本地分支名>

git pull origin dev1:dev1:取回origin主机dev1分支的更新,与本地的dev1分支合并。

一般来说,pull命令都是在关联的本地分支和远程分支之间进行;当然,你可以使用不关联的本地分支和远程分支进行pull操作,但是不建议这么做。

省略本地分支名:git pull origin dev1,表示取回origin/dev1远程分支的更新,然后合并到当前分支

如果当前分支存在上游(关联)分支,可以直接使用git pull origin

git pull操作实际上等价于,先执行git fetch获得远程更新,然后git merge与本地分支进行合并。

pull命令也支持使用rebase模式进行合并:

1
git pull --rebase <远程主机名> <远程分支名>:<本地分支名>

在这种情况下,git pull就等价于git fetch加上git rebase

建议使用”git fetch”加上”git rebase”的方式来取代”git pull”获取远程更新。

git fetch

fetch命令比较简单,作用就是将远程的更新取回到本地。

git fetch origin表示将远程origin主机的所有分支上的更新取回本地

git fetch origin master只取回远程origin主机上master分支上的更新

Git 的merge和 rebase

merge

merge命令会有三种情况发生:

  1. merge命令不生效

当前分支已经是最新的了,在这种情况下merge命令没有任何效果。

1
$ git merge dev

提示”Already up-to-date.”,表示merge不生效,因为master比dev还要新,就不用merge了。

  1. Fast-forward合并模式

git merge --no-ff master 合并master时可以禁止Fast-forward, 以保留版本演进的历史信息,否则commit仍然是线性的

  1. 三方合并:不使用!

cherry-pick

在实际应用中,我们可能会经常碰到这种情况,在分支A上提交了一个更新,但是后来发现我们同样需要在分支B上应用这个更新。那么这时cherry-pick就可以帮助你解决问题。

我们要做的就是通过git reflog找到A上那个更新的SHA1哈希值,然后切换到B分支上使用git cherry-pick

1
2
#假设A上的更新是00abc3f
$ git cherry-pick 00abc3f

如果出错,按提示修改,先进行手动合并冲突,之后git add,然后继续cherry-pick,git cherry-pick --continue即可。

如果想要终止cherry-pick:git cherry-pick --abort

rebase

在merge的过程中,比较好的就是我们可以看到分支的历史信息,但是,如果分支很多的时候,这个分支历史可能就会变得很复杂了。如果我们使用rebase,那么提交的历史会保持线性。

rebase的原理:先将当前分支的HEAD引用指向目标分支和当前分支的共同祖先commit节点,然后将当前分支上的commit一个个apply到目标分支上,apply完以后再将HEAD引用指向当前分支。是不是有点绕,下面我们看个实例。
下面就开始rebase的介绍,我们会基于master新建一个release-1.0的分支,并在该分支上提交一个更新。

这时,我们在release-1.0分支上执行”git rebase master”,就会得到下面的对象关系图。

根据rebase的工作原理进行分析:
把当前分支的HEAD引用指向”00abc3f”
然后将当前分支上的commit一个个apply到目标分支,这里就是把”ed53897”更新apply到master上;注意,如果没有冲突,这里将直接生成一个新的更新
最后更新当前分支的HEAD引用执行最新的提交”8791e3b”
这个就是rebase操作,可以看到通过rebase操作的话,我们的commit历史会一直保持线性。在这种情况下,当我们切换到master分支,然后进行”git merge release-1.0”分支合并时,master将会直接是Fast-forward合并模式,commit历史也会是线性的。
当然rebase操作也会产生冲突,当一个冲突发生的时候,我们可以skip过当前的patch(一定要当心,不要随便使用,以免丢失更新);也可以手动的解决冲突,然后继续rebase操作

rebase交互模式
其实,rebase还有别的很强大功能,比如rebase交互模式,通过交互模式我们可以改变commit的信息,删除commit,合并commit以及更改commit的顺序等等。
假如我们现在想要更新一个commit的信息,我们就可以使用rebase交互模式,找到commit的哈希值,然后进入交互模式。

根据rebase的操作提示,我们选择edit操作,然后保存退出。

这时,Git将会提示我们,是进行更改,还是可以继续操作。这里我们通过”git commit –amend”进入编辑模式。

在编辑模式中对commit进行更新,然后保存退出,继续rebase操作。

关于rebase交互模式的其他命令,这里就不做介绍了,有兴趣的同学可以google一下。

总结

这篇文章主要对merge和rebase进行了介绍。对于最终的结果而言,rebase和merge是没有区别的,不会发生rebase和merge导致最终更新不一致的情况。
rebase和merge的差别主要是:
rebase更清晰,因为commit历史是线性的,但commit不一定按日期先后排,而是当前分支的commit总在后面(参考rebase原理)
merge之后commit历史变得比较复杂,并且一定程度上反映了各个分支的信息,而且 commit按日期排序的。
大家可以根据自己的项目需求进行选择使用哪种方式拉取远程的更新。

QT学习

什么是QT

Qt是一个1991年由奇趣科技开发的跨平台C++图形用户界面应用程序开发框架。它既可以开发GUI程序,也可用于开发非GUI程序,比如控制台工具和服务器。Qt是面向对象的框架,使用特殊的代码生成扩展(称为元对象编译器(Meta Object Compiler, moc))以及一些宏,易于扩展,允许组件编程。2008年,奇趣科技被诺基亚公司收购,QT也因此成为诺基亚旗下的编程语言工具。2012年,Qt被Digia收购。2014年4月,跨平台集成开发环境Qt Creator 3.1.0正式发布,实现了对于iOS的完全支持,新增WinRT、Beautifier等插件,废弃了无Python接口的GDB调试支持,集成了基于Clang的C/C++代码模块,并对Android支持做出了调整,至此实现了全面支持iOS、Android、WP。

学习视频教程网站http://my.tv.sohu.com/pl/5176735/

QT/安装步骤/环境搭建

下面介绍Windows版QT开发环境Qt Creater + MinGW + Qt libraries配置方法

下载安装MinGW

从MinGW网站下载,默认安装到C盘根目录下:C:\MinGW,安装时选择C和C++ compiler,默认只选中了C编译器。

下载安装配置QT libraries

http://qt-project.org/downloads

默认安装路径为C:\Qt\4.8.3,安装时需要指定MinGW的安装路径为C:\MinGW。

安装完后需要把C:\Qt\4.8.3\bin目录添加到系统变量的Path路径中。

并新建系统环境变量QMAKESPEC,32位系统把值设置为C:\Qt\4.8.3\mkspecs\win32-g++;如果是64位系统,需要把值设置为C:\Qt\4.8.3\mkspecs\tru64-g++

还要新建系统环境变量QTDIR,值为C:\Qt\4.8.3

下载安装配置QT Creater

默认安装到C:\Qt\qtcreator-2.6.0目录下。

需要把C:\Qt\qtcreator-2.6.0\bin目录添加到系统变量的Path路径中。如果不设置系统环境变量,则创建工程时kit不能设置成功,并且可创建的工程类型也会受到限制。

设置QT Creator构建和运行配置项:打开QT Creator,选择菜单“工具/选项”,选择左边的”构建和运行”,再选择“Qt版本”选项卡,点击“添加”,qmake路径:C:\Qt\4.8.3\bin\qmake.exe。

还需要设置Compilers选项卡中的“手动设置”项的编译器,Name设置为MinGW,编译器路径设置为C:\MinGW\bin\mingw32-g++.exe。

hello word程序

1
2
3
4
5
6
7
8
9
#include <qcoreapplication>
#include<qdebug>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);</qdebug></qcoreapplication>

qDebug()<<"hello world";
return a.exec();
}

用界面标签显示Hello World

新建—>其他项—>空的QT项目—>c++ source

1
2
3
4
5
6
7
8
9
10
11
###basic application and HTML WIDEGTS
#include<qapplication>
#include<qlabel>
int main(int argc ,char *argv[])
{</qlabel></qapplication>

QApplication app(argc, argv);
QLabel *label =new QLabel("Hello World");//显示Hello World
label->show();
return app.exec();
}

pushButton按钮

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui-&gt;setupUi(this);
ui-&gt;pushButton-&gt;setText("close");
}

MainWindow::~MainWindow()
{
delete ui;
}

添加事件—>信号槽—>clicked()—>close(), 这是直接在控件操作的

1
2
3
4
5
void Dialog::on_pushButton_clicked()
{

QMessageBox::information(this,"hello world","hello world");
}

和MFC中的messagebox(),效果一样。

1
2
3
4
5
6
7
void Dialog::on_pushButton_clicked()
{

//QMessageBox::information(this,"hello world","hello world");
ui->lineEdit->setText("Hello world!");
ui->lineEdit_2->setText("sssssss");
}

按一下按钮就在EDIT上面显示字符串”hello world”.

Signal和slot程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui-&gt;setupUi(this);
connect(ui-&gt;horizontalSlider,SIGNAL(valueChanged(int)),ui-&gt;progressBar,SLOT(setValue(int)));//添加信号
}

MainWindow::~MainWindow()
{
delete ui;
}

或者直接用信号槽和上面的一样

如果再加一条工程条,则代码一样

1
connect(ui-&gt;horizontalSlider,SIGNAL(valueChanged(int)),ui-&gt;progressBar_2,SLOT(setValue(int)));

只是其中的名字发生了改变ui->progressBar–>ui->progressBar_2

下面为取消信号槽

1
disconnect(ui->horizontalSlider,SIGNAL(valueChanged(int)),ui->progressBar_2,SLOT(setValue(int)));

显示对话框

首先先创立工程文件 DialogTest—->然后点“在这里输入”—–>”输入File”——>在File下面输入所需要的列表”New Window”——->在界面上拖入文本框

然后执行下列程序

1
setCentralWidget(ui->plainTextEdit);//是文本框左右充满整个mainwindow

在DialogTest下面添加新的类MyDialog

然后在”New Window”添加触发事件trigged()

下面往里面添加代码

1
2
3
4
5
6
7
8
9
10
void MainWindow::on_actionNew_Window_triggered()
{

MyDialog dialog;

// dialog.setModal(true);
// dialog.exec();//显示Dialog(第一种显示方法)
dialog.show();这样直接是不好使的,dialog一闪就没

}

第二种dialog显示是在MainWindow类下直接写#include "mydialog.h"

然后在mainwindow.h里面private中声明MyDialog *mydialog

然后直接在

1
2
3
4
5
void MainWindow::on_actionNew_Window_triggered()
{
mydialog=new MyDialog(this);
mydialog->show();
}

layout

直接创建Qdialog,添加事件。

Basic Application and HTML Aware Widgets

对之前的helloworld进行文件的变化(比如加粗或者上颜色)

1
2
3
4
5
6
7
8
9
10
11
12
13
#include<qapplication>
#include<qlabel>
int main(int argc ,char *argv[])
{</qlabel></qapplication>

QApplication app(argc, argv);
QLabel *label =new QLabel("<b>Hello</b> <span color="red" style="color: red;"> <i>World</i></font color =red>");//标签属性
label->show();
return app.exec();
//QLabel *label =new QLabel("</span>
<h2>Hello <span color="red" style="color: red;"> <i>World</i></font color =red></span></h2>
");标签可以随便加
}

Horizontal and Vertical Layouts

界面的布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include<qapplication>
#include<qpushbutton>
#include<qhboxlayout>
#include<qvboxlayout>
int main(int argc ,char *argv[])
{</qvboxlayout></qhboxlayout></qpushbutton></qapplication>

QApplication app(argc, argv);
QWidget *window =new QWidget;//创建一个新的window
window->setWindowTitle("my app");//把window的title 设置为 my app
QPushButton *button1=new QPushButton("one");//button1名字为one
QPushButton *button2=new QPushButton("two");
QPushButton *button3=new QPushButton("three");
QHBoxLayout *layout=new QHBoxLayout;//创建一个水平布局变量
//QVBoxLayout *layout=new QVBoxLayout;//创建一个垂直布局变量
layout->addWidget(button1);
layout->addWidget(button2);
layout->addWidget(button3);
window->setLayout(layout);
window->show();
return app.exec();
}

QGridLayout

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include<qapplication>
#include<qtgui>
#include<qtcore></qtcore></qtgui></qapplication>

int main(int argc ,char *argv[])
{

QApplication app(argc, argv);
QWidget *window =new QWidget;
window->setWindowTitle("my app");
QGridLayout *layout=new QGridLayout;
QLabel *label=new QLabel("Name:");
QLineEdit *txtName=new QLineEdit;
layout->addWidget(label,0,0);
layout->addWidget(txtName,0,1);
window->setLayout(layout);
window->show();
return app.exec();
}

如果还要在加一行

1
2
layout->addWidget(label2,1,0);//把名字改变就行,然后里面的参数也要改变
layout->addWidget(txtName2,1,1);

往里面添加按钮

1
layout->addWidget(button,2,0,1,2);//行,列 ,上下宽度,左右长度

Splitters

Qdialog

用到spliter键,我们可以用两个按钮进行演示—->先用水平splider—>水平布局

QDir

调用#include<QDir>

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
#include <qcoreapplication>
#include <qdebug>
#include <qdir>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QDir m_dir("C:\\TEXT");
qDebug() << m_dir.exists();
return a.exec();
}
​```</qdir></qdebug></qcoreapplication>

其作用是判断一个某个路径下的文件是否存在
如果存在返回“true”不存在返回"false"

​```c
int main(int argc, char *argv[])
{
QDir m_dir;
foreach (QFileInfo M_Item,m_dir.drives())
{
qDebug() << M_Item.absoluteFilePath();//返回硬盘的路径

}

}

下面这段程序为判断是否这有没”3.txt”文件,如果没有则创建,然后输出”created”,否则输出”already exists”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
QDir m_dir;
QString m_path="C:\\3.txt";
if(!m_dir.exists(m_path))
{
m_dir.mkpath(m_path);
qDebug()<<"Created";

}
else
{

qDebug()<<"Already Exits";

}
QDir m_dir;
QString m_path="C:/";
m_dir.setPath(m_path);
foreach (QFileInfo m_Item, m_dir.entryInfoList())
{

qDebug()<<m_Item.absoluteFilePath();//为查看当前下的文件目录
}

QFile文件读写

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
void write(QString Filename)//文件写
{
QFile mFile(Filename);
if(!mFile.open(QFile::WriteOnly|QFile::Text))
{
qDebug()<<"could not open the file for writting";
return;
}
else
{
QTextStream out(&mFile);
out<<"Hello man!";
qDebug()<<"Write Correctly";

}
mFile.flush();
mFile.close();

}
void read(QString Filename)//文件读
{
QFile mFile(Filename);
if(!mFile.open(QFile::ReadOnly|QFile::Text))
{
qDebug()<<"could not open the file for writting";
return;
}
else
{
QTextStream in(&mFile);
QString s=in.readAll();
qDebug() << s;
qDebug() << "Read Correctly"; } mFile.close(); } ``` ### CheckBox和RadioBOX、Commbox, listwidget ```c void Dialog::on_pushButton_clicked() { if(ui-&gt;checkBox-&gt;isChecked()//ui-&gt;radioBox-&gt;isChecked())//判断是否已经选择 { QMessageBox::information(this,"","selected"); } else { QMessageBox::information(this,"","not selected"); } } ``` ``ui-&gt;comboBox-&gt;addItem("123");//commbox加项`` 显示当前comboBox所选择项的文本 ```c QMessageBox::information(this,"",ui-&gt;comboBox-&gt;currentText());

显示当前listwidget所选择项的文本

1
2
3
4
QMessageBox::information(this,"hello world",ui-&gt;listWidget-&gt;currentItem()-&gt;text());
QListWidgetItem *item=ui-&gt;listWidget-&gt;currentItem();
item-&gt;setText("123");//选中当前项然后改变的文本
item-&gt;setTextColor(Qt::red);//把当前文本变成红色

qtreewidget

首先在Dialog.h里面声明两个函数 (addroot 和 addchild)

1
2
void AddRoot(QString name, QString Description);
void AddChild(QTreeWidgetItem *parent, QString name, QString Description);

然后在构造函数里面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
ui-&gt;treeWidget-&gt;setColumnCount(2);
AddRoot("HELLO","WORLD");
AddRoot("2","3");
void Dialog::AddRoot(QString name ,QString Description)
{
QTreeWidgetItem *itm=new QTreeWidgetItem(ui-&gt;treeWidget);
itm-&gt;setText(0,name);
itm-&gt;setText(1,Description);
ui-&gt;treeWidget-&gt;addTopLevelItem(itm);//这如果删除会产生什么情况
AddChild(itm,"one","hello");
AddChild(itm,"two","world");
}
void Dialog::AddChild(QTreeWidgetItem *parent,QString name,QString Description)
{

QTreeWidgetItem *itm=new QTreeWidgetItem(ui-&gt;treeWidget//这如果删除会产生什么情况);
itm-&gt;setText(0,name);
itm-&gt;setText(1,Description);
parent-&gt;addChild(itm);
}

在AddRoot()中加入AddChild()

1
2
3
4
5
6
7
8
9
void Dialog::AddRoot(QString name, QString Description)
{
QTreeWidgetItem *itm=new QTreeWidgetItem(ui-&gt;treeWidget);
itm-&gt;setText(0,name);
itm-&gt;setText(1,Description);
ui-&gt;treeWidget-&gt;addTopLevelItem(itm);
AddChild(itm,"one","hello");
AddChild(itm,"two","world");
}

button里面

1
2
3
4
5
6
7
void Dialog::on_pushButton_clicked()
{

ui-&gt;treeWidget-&gt;currentItem()-&gt;setBackgroundColor(0,Qt::red);//设置第一排颜色为red
ui-&gt;treeWidget-&gt;currentItem()-&gt;setBackgroundColor(1,Qt::yellow);//设置第一排颜色为yellow

}

设置头标签

1
2
3
4
5
6
7
8
9
10
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui-&gt;setupUi(this);

ui-&gt;treeWidget-&gt;setColumnCount(2);
//ui-&gt;treeWidget-&gt;setHeaderLabel("123");
ui-&gt;treeWidget-&gt;setHeaderLabels(QStringList()&lt;&lt;"123"&lt;&lt;"456"); AddRoot("HELLO","WORLD"); AddRoot("2","3"); } ui-&gt;treeWidget-&gt;setHeaderLabel("123");
ui-&gt;treeWidget-&gt;setHeaderLabels(QStringList()&lt;&lt;"123"&lt;&lt;"456");
0%