复习一下有关压片

室友让我帮他再RIP一下他第一场音乐会的DVD,以前似乎只做过下半场,做了下节目字幕然后寄出去了,可能是报名青歌赛的说。但是我找遍了整个硬盘发现无论是字幕文件还是AVS文件都不见了,估计是清理硬盘的时候把整个工程文件夹都删了。于是又得重新写脚本。

人呀,老了记性就不行了。脚本又忘了怎么写了,又得重新上网查资料,以及翻硬盘上没删的其他项目的AVS文件。

这次写文总结一下吧。

一、片源

对于DVD原盘,一般用DVD Decrypter分析章节并拷贝到硬盘上。Demux的工作一般也在这步完成。对于已在硬盘上存在的M2V、VOB文件,需要用DGIndex走带,VOB文件可以用DGIndex分离并将音频解压为WAV。用DGIndex分离音频时要指定要分离的音轨。MPEG2输出的DV也要用DGIndex走带,MPEG4的DV可以直接DirectShowSource,也可以用DGAvcIndex走带分析。

二、反交错

处理视频最烦的这一部分,首先请容许我深深地鄙视设定广播电视制式的科学家。

基本上我能遇到的就下面两种。

1、NTSC Film。这种常见于日本动漫DVD。片源为3:2的交错,需要反电影过带还原为24p,使用IT滤镜完成。

LoadPlugin("D:\Programs\AviSynth\plugins\IT.dll")
IT(fps=24, ref="TOP")

场序通过DGIndex走带时确定。

2、NTSC 30i或PAL 25i。这种一般是DV拍摄,张张交错,需要进行反交错,目前我使用Yadif和BlendBob滤镜。

Load_Stdcall_Plugin("D:\Programs\MeGUI\tools\yadif\yadif.dll")
Yadif(mode=1, order=1)
LoadPlugin("D:\Programs\AviSynth\plugins\BlendBob.dll")
BlendBob(badframes=0,forcematch=1)

Yadif的场序同样在走带时确定。

对于有些动画片片尾字幕是30i的这种画面中混合3:2和30i,我一般不例外处理,统统走IT,压个片没必要追求那么完美。

三、切边与拉伸

这点在片源上很混乱,具体片源具体分析,借助AviUtl软件看比较方便。对于动画片,先按保留全部有效画面的方法切看看情况,例如对School Days,是切9个像素然后拉伸到864×480比较好。对于DV我一直比较愤慨,因为我搞不清他到底怎么录的,反正是没有黑边,干脆就不切了,直接拉到1280×720或848×480拉倒。

四、压制参数

个人目前喜欢用x264默认的crf=23。对于480p的出片,其他参数为–level 4.1 –bframes 6 –b-adapt 2 –ref 13 –me umh –subme 9,对于720p的出片,其他参数为–level 4.1 –bframes 6 –b-adapt 2 –ref 9 –me umh –subme 9。720p在level4.1标准下受块缓存限制,参考帧必须降低到9。

音频使用NeroAAC q=0.25,对于注重音频的片子如音乐会用q=0.4。

解决MeGUI有时出现的StackHash崩溃问题

无聊想压一份Lord of The Rings,结果写了AVS结果在MeGUI中载入会导致崩溃,故障模块显示为StackHash_XXXX这样的字样,但是Windows中并没有这样一个模块。

Google得到的信息并不多,有这一篇文章引起了我的注意:

解决WIN7出现stackhash APPCRASH问题(故障模块名称: StackHash_af76)

虽然不确定是不是如文章所说的DEP问题,但还是按照文章的做法试着添到Windows的白名单中,结果提示此程序不支持关闭DEP保护类似这样的字样,无法添加进Windows的例外。

立即联想到PE文件头中有一个IMAGE_DLL_CHARACTERISTICS_NX_COMPAT标记指示该可执行映像是否为NX兼容。用WinHex打开MeGUI.exe,找到0xDE位置,将0x8540去掉该标记改为0x8440。

再重新打开会导致崩溃的AVS,发现问题已解决,而无需再添加白名单。

感慨一下,这可真的是很冷门的故障,以及,更冷门的解决方案。。。幸亏做毕设熟悉这块。。。

做了份School Days的DVDRIP,以及再次重新压制了一套EVA

首先吐槽一下我爹这破电脑,五年前的Dell 640m,实测压制848×480的片子速度仅3-4fps,压一集动画片要两个多小时,比i3 530要慢5倍左右。下面正题。

