Linux系统下高质量(微软)中、英文语音合成TTS的安装

由于本人需要对录入文本进行校验,所以决定使用TTS语音合成系统(看自己写的东西很难发现错误)。首先尝试在Linux系统下自带的festival和espeak,但使用后总觉得效果不理想。作为Linux用户还是喜欢用Linux原生程序,所以第二步尝试非系统自带,但对中文特别对待的ekho(余音),然而效果依然令人乍舌,在使用了半天后便决定更换为确实可用的文本朗读系统。这便不得不使用Wine来模拟运行Windows程序,经过一番不懈的努力,终于成功在Linux平台上实现了高质量的文本朗读效果。现在将安装方法记录下来,方便自己也方便大家。

此次进行安装的Linux系统为Ubuntu 16.04 Mate版本(其它发行版应该也没有问题)。因为需要和别人交换文档,所以不得不使用Word 2010(正版),因此系统里早已安装好了Wine,版本为1.8。

Wine的安装:

如果你还没有安装Wine,可通过

sudo apt-get install wine
sudo apt-get install winetricks

进行安装。

英文语音引擎所需软件下载:

安装微软TTS引擎分为两部分,一部分为平台接口,一部分为语音包,注意下载时全部选择32位版本。

下载微软TTS语音平台SpeechPlatformRuntime:

下载地址为:https://www.microsoft.com/en-us/download/details.aspx?id=27225

微软TTS语音包下载(有多种语言可供选择,下载你需要的,如英语ZiraPro和中文Huihui。)

首先安装英文语音包(MSSpeech_TTS_en-US_ZiraPro.msi),中文语音包(MSSpeech_TTS_zh-CN_HuiHui.msi)在按英文语音包的安装方法安装后并不能正常工作,需要额外的修复步骤。

下载语音包:

https://www.microsoft.com/en-us/download/details.aspx?id=27224

下载语音播放软件:

当然下载了引擎后我们还需要语音播放软件balabolka
直接下载地址:http://balabolka.site/balcon.zip
直接下载地址为命令行版本,文件名为balcon.exe,本软件体积很小,不对Windows其它组件产生依赖。我没有对GUI版本测试。

以下的步骤必须在同一个终端窗口中进行,否则会出错。

Wine的设定:

接下来我们需要对Wine进行设定,因为balcon和TTS平台使用32位系统(64位系统我未测试),所以设定模拟构架为32位Windows,并指明Wine虚拟Windows的工作目录。

$export WINEARCH=win32
$export WINEPREFIX=~/.wine32tts

因对wine运行环境变量进行了设定,所以后续操作均需在该环境中,请勿更换命令操作窗口。
TTS引擎的安装文件为msi格式,所以需要msxml3以让wine正确处理msi文件。通过winetricks来安装msxml3。

 $winetricks msxml3

安装TTS语音平台和英文语音包:

 $wine msiexec /i SpeechPlatformRuntime.msi
 $wine msiexec /i MSSpeech_TTS_en-US_ZiraPro.msi

安装过程中会看到有错误提示,不必理会。此时的英文TTS引擎应该已经可以正常工作了。为测试TTS是否工作正常,我们可建立一个englishText.txt的文本文件,里面随意写入一些英文句子。然后通过以下命令进行测试:

 $wine balcon.exe -n en-US -f englishText.txt

此时应该已可以听见高质量的英文语音合成。参数-n指明balcon.exe所需调用的声音,-f指明需要进行语音合成的文本文件名。关于balcon.exe的其它参数,可直接wine balcon.exe查看说明。

以上信息感谢来自reddit的dazoe

中文语音引擎的安装

接下来是关键的中文语音。通过以下命名安装微软Huihui中文语音包。

 $wine msiexec /i MSSpeech_TTS_zh-CN_HuiHui.msi

准备一段包含中文文本的txt文件进行测试

 $wine balcon.exe -n zh-CN -f chineseText.txt

结果却提示Error: OLE error 80004005错误,无声音输出。中文在计算机世界里总是多一层麻烦。
这是因为缺少了chsbrkr.dllchtbrkr.dll两个文件,看文件名是用于简体中文和传统/繁体中文文本分割的。下载这两个文件并拷贝至

 ~/.wine32tts/drive_c/Program Files/Common Files/Microsoft Shared/Speech/TTS/v11.0

从网上下载时请注意版本,这里使用的是XP 32bit版本。
再次尝试运行,依然无法播放语音,错误信息更改为Error: OLE error 80040111。此时需要将Wine虚拟的Windows环境转换为Windows 8即可。输入命令

 $winecfg

