跨平台的.net文件生成器

之前写过一个生成 pajek 所需的 .net 格式文件的一个程序,当时是用 C++ 在 Visual Studio 2008 环境上写的。最近将其改造成了跨 MacOSX,Windows,Linux 三个平台的命令行工程,在此,记录一下跨平台的相关内容,并对 CreateNetFile 做相关介绍,以便对正在做复杂网络的同学有所帮助。

1. 跨平台过程


1.1 创建各个平台对应的工程文件

MacOSX:创建一个空的命令行工程,然后将 C++ 的源文件导入进去,debug。

Linux:编写makefile。因为MaxOSX 环境与 Linux 环境其实差不多,并且也预装了 make 、g++ 等工具,所以创建的 makefile 最初在 Mac 上调试就可以了。迁移到 Linux 基本没啥问题。

Windows:因为之前的工程使用 VS2008 创建的,而如今微软流行的版本已经到了 VS2015,所以按照 VS2015 的提示升级了工程,解决了一些 bug。


1.2 在字符集上遇到的问题

最跨平台最先遇到的是字符集的问题。Windows 流行较早,最初是通过各国的代码页解决的地域显示问题。而后来 Unicode 流行起来,VS 为了兼容之前的字符集,提供了『多字节编码』与『Unicode字符集』两种工程配置方案。『多字节编码』是 char,而『Unicode字符集』 使用的是 wchar_t而方案底层体现在对 C++ 内置字符的宏定义上。如下:

typedef wchar_t WCHAR; // wc, 16-bit UNICODE character
#ifdef _UNICODE //而对于TCHAR来说,其定义跟工程的字符集设置有关
typedef WCHAR TCHAR;
#else
typedef char TCHAR;
#endif

最初用 VS2008 创建的版本使用了 TCHAR,用以集中管理使用了代码页的工程以及使用了Unicode字符集的工程,这个宏在 Xcode 平台是不支持的。之前 VS 工程中使用的 wchar_t 居多,而 wchar_t 其实在后来被认为是一种不佳的设计,因此我将工程中使用 wchar_t 的部分统一替成了 char.
以上是代码语法中存在的字符编码问题,源文件本身的编码也有影响。Xcode 无法识别 GBK、UTF-16,可以识别 UTF8;VS 可识别 UTF-16 或者带 BOM 的UTF8。因此,最终统一的数据格式只能是 带BOM 的 UTF8。Windows 上 Notepad++ 带有编码转换的命令,可以使用它来将所有的源文件转换成带 BOM 的UTF8 文件,如此,Xcode 与 VS 方能正确处理。

而对于数据文件,又有不同。如果使用了错误的格式,处理出来的数据集可能会出现乱码。对于将要处理的文本,如果加上BOM,会结合后面的字符生成一个新的词语,比如 “中国” 和 [BOM]”中国” 显示上是一样的,但是却会被当做两个词语,所以将要处理的文本中不应该有 BOM 标志。

除了运行时字符变量的编码、源文件的编码、数据文件的编码之外,还有文件路径的编码,由于在改改造成跨平台的过程中并没遇到太大问题,故搁下不谈。

关于字符编码,可部分参考我之前写的一篇文章


1.3 平台依赖的函数库的处理

比如 strcpy_s 这个函数,仅在 Windows 平台可以,而在 Linux 与 MacOSX,就只能使用 strcpy;_tmain 也是只能在 Windows 平台使用,在其他平台就必须屏蔽掉了;后面将要提到的 <unistd.h>,只有 Linux/MacOSX 才有,在 Windows 也没有找到对应的解决方案。而这些,就只能通过预编译宏来区别。设想是在不同的编译环境中自动配置上相关的预编译宏,不过目前尚未实现;如果想切换平台编译代码,一定记着把 stdafx.h 文件中定义的平台宏给对应处理了。否则必然会出错。


1.4 在编写makefile遇到的坑