假期宅了几部岛国动画,觉得School Days很不错,遂决定收藏。但HKG字幕组发布的DVDRIP版使用的是24/60变码率封装,不喜欢就此收藏。变码率主要是为了解决片尾滚动字幕导致的24p/30i混合难以进行反交错的问题,但只有mkv支持这种封装,并且由于并不标准,若不进行任何修改直接用作片源转码则必定会出现兼容性问题(目测优酷上的高清版School Days声音与图像不同步完全无法观看就是由于这个原因)。然而找不到更好的片源,遂打算自己做DVDRIP。问卡车同学借了迅雷帐号,挂了8张DVD镜像ISO下来(两张OVA)。在此对卡车同学致以严重的感谢。

另外一个原因是HKG发布的是864×480分辨率,而我习惯使用848×480,所以想看看片源到底是多少乘多少的。事实证明我傻X了,下载下来DVD镜像才知道全天下的DVD都是统一标准的720×480,要进行切边与缩放操作。

DVD的另外一个重要特征就是需要进行30i到24p的反电影过带,亦可以等同为通常所说的反交错(两者并不完全等价,但对于初学者可以相同理解)。本着技术宅实事求是的态度,我系统地学习了DVDRIP的详细原理教程而非傻瓜教程,当然要真正深入地了解则需要学习广播电视相关的专业知识,这个倒可以略过 =-=。对于这块,我尝试了AVIUTL与TMPGEnc,这两者都可以做一定程度的人工反交错,也便于对这块原理的理解。但鉴于压片似乎没必要这么麻烦,最后还是选择了比较原始的AVS,使用最简单的IT滤镜来承担这一重要工作。

片源分离方面,使用DVD decryptor与DGIndex来弄。先用DGIndex打开光盘中的VOB文件预览cell的信息,然后用DVD Decryptor拷下来并分离视频和音频,再用DGIndex打开m2v文件保存为d2v文件留作AVS输入。

切边方面,先是尝试了保留全部画面,School Days的话是左3右4还是左4右3来着,但看到某个月亮突然觉得似乎有点扁,最后还是用了传统的左右各8,这样那个月亮就圆了。结果出片后发现有另外的月亮又扁了,fxxk。。。管他的了,反正人物胖点也没什么大不了的。。。

调色方面,按照教程TV转电脑是应该做色域扩大的,EVA-FANS的EVA2003一套也做了这个,但我实践后发现这样虽然画面色彩好了些,但是会造成亮部与暗部的细节丢失严重,压片时暗部还容易出现色块,最后决定还是不调色。事实上EVA 2.22以及我下的另外一些动画也都没有做调色。

压片依旧出MP4,开始定码率做成了两张CD共1.4G,突然想到要不尝试一下264默认的crf编码方法。就用默认的crf23压了一集,发现小了几乎一半。并且两者看不出什么差别。拿给豆子他说他能看出很明显的差别,豆子麻麻也是,好吧,我承认他家似乎有能听出火电与水电的差别对MP3听歌影响的基因。=-= 但是哥看不出并且豆子也承认并不影响观感,那就果断出crf版的。在用crf23压完全套12集后,计算知如果音频使用q=0.16编码则刚好可以放进一张CD(在此膜拜下MP4标准的制定,想当年一张CD只能放下1个小时的352×288的MP1编码,也就是VCD,现在可以放5个小时的MP4,并且是848×480。当然,这里仅限动画)。但是鉴于如上所述能够听出火电与水电差别的耳朵,我还是老老实实压了q=0.25。。。最终出片是766M,如果加上两部OVA的话就是905M。字幕选用繁体的,因为同样的字体没有简体版,HKG改成黑体不好看。

贴上第12集的AVS脚本(好吧,我承认我有点重口):

LoadPlugin("D:\Programs\DGIndex\DGDecode.dll")
LoadPlugin("D:\Programs\AviSynth\AC3Source.dll")
LoadPlugin("D:\Programs\AviSynth\IT.dll")
LoadPlugin("C:\Program Files\WMZHE\Pure Codec\Codecs\VSFilter.dll")

MPEG2Source("VTS_01_1 – 0xE0 – Video – MPEG-2 – 720×480 (NTSC) – 16~9 – Letterboxed.d2v")
AC3Source("VTS_01_1 – 0x80 – Audio – AC3 – 2ch – 48kHz – DRC – Japanese – DELAY -601ms.ac3")
DelayAudio(-0.601)