然后在“Application”选项卡下将“Windows Version”改为“Windows 8”,确定。

再次运行测试命令应该就可以听到清晰的中文了。如果听到内容与文本内容不相符,则可能是文件编码的问题,请转换文件编码。我主要是选择文本然后播放,所以需要通过快捷键来迅速播放被选中的文本。这需要让balcon读取剪贴板内容并生成语音,可以通过-c参数来实现。

创建朗读快捷键:

可以创建两个简单的脚本文件,一个用于播放英文文本,一个用于播放中文文本,脚本中注意加入wine运行环境的变量即可。

英文文本朗读脚本speakEn.sh:

 #!/bin/sh
 export WINEARCH=win32
 export WINEPREFIX=~/.wine32tts
 wine /usr/local/bin/balcon.exe -n en-US -k -c

其中-k参数是杀死其它正在运行的balcon进程,这样作可以通过选择一个汉字,然后使用快捷键迅速结束朗读。

中文文本朗读的脚本speakCN.sh:

 #!/bin/sh
 export WINEARCH=win32
 export WINEPREFIX=~/.wine32tts
 wine /usr/local/bin/balcon.exe -n zh-CN -k -c

最后利用的X桌面环境设定自己的快捷键来运行以上两个脚本。

小提示:如果你选择了文本,但balcon不播放声音,这是因为Linux下有三个剪贴板,鼠标选取内容存储到了Primary剪贴板中,而balcon调用的是Second剪贴板,因此可以用类似Diodon的剪贴板管理软件来自动同步三个剪贴板的内容。

好了,享受Linux下的高质量TTS朗读吧。

在此强烈谴责一下国内的百度、腾讯等IT厂商,中文TTS本已不错,你们不愿开源,至少发布一个Linux下闭源的免费个人版本吧!

Dijkstra算法详解

Dijkstra算法,中文翻译为迪杰斯特拉算法,是在带权有向图G = (V, W) (V是节点的集合,W是边上权值的集合,在集合W中,不直接相连的节点间的距离标记为无穷大)中求某个节点到其余节点之间最短路径的经典算法。该算法的核心思想是将图中所有的节点划分为2个集合,集合S包含已经找到最短路径的节点,集合U包含尚未找到最短路径的节点的距离(初始值全部设定为无穷大)。从某一点v开始采用广度优先遍历思想对图进行遍历,每找到一个节点的最短路径,就将该节点从集合U中移除,并加入到集合S中,直到最后U变为空集,S包含图中所有的节点到出发点的最小距离。在加入的过程中,总保持从源点v到S中各顶点的最短路径长度不大于从源点v到U中任何顶点的最短路径长度。

Dijkstra算法的过程:

  1. 选取某个节点v作为出发点,从出发点到出发点距离为0,即找到最小路径。将该出发点从集合U中移除并加入到集合S中。
  2. 在集合W中查看出发点v到集合U中各个节点的距离。找出距离最小的节点k(到k的最短路径找到),将k从集合U中移除并加入到集合S中。
  3. 以k作为中间点,在W中查看从k到U中节点m的距离,如果m在W中的值小于m在U中的值(k和m直接连接),将m在W中的值与k在U中的值相加(从v出发经过k到达m的距离),再将相加结果与W中v到m的值(v不经过中间点到m的距离)对比大小,选取较小的值跟新k在U中的数据。
  4. 在U中找出数值最小的节点n,该值即为v到n的最短路径。将n从集合U中移除并加入到集合S中。把n作为中间点重复第3、4步,直到U中所有的节点都移动到S中。

图1

Dijkstra算法的手算方法

如图1,从节点A开始。首先我们创建一个表,用该表记录每次运算的值。该表表头代表了集合U中包含的节点(未找出最短路径节点:A、B、C、D)。第2行将各节点的距离,初始化为无穷大,用INF表示。

 

A

B

C

D

 

INF

INF

INF

INF

 

点A是出发点,自身到自身的路径最短,为0,即A到A的最短路径找到。在A列的第2行输入0,并用下划线标出,表明将A从U中移除。在表的第2行最左侧写A,表示将A加入集合S。

 

B

C

D

0

INF

INF

INF

