前言:
近期在学习过程中,需为 Windows 11 环境下的 PostgreSQL 16 配置中文分词功能,核心依赖 zhparser 插件。然而,网上现存教程多为多年前的旧版本适配方案,与 PostgreSQL 16 + Windows 11 的新环境存在兼容性差异,可参考性有限。经过多次查阅资料、排查编译报错与环境配置问题,最终成功完成插件的编译与部署。现将完整实操经验整理分享,希望能为有同样需求的开发者避坑。
配置环境:
- Windows 11 64位
- PostgreSQL 16
- Visual Studio 2026
1. scws和zhparser源码下载
scws中文分词库(zhparser依赖)下载地址:github.com/hightman/sc… zhparser工程下载地址:github.com/kerneltrave… (已使用vs改进)
2. 将scws文件夹和zhparser文件夹放在同一层目录下(注意名称)
- 打开文件夹 scws\win32\scws.sln文件,将工程文件自动升级到当前可用版本。
- 打开文件夹 zhparser-master\zhparser.sln文件,将工程文件自动升级到当前可用版本。(工具集版本不匹配提示:把下拉框里的「忽略」改成 「更新为当前已安装的工具集」)。
- 添加文件目录:在解决方案资源管理器中右键点击libscws,依次选择属性→C/C++→常规,在右侧附加包含目录中,添加对应路径(请根据自身实际路径调整)。
D:\project\vsProject\scws\libscws
D:\software\PostgreSQL\16\include
D:\software\PostgreSQL\16\include\server
D:\software\PostgreSQL\16\include\server\utils
D:\software\PostgreSQL\16\include\server\port
D:\software\PostgreSQL\16\include\server\port\win32
D:\software\PostgreSQL\16\include\server\port\win32_msvc
%(AdditionalIncludeDirectories)
- 若你使用 64 位系统,可继续在该属性页面操作:点击窗口右上角的配置管理器,新建活动解决方案平台,选择新平台为x64,点击确定即可。
- 为了避免生成报错,找到打开xdict.h文件,将其中的中文注释删除。
- 找到config_win32.h文件,注释掉以下logf定义的代码行。
- 打开 scws.h,加入以下代码块,否则编译成功后对应文件夹没有lib文件。
#ifdef _WIN32
#ifdef LIBSCWS_EXPORTS
#define SCWS_API __declspec(dllexport)
#else
#define SCWS_API __declspec(dllimport)
#endif
#else
#define SCWS_API
#endif
scws.h完整版替换: 因为DLL 是一个动态库,如果你的代码里没有明确告诉编译器“我想把哪些函数公开给外部使用”,链接器就不会生成对应的 .lib(导入库)文件。
/**
* @file scws.h (core include)
* @author Hightman Mar
* @editor set number ; syntax on ; set autoindent ; set tabstop=4 (vim)
* $Id$
*/
#ifndef _SCWS_LIBSCWS_20070531_H_
#define _SCWS_LIBSCWS_20070531_H_
/* Windows 导出/导入宏定义 */
#ifdef _WIN32
#ifdef LIBSCWS_EXPORTS
#define SCWS_API __declspec(dllexport)
#else
#define SCWS_API __declspec(dllimport)
#endif
#else
#define SCWS_API
#endif
#ifdef __cplusplus
extern "C" {
#endif
#include "version.h"
#include "rule.h"
#include "xdict.h"
#define SCWS_IGN_SYMBOL 0x01
//#define SCWS_SEG_MULTI 0x02
//#define SCWS_XDB_USAGE 0x04
#define SCWS_DEBUG 0x08
#define SCWS_DUALITY 0x10
/* hightman.070901: multi segment policy */
#define SCWS_MULTI_NONE 0x00000 // nothing
#define SCWS_MULTI_SHORT 0x01000 // split long words to short words from left to right
#define SCWS_MULTI_DUALITY 0x02000 // split every long words(3 chars?) to two chars
#define SCWS_MULTI_ZMAIN 0x04000 // split to main single chinese char atr = j|a|n?|v?
#define SCWS_MULTI_ZALL 0x08000 // attr = ** , all split to single chars
#define SCWS_MULTI_MASK 0xff000 // mask check for multi set
#define SCWS_ZIS_USED 0x8000000
#define SCWS_YEA (1)
#define SCWS_NA (0)
/* data structures */
typedef struct scws_result *scws_res_t;
struct scws_result
{
int off;
float idf;
unsigned char len;
char attr[3];
scws_res_t next;
};
typedef struct scws_topword *scws_top_t;
struct scws_topword
{
char *word;
float weight;
short times;
char attr[2];
scws_top_t next;
};
struct scws_zchar
{
int start;
int end;
};
typedef struct scws_st scws_st, *scws_t;
struct scws_st
{
xdict_t d;
rule_t r;
unsigned char *mblen;
unsigned int mode;
unsigned char *txt;
int zis;
int len;
int off;
int wend;
scws_res_t res0;
scws_res_t res1;
word_t **wmap;
struct scws_zchar *zmap;
};
/* api: init the scws handler */
SCWS_API scws_t scws_new();
SCWS_API void scws_free(scws_t s);
/* fork instance for multi-threaded usage, but they shared the dict/rules */
SCWS_API scws_t scws_fork(scws_t s);
/* mode = SCWS_XDICT_XDB | SCWS_XDICT_MEM | SCWS_XDICT_TXT */
SCWS_API int scws_add_dict(scws_t s, const char *fpath, int mode);
SCWS_API int scws_set_dict(scws_t s, const char *fpath, int mode);
SCWS_API void scws_set_charset(scws_t s, const char *cs);
SCWS_API void scws_set_rule(scws_t s, const char *fpath);
/* set ignore symbol or multi segments */
SCWS_API void scws_set_ignore(scws_t s, int yes);
SCWS_API void scws_set_multi(scws_t s, int mode);
SCWS_API void scws_set_debug(scws_t s, int yes);
SCWS_API void scws_set_duality(scws_t s, int yes);
SCWS_API void scws_send_text(scws_t s, const char *text, int len);
SCWS_API scws_res_t scws_get_result(scws_t s);
SCWS_API void scws_free_result(scws_res_t result);
SCWS_API scws_top_t scws_get_tops(scws_t s, int limit, char *xattr);
SCWS_API void scws_free_tops(scws_top_t tops);
SCWS_API scws_top_t scws_get_words(scws_t s, char *xattr);
SCWS_API int scws_has_word(scws_t s, char *xattr);
#ifdef __cplusplus
}
#endif
#endif
- 点击生成,就可以获得
libscws.lib
4. 编译zhparser
- 添加文件目录:在解决方案资源管理器中右键点击zhparser,依次选择属性→C/C++→常规,在右侧附加包含目录中,添加对应路径(请根据自身实际路径调整)。
D:\project\vsProject\scws\libscws
D:\software\PostgreSQL\16\include
D:\software\PostgreSQL\16\include\server
D:\software\PostgreSQL\16\include\server\utils
D:\software\PostgreSQL\16\include\server\port
D:\software\PostgreSQL\16\include\server\port\win32
D:\software\PostgreSQL\16\include\server\port\win32_msvc
%(AdditionalIncludeDirectories)
- 在属性属性→链接器→常规→** 附加库目录**添加以下路径。
D:\software\PostgreSQL\16\lib
D:\project\vsProject\zhparser-master
%(AdditionalLibraryDirectories)
- 右键zhparser→属性→链接器→输入→附加依赖项→编辑。清空现有内容,输入以下 2 个库名(直接输名字,不用带路径,链接器会通过上面的库目录找到),点击确定保存。
postgres.lib
libscws.lib
- 将 PostgreSQL 安装目录下的
postgres.lib文件,与 scws 编译完成后生成的libscws.lib文件,一同拷贝至 zhparser 项目根目录,并替换目录中原有的同名旧文件。 - 打开 zhparser 项目中的
zhparser.c文件,在文件最顶部(所有#include之后、第一个函数之前)粘贴以下代码,定义pstrdup_call函数,替换pstrdup。
// 自定义pstrdup替代函数,适配PG16
char *pstrdup_call(const char *src)
{
char *dst;
int len;
if (src == NULL)
return NULL;
len = strlen(src) + 1;
dst = (char *) palloc(len);
memcpy(dst, src, len);
return dst;
}
- 点击生成,就可以获得
zhparser.dll
5.补全文件
- 将
zhparser.dll复制至 PostgreSQL 安装目录的 lib 文件夹下。 - 将 zhparser-master 目录下的
zhparser.control、zhparser--1.0.sql、zhparser--unpackaged--1.0.sql三个文件,拷贝到 PostgreSQL 安装目录的 share\extension 文件夹下。 - 将
rules.utf8.ini和dict.utf8.xdb两个文件,拷贝至 PostgreSQL 安装目录的 \share\tsearch_data 文件夹下。
6.测试插件
- 拷贝完文件后,必须重启 PostgreSQL 16 服务(仅重启 psql/pgAdmin 没用,服务未加载新拷贝的 DLL),步骤如下:
- 按下Win+R,输入services.msc,回车打开「服务」窗口;
- 在服务列表中找到 postgresql-x64-16(PG16 的默认服务名);
- 右键→「重启」,等待服务重启完成(约 10 秒);
- 重新打开 psql,连接你的数据库。
- 服务重启后,在 psql 中再次执行。
CREATE EXTENSION zhparser;
- 成功!