很少用 makefile 编写 build 说明,这次也试了一把,遇到了不少问题。比如 VSCode 默认情况下会将 <Tab> 变换成4个空格,直观上又看不出来,而这在写 makefile 中的命令时直接导致运行不过;比如在 makefile 中生成目录,并使中间对象依赖于这个对象,并且创建目录需要使用级联的命令 mkdir -p build;纯粹手工来写 makefile 确实费劲,没有使用 cmake 等高级一些的工具。


1.5 工程目录的整理

最初工程目录文件糅杂在一块,分不清彼此。后来清理了一下,在根层级形成了如下结构目录:CreateNetFile、Data、Linux、MacOS、Windows。而一般布置跨平台项目时,也基本是这个目录结构。除此之外,还需要把一些临时文件从版本库中 ignore 掉。一种简单的方法是从 github 上拉取各个 IDE 应该忽略的内容,然后把他们拼合放在跨平台工程的 .gitignore 文件中。


1.6 编译后的资源拷贝

因为 CreateNetFile 是用来处理数据的,所以最好在调试的时候就可以自动地链接数据。这里要区分『工程目录』与『运行时目录』两个概念。『工程目录』一般对应的是工程文件(makefile,  .xcodeproj, .sln)所在的目录,而『运行时目录』是编译出来的运行文件所在的目录。由于默认情况下 CreateNetFile 是取的运行时目录中的数据文件,所以编译完成之后最好能把数据拷贝一份到这里。而 Xcode 在 Build Phase 中提供了 Copy Files 命令,VS 在 项目属性中提供了后期生成事件,Linux 在 makefile 中可以直接写拷贝数据的命令。这样就使得编译与运行可以一并进行,极大地方便了调试。


1.7 命令行支持参数

既然是命令行工具,最好能够处理命令行中的参数。使用 getopt() 便捷地增加了对 -f 参数的支持。然而 getopt() 所依赖的 <unistd.h> 并未存在于 Windows 平台,最终也没有找到好的解决方案,所以 Windows 平台下,CreateNetFile 是无法通过 -f 参数指定输入文件的。


2. 开源工程

工程源码相对简单,处理高效地创建 .net 格式文件再无其他。开源放在了这里:https://github.com/unash/CreateNetFile


2.1 编译工具

  • Xcode 7.2.1
  • Visual Studio Community 2015
  • g++ 4.8.2

在重新使用 VS 时发现微软在开源道路上走得越来越远了,赞。VSCode 很好用,C#的跨平台也很牛逼。


2.2 工程使用

下载下来工程之后,直接进入对应的平台,修改宏为你所用的平台,

//#define PLATFORM_MACOSX
#define PLATFORM_WINNT
//#define PLATFORM_LINUX

然后编译。进入运行文件所在目录,

a. 对于 Linux/MacOSX ,运行 ./CreateNetFile -f tab_file_test.txt,就可以处理了。

b. 对于 Windows,需要把数据文件命名为tab_file_test.txt,双击 CreateNetFile.exe 即可以处理

tab_file_test.txt 数据集比较小,你可以使用 tab_file_real.txt 大数据集试试 CreateNetFile 的速度。

工程有一些可配的参数,目前写死在源码里,如果处理非常大的数据文件,可能会要求修改这些参数:

const int LINE_LEN=5000;    //文件的一行有200字节
const int LINE_NUM=1000000;    //文件的最大行数
const int ARC_NUM=300000;    //最大的节点个数
const int ARC_HAS_EDGE=15000;    //一个节点拥有的最大的边数
const int ARC_PER_LINE=3000;    //一行存在的节点数
const int EDGE_NUM=1000000;

2.3 输入文件

必须是 <Tab> 分隔的文本文件,要求 不带 BOM 的 UTF8 编码。


2.4 输出文件

  • FreqFile.txt,统计出来的网络节点的词频结果。
  • NetFile.net,用于给 pajek 处理的数据文件。

2.5 其他

为方便使用,我编译出了3个平台的可运行软件放到了百度云盘,请自行下载。

注:如果在 MacOSX/Linux 上无法正确识别命令,请用 sudo chmod x+u CreateNetFile_mac64bit  命令为程序添加执行功能。

Tagged with: , , , , , ,

发表评论

邮箱地址不会被公开。 必填项已用*标注

*