建立表格第3行。此时集合U中包含{B、C、D},由图可知A->B的距离为1(查询集合W),更新B列的数值,写入第3行。由图可知A->C的距离为5,更新B列的数值,写入第3行。由图可知A->D不可直达,标记为INF。在表第3行中对集合U中所拥有的元素{1,5,INF}求最小值,得到B,此时B列第3行数据即为A->B的最小路径。对B列第3行数据加下划线标识出,即将B移出集合U。同时在第3行最左端写B,即将B加入集合S中。

 

A

B

C

D

0

INF

INF

INF

0

1

5

INF

建立表格第4行。此时集合U中包含{C、D},节点B为中间点。由图可知B->C的距离为1,而A->B的距离保存在表B列中,是1,则A->B->C的距离为1+1=2。由表C列第3行数据可知目前到达C的距离为5(A直接到达C)。求两条路径的最小值min(5,1+1)=2;更新C列数据,将2写入C列第4行。由图可知B->D点的距离为1,则A->B->D的距离为1+1=2,而A->D不可直接抵达,故而取值无穷大INF(该值记录在集合W中,此时该值等于表中D列数据)。求两条路径的最小值min(INF,1+1)=2。更行D列数据,将2写入到D列第4行。对集合U中的数据取最小值,C和D均为2,任选一个,该节点到A的最短距离即被计算出来。比如选C。对C进行标记。

 

A

B

C

D

A

0

INF

INF

INF

B

0

1

5

INF

C

0

1

2

2

重复上面的操作,建立表格第5行。此时集合U中只包含{D},节点C为中间点。由图可知C->D的距离为1。而A->C的最短距离已经求出,在上表中C列第4行,为2(经过节点B)。故A->B->C->D的距离为2+1=3。表中D列第4行记录到达D点的最小路径为2(通过B点),求两条路径的最小值min(2,2+1)=2。即D点的最小路径为2,将2写入D列第5行并对D列第5行进行标记。此时集合U为空集,集合S中的元素为{A、B、C、D},计算完毕。表中第5行所记录数据即为A点到各点的最小路径值。

 

A

B

C

D

A

0

INF

INF

INF

B

0

1

5

INF

C

0

1

2

2

D

0

1

2

2

 

具体路径(经过的节点)可以通过回溯法求出。在最后一张表中找到要求点所在的列,从下向上回溯,当数值发生变化时,跳转到所在行所对应的S集合中的元素所在的列,记录下该点,然后继续回溯,直到到达出发点。

如求到达C点的具体路径,从C列第5行向上回溯,到达第3行时数值发生变化,第3行对应为S集合中的B,则从B列第3行向上回溯,到达B列第2行,数值发生变化,第2行所对应的集合S中的元素为A,是起点,回溯完成。具体路径即为A->B->C。

本思路所对应的C#源代码请参看《Dijkstra算法C#演示程序》一文。

Dijkstra算法C#演示程序

最近学习Dijkstra算法和C#,用C#写了一个Dijkstra算法的演示程序。图中的节点要求使用26个字母来表示。节点之间的距离为整数,不相邻节点输入”I”或”i”来代表Infinite。该程序移除了输入的冗余,并且显示输入节点和距离后的邻接矩阵。同时会在最终结果给出前,打印出 Dijkstra算法的计算过程表和最短路径点的次序(path数组)。在代码中给出了详细的注释,对于学习Dijkstra算法的人具有一定帮助。
运行效果图:
050615_0543_DijkstraC1.png