IT(fps=24, ref="TOP")
Crop(8,0,-8,0)
LanczosResize(848,480)

Trim(0,5)+Trim(6,0).TextSub("[HKG][School_Days][DVDrip][12][AVC_VFR AAC][864×480][E87F346D].BIG5.ass")

由于字幕与视频有一定的不同步,而ass字幕似乎不能指定整体偏移,就用trim的方法将媒体偏移掉再加字幕。这种方法还是蛮有用的。

在尝试了crf模式编码后,我表示对crf大大地感冒,觉得这样比我自己算码率保持体积要更科学,于是就重新开压EVA。事实证明我在4月给EOE分配的码率过低。crf整部EVA下来总大小比4月版稍小,出片为3.55G,主要是TV的某些集码率分配降低了,而EOE由于其中的故意噪点部分以及真人电影部分,总平均码率达到了1M左右。

其他参数与4月份保持不变。level=4.1,bframes=6,b-adapt=2,ref=13,me=umh,subme=9,merange=24。此参数请大家不要照搬,比如ref要根据level与你的设备的播放能力计算决定,480p可以设置13,但720p原则来讲就不行了,压片的技术要实事求是地学习理论外加积攒经验才行。

有兴趣者可拿硬盘来拷。无脑大文件高清控自觉走人不送,本人小体积性价比控谢谢。

WRT54G的河南联通PPPoE拨号设置手记

由于实在忍受不了家里无线路由的丢包率,抱着赌博的心态在TB上淘了个WRT54G V4。WRT54G这款路由器不用多做介绍,一代经典,堪称“机王”,尤其是V2到V4版本。此机早在07年就已停产,但远远不能满足人们对cisco一代经典的追求,于是万能的天朝商人便打起了山寨的主意,之所以说是赌博便因为目前TB上WRT54G与WRT54GS买到山寨的概率远远高于买到二手真货的概率。我本来就赌它是个假的,只要比我现在用的无线好并且能编程折腾就行,来了就拆机看电路板,结果居然是真货!并且是WRT54G-CN V4的大陆行货!!!恩,恭喜我中奖。。。

因为用不上太多功能,所以目前不急着刷系统。先弄上网再说。。。

结果我悲催地发现我把宽带的密码忘了!而且我爸妈都忘了!

不过这个难不倒,旧的Tenda路由器里记录的有,恩,用Firefox的Inspect Element功能看一般密码毫无压力的说,于是我拿到了一个6位的数字密码。

然后我发现linksys拨不上号!提示pppoe验证失败!我一度以为是tenda记录的不对,比如说只显示了前6位等等。于是这两天就先用两层路由,tenda拨号,linksys的wan接到tenda的lan上,因为tenda只是无线丢包,有线还没怎么坏。

今天我爸找到了用纸记录的密码,居然就是我两天前试的那个!!!我倒!!!

LZ已死,有事烧纸。。。

==============华丽丽的分割线================

天朝互联网寓言故事之小鬼大战路由器拨号

第一回合:躺枪阶段

若干次试验后,linksys依旧无法拨号,而插到tenda上立即就能拨上,所以可以否定是刚下线不能立即拨的看法。为了验证是不是isp做了mac地址绑定之类的动作,直接用电脑接猫,新建pppoe连接,输入帐号和密码,结果失败,提示用户名或密码错误。于是推断isp做了mac地址绑定。

如果判断为MAC地址绑定,那么很简单,修改电脑网卡(本机拨号情况)或路由器wan的mac地址即可。于是将linksys的mac地址修改为tenda的,失败!将电脑网卡的mac地址修改为tenda的,失败!将tenda的mac地址修改为linksys的,成功??!!!

也就是说isp并没有检测mac地址。那怎么tenda怎么弄都能上,其它设备怎么弄都弄不上。难道拨号时还有其他方法可能判断客户端设备?于是我翻了遍ppp协议,似乎并没有什么新的发现,并且最最重要的是,网络上讲,识别设备的唯一办法就是mac地址。

我艹!!!!!!这不科学!!!!!!!!!!!!!!!!!!!!

到了这个时候,问题的诡异性已经超出了人类的智商。

不信?让我们把思路整理一下:

条件一,密码真的真的真的真的真的是对的。

条件二,tenda能拨上linksys和电脑拨不上,设置上没有任何纰漏。

条件三,isp并没有通过绑定mac地址来限制拨号设备。

苍天啊,大地啊,这三个条件真的真的是无解啊!!!!!!!!!!!