?View Code CSHARP
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace CsharpVersion
{
 
    class Program
    {
        public class DEF
        {
            public const int MAX = 26;
            public const int INF = 999999;
        }
        public struct Graph
        {
            public char[] vetx;//用于存储每个节点的名字
            public int vetxNum;//拥有节点的数量
            public int[,] matrix; //相邻矩阵
        }
 
        static public Graph genGraph()
        {
            string input;
            int i, j;
 
            //初始化图的结构体
            Graph map;
            map.vetx = new char[DEF.MAX];
            map.vetxNum = 0;
            map.matrix = new int[DEF.MAX, DEF.MAX];
 
            Console.Write("Please enter the number of nodes:");
            input = Console.ReadLine();
            map.vetxNum = Convert.ToInt32(input);
 
            //获取每一个节点的字母
            for (i = 0; i < map.vetxNum; i++)
            {
                Console.Write("Please enter the letter of nodes:");
                map.vetx[i] = Convert.ToChar(Console.ReadLine());
            }
            //创建邻接矩阵
            for (i = 0; i < map.vetxNum; i++)
                for (j = i + 1; j < map.vetxNum; j++) //只需输入半个矩阵
                {
                    Console.Write("Please enter the distance from {0} to {1} (I stands for Infinte):", map.vetx[i], map.vetx[j]);
                    input = Console.ReadLine();
                    if (input.ToLower() == "i")
                        map.matrix[i, j] = DEF.INF;
                    else
                        map.matrix[i, j] = Convert.ToInt32(input);
                }
            //填补节点自己到自己的距离 = 0
            for (i = 0; i < map.vetxNum; i++)
                map.matrix[i, i] = 0;
 
            //填补重复部分的矩阵
            for (i = map.vetxNum - 1; i > 0; i--)
                for (j = i - 1; j >= 0; j--)
                    map.matrix[i, j] = map.matrix[j, i];
 
            //debug: 显示邻接矩阵
            Console.WriteLine("*************START**************");
            Console.WriteLine("The matrix of distance is:");
            for (i = 0; i < map.vetxNum; i++)
            {
                for (j = 0; j < map.vetxNum; j++)
                {
                    Console.Write("{0}", map.matrix[i, j]);
                    Console.Write("\t");
                }
                Console.Write("\r\n");
            }
            Console.Write("\r\n");
            return map;
        }
 
 
        //Dijkstra algirthm
        static void dijkstra(Graph map, char sNode)
        {
            int[,] dist = new int[map.vetxNum, map.vetxNum];//距离数组
            bool[] flag = new bool[map.vetxNum]; //标记数组
            int min, tmp, preBaseVal = 0, i, j, preMarkedIndex, vs = 0;
            int[] path = new int[map.vetxNum]; //用于存储推演表中所有被选中的中继点
            Stack<char> pathStack = new Stack<char>();
 
            //找出起始点的编号
            for (i = 0; i < map.vetxNum; i++)
                if (map.vetx[i] == sNode)
                    vs = i;
            //距离数组初始化
            for (i = 0; i < map.vetxNum; i++)
            {
                dist[0, i] = DEF.INF; // map.matrix[vs, i];
                flag[i] = false;//所有节点标志位均设为未标记
            }
 
 
            //初始化起始点数据
            dist[0, vs] = 0;//起始点到自身的距离为0
            preMarkedIndex = vs; 
 
            for (i = 0; i < map.vetxNum - 1; i++) //i代表第几行,
            {
                //在推演表中第一行找出最小数
                min = DEF.INF;
                for (j = 0; j < map.vetxNum; j++) //j代表第几列
                {
                    if (min > dist[i, j] && flag[j] == false)
                    {
                        min = dist[i, j];
                        preMarkedIndex = j;
                        preBaseVal = min; //记录本轮找到的最小值
                    }
                }
 
                flag[preMarkedIndex] = true;//将找到的节点标记
                path[i] = preMarkedIndex;//将找到的节点(次序)保存
 
                //把已标记的值复制到推演表下一行
                for (j = 0; j < map.vetxNum; j++)
                {
                    if (flag[j] == true) //找到已标记的节点
                        dist[i + 1, j] = dist[i, j]; //把上一行中对应的已标记值复制到本行中
                }
                //求出当前行未被标记列(节点)的距离
                for (j = 0; j < map.vetxNum; j++)
                {
                    if (flag[j] == false) //找出未标记节点
                    {
                        if (map.matrix[preMarkedIndex, j] == DEF.INF) //前驱结点到本列所指节点不可直接到达,复制上一行数据
                        {
                            dist[i + 1, j] = dist[i, j];
                            continue; //继续去判断下一个节点
                        }
                        else //前驱结到当前列所指节点可以直接到达时
                        {
                            dist[i + 1, j] = (dist[i, j] < (preBaseVal + map.matrix[preMarkedIndex, j]) ? dist[i, j] : (preBaseVal + map.matrix[preMarkedIndex, j]));
                        }
                    }
                }
            }
            for (j = 0; j < map.vetxNum; j++) //找出最后一个剩余的节点,并加入到路径数组中path[]
            {
                if (flag[j] == false) //最后剩余的节点尚未标记
                    path[map.vetxNum-1] = j;
            }
 
            //打印推演表
            Console.Write("\r\n");
            Console.WriteLine("The calculation table is:");
            for (i = 0; i < map.vetxNum; i++)
            {
                for (j = 0; j < map.vetxNum; j++)
                {
                    Console.Write("{0}", dist[i, j]);
                    Console.Write("\t");
                }
                Console.Write("\r\n");
            }
 
            //debug: path的内容
            Console.Write("Path=");
            for (i = 0; i < map.vetxNum; i++)
                Console.Write("{0}", map.vetx[path[i]]);
            Console.Write("\r\n\r\n");
 
            //找出路径结果
            for (i = 0; i < map.vetxNum; i++)
            {
                tmp = i; //tmp代表了第几列
                pathStack.Clear();//清空路径栈
                Console.Write("The shortest distance from {0} to {1} is {2}: ", map.vetx[vs], map.vetx[i], dist[map.vetxNum - 1, i]);
 
                //找出从给定点出发到达每一个节点的路径
                for (j = map.vetxNum - 1; j > 0; j--)
                {
                    if (j == map.vetxNum - 1) //j代表了第几行
                        pathStack.Push(map.vetx[i]); //将目的地节点压入路径栈
 
                    if (dist[j, tmp] == dist[j - 1, tmp])
                        continue;
                    else
                    {
                        pathStack.Push(map.vetx[path[j - 1]]);//将前驱结点名压入路径栈
                        tmp = path[j - 1]; //为下一次循环定位列,移动到前驱结点所在的列
                    }
                }
 
                //打印出路径栈中的路径
                foreach (char c in pathStack)
                {
                    Console.Write("->{0}", c);
                }
                Console.Write("\r\n");
            }
            Console.WriteLine("***************END***************");
        }
        static void Main(string[] args)
        {
            char startPoint;
            Graph map = genGraph();
 
            Console.Write("Please enter which node starts:");
            startPoint = Convert.ToChar(Console.Read());
 
            dijkstra(map, startPoint);
 
            Console.ReadKey();
        }
    }
}

使用kindle 3 3G三天的总结(重点PDF)

首先攒一下UPS邮递(Priority International Courier),很好,很快,送货上门。

折腾了3天,首先是试用原带系统,说句实在话,原带系统对英文文档支持非常好,但中文显示的效果差强人意,当然也不能输入中文了,网上传言可以使 用使用云输入法,但我还没有测试,不过我想能用也是只能在浏览器中使用。另外说一下浏览器,不支持页面中的target=”_blank”标签,也就是说不 能浏览那种如果需要在开启新页面来显示的网页,但是先用www.google.com/gwt/n 然后再浏览网站就没有任何问题了。这个浏览器也就是用来看页面上的文章可以(新闻网站),泡论坛什么的就痛苦了。但google/gmail/reader/douban/wikipedia等都没有问题,感觉还可以,如果开启了网站的wap或3g页面效果就很舒服了。

用了3个小时,改了系统的locate,中文可以显示了。但只用了一天就越狱了,测试了大量的字体,最后觉得原系统下安装文泉驿米黑来显示中文效 果最好。又用了一天,第三天忍不住安装了多看系统,中文显示好了很多,并且在使用上方便了很多,主要是支持格式多了,而且浏览各种格式的时候的功能也好了 很多,但英文比以前差了,不过可以接受,最主要的是多看系统目前的bugs还比较多,等待正式版的出现。

PDF在原系统下比较累,竖屏我最多坚持看一页,就受不了了,横屏可以一直阅读,说实在的,还可以,通常PDF需要加黑显示,如果是扫描版的,当 然效果会比普通版差一下,但是是可以用来阅读的,不过最好首先使用briss对PDF切白边(效果很好),然后再阅读就会舒服很多。

多看系统下看PDF效果比原系统好太多,其实就因为多看允许了真正的全屏阅读,并且对没有切过margin的pdf,可以在阅读的时候随时选择 切边,效果很棒,如果是非扫面板的 PDF甚至可以将格式重排,也就是说在你阅读大量纯文字的时候选择重排,效果就和mobi/epub/txt的效果一样了,当碰到代码或表格的时候,关闭 格式重拍,自然会显示原格式的PDF,非常好,加上自动切margin和真正全屏,我甚至在阅读过程中不用切换成横屏都可以。

另外说一下无线3G网络,我到手后开机,自动连接了3G网络,并且amazon已经给我注册了,所以开机后显示了我的名字等信息。但非美国用户不 能用3G随意浏览,只能看英文 wikipedia!但随后将注册地址修改为美国。3G就自由了。本打算进一步越狱,用这个东西来免费连接电脑上网,但看到有人被人肉通知不再美国,要求 关闭的时候,我决定保留我的3G上网能力,也就是只用kindle偶尔上上网就行了。