小鬼给跪了。。。。。。。。。

 

第二回合:逆袭阶段

正在一筹莫展之际,我想起了前些天被豆子叫去给某妹纸装电脑的一个细节。

当时是我配置的tp的路由器,等了半天也没拨上号,后来又突然好了。我点开了路由器的日志记录,看到一共拨了3次好像,前两次都是用户名密码认证失败,最后一次成功了。

妹纸家是网通,也就是现在的新联通。而我用网通也有十二年的历史了,我知道河南网通一直用的自己的拨号客户端,从原来的dhcp+到后来的pppoe。我也听说网通的拨号客户端对用户的帐号和密码有一些加密处理。

失败了两次之后才成功,我当时以为是isp意识到用户在使用直接的拨号,所以最后就给放行了。现在看来是我太天真了。这应该是tp在拨号失败后主动尝试了针对isp的拨号客户端,如电信的星空极速,亦或河南网通的racer这些定制的特殊拨号方式,tp的手动拨号配置中是有这个选项的。

那么同是国产的tenda是不是也在固件里内置了河南联通的特殊处理,只不过它也是自动尝试,在设置界面上并不体现出来。想到这点,问题终于有了转机。

stfw得知,河南网通客户端将用户名进行了加密处理,用加密的用户名与未加密的密码进行拨号。有人通过对racer软件的反汇编得出了加密算法并写出了计算拨号用真实用户名的软件,但新联通成立时对客户端进行了升级,结束了dhcp+的时代并更新了加密算法,目前这个算法还没人做现成的软件出来。

下载河南联通356版客户端安装,拨号,成功!

下面就可以考虑找到真正的用户名填入linksys的设置里,目前推荐的做法是打开windows的事件查看器查看RemoteAccess来源的事件。

image

等等。。。3冒号后面是个嘛。。。。。。。。。。what the fxxing。。。

是的,您没有看错,356版河南联通算法即3冒号开头的用户名,表示第三版算法,在用户名中加了个回车符让您没法填到路由器的设置里。这里的用户名实际上是3:\r\n5q3F9zN6lF4。这个回车符遵循的是windows的crlf习惯。

不过没法填到路由器的设置页面里可不一定代表路由器就不支持这玩意,直觉告诉我我可以使用HTTP封包工具将这个回车直接post到路由器的设置页面。打开HTTPAnalyzer,截一个linksys设置的包,然后将post的用户名字段手动改为3:%0D%0A5q3F9zN6lF4,发送。

试着让linksys拨号,成功获取了ip地址!此时是西元2012年7月25日19时12分。经过三个小时的折腾,某小鬼终于成功让“机王”上上了网。

截图一张。注意那里其实是有个\r\n的,看源代码可以看到。

image

 

后记:

这则寓言告诉我们两个道理。

一、河南网通被评为全国最差isp(没有之一)是正确的,科学的,名符其实的。

二、没有在中国的互联网江湖上混过十年八年的,不要买WRT54G之类的传说中的古董神机来装逼,上不了网坑死你个XX的。

修改Firefox的Google搜索为中文

某年某月的某一天,本机firefox的Google搜索突然变成了英文的google.com,经常搜不到想要的结果,这次真的不能忍了,上网搜罗解决办法,经个人折腾整理如下:

1、修改安装目录下(如D:\Program Files\Mozilla Firefox)searchplugins\google.xml文件,将搜索URL由http://www.google.com/search改为http://www.google.com.hk/search

2、(比较重要)在后来的firefox版本中,还需要重建用户配置文件才能起效,在win7下,路径是C:\Users\XXX\AppData\Roaming\Mozilla\Firefox\Profiles\XXX.default,在这里删除search.json

3、重启firefox生效。

网上大多能找到修改google.xml的做法,但没有提到配置文件,照做后会比较郁闷,所以记录一下成功经验给大家参考

自制考研单词朗读音频

下了好几个语音库,最后挑中这个142000个单词的,下载地址:http://www.verycd.com/topics/133276/

这个是用8W韦氏词典语音库和10W沪江网语音库混合来的,全部是wav格式,但在运用的时候有点小问题:尽管都是wav文件,但两套的音频格式是不同的——MW字典是纯波形wav,8bit,采样率11025Hz,而沪江那套mp3压缩的wav,32kbps,采样率16000Hz。要做单词表朗读必须先统一他们的格式,至少要统一采样率。因为比较信MW字典,故决定将沪江那套按11025Hz重采样并以8bit的wav保存。