我可以说,对于按顺序阅读,kindle 3够了,但如果你想要把大本书的扫描PDF当资料来随时查阅,我觉得还是书本好一些,也许大屏幕的kindle DX会好很多(肯定的了),但是价格和携带性以及耗电速率都不划算。

中国的兰州拉面“拉”火了巴黎

作为在兰州生活了二十多年的人,看到这则新闻还是比较开心的,所以转过来。

巴黎最后的晚餐吃什么?“活着的面条”。这么有趣的店名听着幽默,其实一点也不新鲜,就是兰州牛肉拉面。

在法国,本地面条的形象,在反反复复的期望与失望中,越来越“一团面酱”了。意大利面条折射“危机化”,似乎一堆工业化的面条子上整一堆番茄酱肉沫子就算是了;而中餐馆里,不知是做不出好吃的面,还是不屑于做,总之代表作品就是满是碱和鸡蛋的黄橙橙面条子泡在一碗味精汤中,架上几块烧鸭熟肉一片生菜叶了事;越式米粉虽能找到正宗的,但终究与华夏之“宗”迥异,代替不了中国食客对中国面的饕餮。

在这样险恶的面条环境中,2008年2月开业的位于巴黎9区Faubourg Montmartre街的名曰“活着的面条”的兰州拉面馆,“活”起来并“火”起来,就一丝丝也不值得奇怪了。

老板娘当然是兰州人。一身唐装,热情待客。对于她的成功,她不想作任何谦虚状:“凡是正宗的东西,一定有人吃”,她一语道破天下美食之“天机”,也同时一脚踹到当今很多餐馆的软肋。

“活着的面条”有个规矩,店里不给刀叉,一律用筷子吃面,因为只有这么吃才能吃出感觉。为更进一步鼓励法国客人抛弃自家习惯,吃面喝汤要“出声”。这是何等的“店大欺客”的文化征服?环视四周用筷子虽不很熟练但呼噜噜有滋有味的洋人们,又一次印证了远在异乡却要“将正宗进行到底”的美食文化观念。

总把他乡做故乡,也是一种营销境界。在人们面前现吃现拉的手拉面,不但让中国人有了故乡感,更让洋人们神奇倍至。说实话,本博秦全耀以为,正是这种中国文化在法国巴黎的活灵活现,才让面条活了起来,也火了巴黎。

两年来,“活着的面条”又在巴黎开了几家分店。还是拉,现场拉,一拉面就活,一拉店就火。拉得是什么?拉得是中国文化。

Lingoes 2.7.1 封服务器去广告(弹窗)方法

其实Lingoes还是一个不错的字典软件,我也用了2年多了,并且设定为开机启动,随时需要,随时调用。很喜欢,可是2011年1月1日,我一直使用的Lingoes 2.6竟然提示不被支持了,呵呵,说白了就是强制升级,启用了新版的Lingoes,也就是2.7.1 beta,虽说可以使用了,但竟然有了弹窗广告,是互通百科的东西,一个字典软件怎么能允许它弹窗加广告呢?不过也不能怪开发者,中国政府首先不保护知识产权,再加上中国人一向喜欢占小便宜,所以在中国,几乎是没人会花钱购买软件的,这也就逼迫中国的软件开发者们不得不通过软件上面打广告的方式来赚钱,从整体上将中国的国产软件垃圾化。垃圾就垃圾吧,反正我计算机里也没有几个国产软件,Lingoes算是特例了。虽然是特例,但也得保证我计算机界面上的无广告效果。所以首先从网上搜索方法,看到了一堆通过改tips.xml和update目录的做法,挺好,启用后发现依然后互动百科的弹出窗口,很是不爽,花了点时间,跟踪了一下lingoes要连接的服务器,其实如果你不启用在线词典或百科全书网站,直接把lingoes用防火墙封杀就完事了,但我还是很喜欢一些在线百科网站上面提供的内容的,所以只封杀广告服务器。发现其实Lingoes使用的服务器并不多,就2个(我不确定,只是我在30分钟内看到的结果,如果你发现更多的,欢迎告诉我),一个在netpu.com这家公司的服务器上,一个在net-infinity.net这家公司上,直接在防火墙里将Lingoes对这两个域名的连接全部封杀了,使用2个小时,没有再见到弹窗。如果以后碰到了,再补进来。

整个去广告的操作过程如下:

首先按网上的方法:

XP: X:Documents and Settings你的帐号Application DataLingoesTranslator
Win7: X:Users你的帐号AppDataRoamingLingoesTranslator
删除 tip.xml, 建立同名目录
删除 update 目录, 建立同名文件。‘