于是拿出用了七八年的GoldWave,用批处理功能转文件格式。看上去速度蛮快的,一秒30-50个文件,142000个文件差不多1小时就能转完。就放那儿干其他事去了,结果过一会切回来一看,速度变的奇慢无比,一个文件就要卡死一秒,开始快后来慢这是怎么回事呢,想了一下最后把目光锁定在转换窗口中那个ListBox上,因为每转好一个都会在那里面写一个成功的消息(以前写其他程序文本框末尾添加到最后多了的话也会卡的厉害)。这可糟了,GoldWave作者没想到会有这么多文件。。搁一般人肯定没辙了,不过哥既懂编程也懂破坏别人的程序,于是OD挂上折腾半天,找到添加一行记录的那个函数的地址是4e170c(GoldWave是Pascal写的诶)。用WinHex打开GoldWave.exe,看到text段的RVA是600,定位到e0d0c偏移(4e170c-401000+600),写上C3(x86的retn),另存为。用这个patch过的GoldWave再转,一路顺风~

最后全部弄完是1.48G,RAR再压起来是640M,比原来下的稍稍多一点,不过格式总算统一了。

然后就是按单词表把各个单词连起来做成一个文件,这个原理上比较简单,对于8bit的音频,按每秒11025b的大小开byte数组,全部初始化为80(8bit音频无符号存储,80表示0电平),然后把单词的数据拷贝过去就行。我用来背单词的是网上流传的《考研大纲词汇44页完美打印版》,然后自己把熟悉的单词挖掉,整理出了29页,每页126个词。因为懒每页每页复制粘贴的专门的处理程序中,就直接把程序写成office宏了。。这里贴上窗体代码:

Option Explicit
 
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (dest As Any, src As Any, ByVal Length As Long)
Private Declare Sub FillMemory Lib "kernel32" Alias "RtlFillMemory" (Destination As Any, ByVal Length As Long, ByVal Fill As Byte)
 
Private Const WAVE_CHANNELS As Integer = 1
Private Const WAVE_SAMPLES As Long = 11025
Private Const WAVE_BITS As Integer = 8
 
Private Type RIFF_HEADER
    szRiffID As String * 4
    dwRiffSize As Long
    szRiffFormat As String * 4
End Type
 
Private Type RIFF_BLOCK_HEADER
    szBlockId As String * 4
    dwBlockSize As Long
End Type
 
Private Type WAVE_FORMAT
    wFormatTag As Integer
    wChannels As Integer
    dwSamplesPerSec As Long
    dwAvgBytesPerSec As Long
    wBlockAlign As Integer
    wBitsPerSample As Integer
End Type
 
Dim OutputPath As String
Dim VoicePath As String
Dim PauseTime As Integer
 
Function PageProc(ByVal PageNum As Integer) As Boolean
    PageProc = False
    Dim coll As New Collection
    Dim xml
    Set xml = CreateObject("Microsoft.XMLDOM")
    xml.loadXML ThisDocument.GoTo(wdGoToPage, , , PageNum).GoTo(wdGoToBookmark, , , "\page").xml
    Dim node
    For Each node In xml.SelectNodes("//w:tr/w:tc[0]")
        coll.Add node.Text
    Next
    Dim voice() As Byte
    ReDim voice(WAVE_BITS / 8 * WAVE_CHANNELS * WAVE_SAMPLES * PauseTime * coll.Count() - 1)
    FillMemory voice(0), UBound(voice) + 1, 128
    Dim i As Integer
    For i = 0 To coll.Count() - 1
        Dim b() As Byte
        Dim word As String
        Dim path As String
        word = coll.Item(i + 1)
        path = VoicePath & Left(word, 1) & "\" & word & ".wav"
        If GetWaveData(path, b) Then
            CopyMemory voice(WAVE_BITS / 8 * WAVE_CHANNELS * WAVE_SAMPLES * PauseTime * i), b(0), UBound(b) + 1
        Else
            If MsgBox("无法找到语音:" & word & ",是否继续导出?", vbExclamation + vbYesNo) = vbNo Then Exit Function
        End If
    Next
    SaveWaveData OutputPath & Format(PageNum, "00") & ".wav", voice
    PageProc = True
End Function
 
Function GetWaveData(ByVal path As String, b() As Byte) As Boolean
    GetWaveData = False
    If Len(Dir(path)) = 0 Then Exit Function
    Open path For Binary As 1
    Dim riff As RIFF_HEADER
    Get 1, , riff
    If riff.szRiffID = "RIFF" And riff.szRiffFormat = "WAVE" Then
        Dim block As RIFF_BLOCK_HEADER
        Do While Seek(1) - 1 < 8 + riff.dwRiffSize
            Get 1, , block
            If block.szBlockId = "fmt " Then
                Dim fmt As WAVE_FORMAT
                Get 1, , fmt
                If fmt.wChannels = WAVE_CHANNELS And fmt.dwSamplesPerSec = WAVE_SAMPLES And fmt.wBitsPerSample = WAVE_BITS Then
                    Seek 1, Seek(1) + block.dwBlockSize - LenB(fmt)
                Else
                    Exit Do
                End If
            End If
            If block.szBlockId = "data" Then
                ReDim b(block.dwBlockSize - 1)
                Get 1, , b
                GetWaveData = True
                Exit Do
            End If
        Loop
    End If
    Close 1
End Function
 
Function SaveWaveData(ByVal path As String, b() As Byte) As Boolean
    SaveWaveData = False
    Open path For Binary As 1
    Dim riff As RIFF_HEADER
    riff.szRiffID = "RIFF"
    riff.dwRiffSize = 0
    riff.szRiffFormat = "WAVE"
    Put 1, , riff
    Dim block As RIFF_BLOCK_HEADER
    block.szBlockId = "fmt "
    block.dwBlockSize = 0
    Dim fmt As WAVE_FORMAT

    block.dwBlockSize = LenB(fmt)
    Put 1, , block
    fmt.wFormatTag = 1
    fmt.wChannels = WAVE_CHANNELS
    fmt.dwSamplesPerSec = WAVE_SAMPLES
    fmt.dwAvgBytesPerSec = WAVE_SAMPLES * WAVE_BITS / 8
    fmt.wBlockAlign = WAVE_CHANNELS * WAVE_BITS / 8
    fmt.wBitsPerSample = WAVE_BITS
    Put 1, , fmt
    block.szBlockId = "data"
    block.dwBlockSize = UBound(b) + 1
    Put 1, , block
    Put 1, , b
    riff.dwRiffSize = Seek(1) - 1 - 8
    Seek 1, 1
    Put 1, , riff
    Close 1
    SaveWaveData = True
End Function
 
Private Sub cmdStart_Click()
    VoicePath = txtVoicePath.Text
    PauseTime = txtPauseTime.Text
    OutputPath = ThisDocument.path & "\语音\"
    If Len(Dir(OutputPath)) = 0 Then MkDir OutputPath
    Dim page As Integer
    For page = txtPageStart.Text To txtPageEnd.Text
        Me.Caption = "正在导出第" & page & "页"
        DoEvents
        If PageProc(page) = False Then Exit For
    Next
End Sub
 
Private Sub UserForm_Initialize()
    txtPageEnd.Text = Selection.Information(wdNumberOfPagesInDocument)
End Sub

最后。。最后直接把wav文件拷mp3里了,反正88k的码率本来就不大,再压mp3反而失真厉害(一定要压的话,建议32k码率,不能再低了)

以后如果编辑了单词表,随时可以再导出更新的录音,非常方便。有兴趣的同学可以照做下。

Visual Studio 2008中统计代码行数

想必任何一个写程序的孩子都想对其他人炫耀:我的XXX程序代码量是多少多少~~

以前,我一直数代码的字数,很简单,到代码的目录里,按类型排序,然后把代码文件拖在一起点属性,哇塞,高三写的GMSJ网站有5万多字,正在写的LibCHD有7万多字了~~

但是呢,江湖上,貌似大家都爱说代码行数……行数这东西很不靠谱,写很长很长也是一行,写个int i或者干脆一大括号也叫一行(有关此方面的缩水可参见古龙的武侠)。。

于是呢,各种代码行数统计的工具、插件如雨后春笋般……(此处省略1000字)

其实呢,完全不必。其实VS自己就带有统计行数的功能。。(众:不是吧。。)只是你们不会用而已。。(众扔橘子。。)

很简单——在Visual Studio中按Ctrl+F,查找大家都会用吧~

VS的文档查找是基于代码行的,一个查找结果就是一个代码行。

这就简单了,查找范围选当前项目或当前解决方案,查找选项中选择使用通配符,然后查找内容输入一个*号,回车——

把查找结果拉到最底端,自我陶醉一下吧~