您可能需要一个稍微复杂一点的防火墙,可以自定义TCP规则的防火墙就够了。

然后打开你的防火墙,选择“应用程序规则”,找到“Lingoes.exe”, 在里面添加规则。

我懒得具体追踪Lingoes在net-infinity.net和netpu.com这两家公司上到底有多少个IP,所以一次性将整个A段都封掉,反正Lingoes里面其它的在线词典也不在这两个域名之下(或这两个A段网址上)。

Net-infinity.net: 封锁203.0.0.0/255.0.0.0

Netput.com:  封锁208.0.0.0/255.0.0.0

 

好了,你的Lingoes应该再也不会有任何广告和弹窗了。当然如果发现了,请告诉我,我好再将新的广告服务器地址封掉。谢谢!

免签证区域对比图

中国人出国难,难于上青天,这大伙儿都知道,在国际上奔波过的中国人感受应该就更深了,所以不要怪杨振宁回答说“为了出国旅行方便而换了护照”,您要有过类似的经历,我保证您再也不想用中国护照在国际上行走。至于为什么出国难,原因太多了,粗粗总结一下,以下几点原因吧,懒得细细写了,心烦。

  • 首当其冲,政治原因,偶国在世界上的特殊性大家都知道。道不同,不相为谋。
  • 大量低素质的偷渡者,到了别国,给人家造成很多麻烦。可为什么那么多人要偷渡呢?
  • 中国人太多,其它小国家会害怕。
  • 中国人素质偏低,主要是五千年腐烂的世俗和文化渗入到了每一个中国人的骨头里,以致于自己发现不了自己的缺点,但老外会感觉很不舒服。
  • 老外对中国人的偏见,当年没能把中国像非洲一样的略夺一把,现在中国越来越强大了,害怕丢了自己的位置,所以继续折腾中国。
  • 某些小国为保自身利益给某些大国或集团当走狗,听大国的话,继续抵制中国。

贴个图上来,直观一些,愿中国将来能改变这一现象,让每一个手持中国护照的中国公民能够享有和大多数国家公民在国际上一样的地位和待遇。

 

今天把以前Opera blog上面的文章转了过来

Opera blog上的文章基本上记录了我出国第一年的情况,也就是2006到2007年中的生活状况,以及心情。由于Opera blog被封,所以停用了该博客服务,启用了自己架设wordpress博客,直到今天才基本上把所有内容都转载了过来(个别文章没有转)。现在来看2008年到2009年写的博客全都丢了,因为当时是在一个免费空间上架设的wordpress系统来写博客,用来近1年,突然账号被删除了,可恶至极,所以未能即时导出文章。目前来看从2005年开始写的第一篇博客到现在2010年12月份,除了2008年到2009年的丢失了,剩下的都转到了目前这个博客里。就算完整了,好赖自己也写了5年多的博客了,虽然不是天天在写,甚至几个月都不写一篇,而且经常因为变换博客系统,导致文章内容格式错乱,tag和分类丢失,朋友的留言也基本上都丢了,但终归还是始终拥有着自己的博客,也算一种坚持吧。呵呵。

Linux下VPN(pptp)客户端的设置

由于新注册一个VPN账号,所以特地想试用一下,在Windows下很简单,只要在创建连接中根据提示,填入信息一步一步下来就可以自动连接到VPN服务器上。但在Linux下相对比较麻烦,花了几个小时,终于设置成功了。

首先下载安装所需的PPTP程序包。

sudo apt-get install pptp-linux

然后创建或编辑文件/etc/ppp/options.pptp,具体内容如下:

###############################################################################
# $Id: options.pptp,v 1.1 2005/02/18 01:40:23 quozl Exp $
#
# Sample PPTP PPP options file /etc/ppp/options.pptp
# Options used by PPP when a connection is made by a PPTP client.
# This file can be referred to by an /etc/ppp/peers file for the tunnel.
# Changes are effective on the next connection.  See "man pppd".
#
# You are expected to change this file to suit your system.  As
# packaged, it requires PPP 2.4.2 or later from http://ppp.samba.org/
# and the kernel MPPE module available from the CVS repository also on
# http://ppp.samba.org/, which is packaged for DKMS as kernel_ppp_mppe.
###############################################################################
 
# Lock the port
lock
 
# Authentication
# We don't need the tunnel server to authenticate itself
noauth
 
# We won't do EAP, CHAP, or MSCHAP, but we will accept MSCHAP-V2
refuse-eap
refuse-chap
refuse-mschap
 
# Compression
# Turn off compression protocols we know won't be used
nobsdcomp
nodeflate
 
# Encryption
# (There have been multiple versions of PPP with encryption support,
# choose with of the following sections you will use.  Note that MPPE
# requires the use of MSCHAP-V2 during authentication)
 
# http://ppp.samba.org/ the PPP project version of PPP by Paul Mackarras
# ppp-2.4.2 or later with MPPE only, kernel module ppp_mppe.o
# {{{
# Require MPPE 128-bit encryption
require-mppe-128
# }}}
 
# http://polbox.com/h/hs001/ fork from PPP project by Jan Dubiec
# ppp-2.4.2 or later with MPPE and MPPC, kernel module ppp_mppe_mppc.o
# {{{
# Require MPPE 128-bit encryption
#mppe required,stateless
# }}}

接下来创建或编辑用户名及密码文件/etc/ppp/chap-secrets, 内容如下:

# Secrets for authentication using CHAP
# client	server	secret			IP addresses
username 	PPTP	password	*

完成之后,需要创建文件/etc/ppp/peers/$TUNNEL,注意,这里的$TUNNEL指的是你将来用pon [参数],中的参数名。我们可以把它设定为vpn, 或你公司的名字。文件内容如下:

pty "pptp $VPN_SERVER_URL --nolaunchpppd" #VPN_SERVER_URL应该为你的VPN服务器地址
name $USERNAME                                      #你的VPN账号
remotename PPTP
require-mppe-128
file /etc/ppp/options.pptp
ipparam $TUNNEL                                       #这里是vpn,或你公司的名字

完成之后就可以使用命令:

pon $TUNNEL  #你自己设定的名称

来启动拨号连接。有些时候可能会有问题,如果权限不够,请用root来启动。提供了一种排错的启动方式,可以用一下命令启动连接,查看问题原因:

pon $TUNNEL debug dump logfd 2 nodetach

连接成功了,可是发现我上网的数据并没有通过VPN服务器,肯定是路由表的问题了,接下来需要设定内核路由表。
首先保证连接到VPN服务器的路由正常,数据包必须从原网络适配器出发,走ISP的网关。比如我用的是3G无线上网,所以我默认的网络接口为ppp0, 新的VPN连接的网络接口被分配为ppp1. 通过route命令添加一条路由:

sudo route add -host vpn.server.com dev ppp0

这样就保证了从本机到VPN服务器链路的正常,没有这条路由,会导致数据包在PPP1网络接口上的死循环。
然后删除就的default路由信息和加入新的default路由:

sudo route del default dev ppp0
sudo route add default dev ppp1

好了,这样就完成了路由的转换,以后所有上网的数据包都回通过VPN服务器了。打开浏览器,却发现什么网也上不了,用Ping进行测试,提示Unknow server, 呵呵,肯定是DNS又连接不上了,一定是ISP限定了只有从内网出发的机器才能访问该DNS服务器。于是用

more /etc/resov.conf

查出DNS服务器地址为

nameserver 79.170.225.139
nameserver 79.170.224.1

并用route查出ISP内网中我的网关地址为10.87.249.85。route命令执行后的显示为:

Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
10.64.64.64     *               255.255.255.255 UH    0      0        0 ppp0
10.89.234.1     *               255.255.255.255 UH    0      0        0 ppp1
10.87.249.85    *               255.255.255.255 UH    0      0        0 ppp0
default         *               0.0.0.0         U     0      0        0 ppp1

从以上的路由表也可以看出,VPN服务器的内网网关地址为10.89.234.1。接下来添加到达DNS服务器的路由:

sudo route add -host 79.170.225.139 gw 10.87.249.85
sudo route add -host 79.170.224.1 gw 10.87.249.85

再次用Ping测试,一切正常了,浏览器也能顺利的打开网页,进入IP查询网站和水木,均显示IP不是我的真实IP,宣告配置成功。
参考文献:
http://pptpclient.sourceforge.net/howto-debian.phtml#install

LINUX多核处理多线程并行编译

对于很多使用多处理器(或核心)的现代系统, 通过设置一个环境变量或告诉 make 程序有几个可用的处理器,以执行”并行编译“可以减少编译一个软件包的时间,比如,一个 Core2Duo 通过下面的设置,可以支持两个并行的处理器:

?View Code SCRIPT
set MAKEFLAGS=’-j 2′

或仅仅在编译时使用下列参数:

make -j2