# LangSC 使用手册

**LangSC**（Language Structure Computation）是一个统一的 Python 包，集成了自然语言结构分析、语料库查询和 JSON 结构检索三大功能。

---

## 目录

- [1. 概述](#1-概述)
- [2. 安装与配置](#2-安装与配置)
- [3. 快速上手](#3-快速上手)
- [4. GPF 类 — 自然语言结构分析](#4-gpf-类--自然语言结构分析)
  - [4.1 构造函数与初始化](#41-构造函数与初始化)
  - [4.2 自动索引机制](#42-自动索引机制)
  - [4.3 NLP 分析](#43-nlp-分析)
  - [4.4 文本与网格操作](#44-文本与网格操作)
  - [4.5 Unit（语言单元）操作](#45-unit语言单元操作)
  - [4.6 Relation（关系）操作](#46-relation关系操作)
  - [4.7 Grid KV（网格属性）](#47-grid-kv网格属性)
  - [4.8 Table（数据表）操作](#48-table数据表操作)
  - [4.9 FSA（有限状态自动机）](#49-fsa有限状态自动机)
  - [4.10 索引创建](#410-索引创建)
  - [4.11 服务调用与 Dot 可视化](#411-服务调用与-dot-可视化)
  - [4.12 结构添加](#412-结构添加)
  - [4.13 可视化输出](#413-可视化输出)
  - [4.14 其他方法](#414-其他方法)
  - [4.15 GPF 方法速查表](#415-gpf-方法速查表)
- [5. BCC 类 — 语料库查询](#5-bcc-类--语料库查询)
  - [5.1 构造函数与初始化](#51-构造函数与初始化)
  - [5.2 自动索引机制](#52-自动索引机制)
  - [5.3 语料查询](#53-语料查询)
  - [5.4 KV 操作](#54-kv-操作)
  - [5.5 语料索引](#55-语料索引)
  - [5.6 BCC 方法速查表](#56-bcc-方法速查表)
- [6. JSS 类 — JSON 结构检索](#6-jss-类--json-结构检索)
  - [6.1 构造函数与初始化](#61-构造函数与初始化)
  - [6.2 自动索引机制](#62-自动索引机制)
  - [6.3 数据目录结构](#63-数据目录结构)
  - [6.4 cfg 配置文件格式](#64-cfg-配置文件格式)
  - [6.5 cfg 自动生成规则](#65-cfg-自动生成规则)
  - [6.6 查询](#66-查询)
  - [6.7 资源释放](#67-资源释放)
  - [6.8 JSS 方法速查表](#68-jss-方法速查表)
- [7. 完整示例](#7-完整示例)
- [8. 数据文件格式说明](#8-数据文件格式说明)
- [9. 平台与依赖](#9-平台与依赖)
- [10. 参考文献](#10-参考文献)

---

## 1. 概述

LangSC 包含三个相互独立的核心类：

| 类 | 功能 | 底层库 | 索引日志 |
|---|------|--------|----------|
| **GPF** | 基于网格（Lattice/Grid）的自然语言结构分析框架，支持分词、词性标注、句法分析、FSA 规则匹配、数据表查询和可视化 | `gpflib` | `IdxLog_GPF.txt` |
| **BCC** | BCC 语料库查询引擎，支持词频统计、上下文检索、计数查询和语料索引 | `bcclib` | `IdxLog_BCC.txt` |
| **JSS** | JSON 结构化数据检索引擎，支持 SQL 风格查询、多表管理和自动建索引 | `jsslib` | `IdxLog_JSS.txt` |

三个类**相互独立**，按需导入即可，不会加载多余的动态库。例如只使用 JSS 时，不会加载 `gpflib` 和 `bcclib`。

### 架构设计

```
LangSC/
├── __init__.py        # 导出 GPF, BCC, JSS, __version__
├── __version__.py     # 版本号
├── _utils.py          # 共享工具函数（编码检测、索引日志管理等）
├── gpf.py             # GPF 类（封装 gpflib）
├── bcc.py             # BCC 类（封装 bcclib）
├── jss.py             # JSS 类（封装 jsslib）
├── gpflib.dll / libgpflib.so / libgpflib.dylib
├── bcclib.dll / libbcclib.so / libbcclib.dylib
├── jsslib.dll / libjsslib.so / libjsslib.dylib
└── (数据文件: GPFconfig.txt, BCCconfig.txt, Parser.lua, Segment.dat, idxPOS.dat, base.lex)
```

---

## 2. 安装与配置

### 2.1 安装

```bash
pip install LangSC
```

### 2.2 依赖

| 依赖包 | 用途 | 说明 |
|--------|------|------|
| `pywin32` | Windows DLL 内存释放 | 仅 Windows 平台需要 |
| `wordcloud` | 词云可视化 | GPF 的 ShowCloud 方法使用 |
| `requests` | HTTP 请求 | GPF 的 DotFile/DotBuff 远程服务使用 |
| `chardet` | 文件编码自动检测 | 全部三个类使用 |

### 2.3 系统要求

- **Python** >= 3.6
- **操作系统**：Windows、Linux、macOS
- **Graphviz**（可选）：GPF 可视化输出需要安装 Graphviz 的 `dot` 命令，或使用包内自带的 `Graph/dot` 工具

### 2.4 版本查询

```python
import LangSC
print(LangSC.__version__)  # 例如 "1.0.1"
```

---

## 3. 快速上手

```python
# GPF：分词
from LangSC import GPF
gpf = GPF("./data")
result = gpf.Segment("自然语言处理是人工智能的重要方向")
print(result)  # "自然 语言 处理 是 人工 智能 的 重要 方向"

# BCC：词频查询
from LangSC import BCC
bcc = BCC("./corpus")
freq = bcc.RunBCC("学习", Command="Freq", Number=10)
print(freq)

# JSS：SQL 查询
from LangSC import JSS
jss = JSS("./table")
results = jss.RunJSS("SELECT TOP 5 id, ci FROM ci WHERE ci = '爱';")
for row in results:
    print(row)
jss.Terminate()
```

---

## 4. GPF 类 — 自然语言结构分析

GPF（Grid-based Parsing Framework）提供基于网格的自然语言结构分析能力。核心数据结构是**网格（Grid/Lattice）**，其中包含**语言单元（Unit）**和**关系（Relation）**，支持文本分析、规则匹配、词表查询和结构可视化。

### 4.1 构造函数与初始化

```python
from LangSC import GPF

gpf = GPF(dataPath="./data")
```

**参数**

| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `dataPath` | `str` | `"./data"` | 数据目录路径，包含 Table 和 FSA 文件 |

**说明**
- 构造函数自动初始化 GPF 网格句柄（`hHandleGPF`）和 CRF/POS 句柄（`hHandleCRFPOS`）
- 自动加载 CRF 分词模型（`Segment.dat`）和词性标注数据（`idxPOS.dat`）
- 自动检测数据目录状态并执行索引（详见 [4.2 自动索引机制](#42-自动索引机制)）
- `dataPath` 中的反斜杠会被自动转换为正斜杠

### 4.2 自动索引机制

构造函数调用 `_init_gpf_data(dataPath)` 自动处理数据索引：

1. 检查 `dataPath` 中是否存在 `IdxLog_GPF.txt` 索引日志
2. **若存在**：说明已是索引目录，直接使用
3. **若不存在**：视为原始数据目录
   - 扫描目录下的 Table 和 FSA 文件
   - 创建 `dataPathIdx/` 索引目录
   - 逐一索引 Table 和 FSA 文件，并记录到 `IdxLog_GPF.txt`
4. 通过文件时间戳比较（`is_same`），避免重复索引

> 索引完成后，`self.dataPath` 指向实际的索引目录路径。

### 4.3 NLP 分析

#### `Segment(text, table="")`

对文本执行 CRF 分词。

```python
result = gpf.Segment("自然语言处理是人工智能的重要方向")
# 返回: "自然 语言 处理 是 人工 智能 的 重要 方向"
```

| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `text` | `str` | （必填） | 待分词的文本 |
| `table` | `str` | `""` | 用户词表名称。为空时使用 CRF 模型分词；指定时使用用户词表辅助分词 |

**返回值**：`str`，以空格分隔的分词结果。

**说明**
- 首次调用时自动初始化 CRF 模型（线程安全）
- 若指定 `table`，会先调用 `SetGridText` 设置文本，再使用 `GPF_GridSegUser` 进行带用户词表的分词

#### `POS(text, table="")`

对文本执行词性标注（先分词，再标注）。

```python
result = gpf.POS("自然语言处理是人工智能的重要方向")
# 返回: "自然/b 语言/n 处理/v 是/v 人工/b 智能/n 的/u 重要/a 方向/n"
```

| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `text` | `str` | （必填） | 待标注的文本 |
| `table` | `str` | `""` | 用户词表名称 |

**返回值**：`str`，格式为 `词/词性` 以空格分隔。

**说明**
- 首次调用时自动初始化 POS 模型（线程安全）
- 内部先调用 `Segment` 分词，再调用 `GPF_POS` 标注

#### `Parse(text, **Others)`

统一解析接口，根据 `Structure` 参数调用不同的解析方式。

```python
# 分词
result = gpf.Parse("自然语言处理", Structure="Segment")

# 词性标注
result = gpf.Parse("自然语言处理", Structure="POS")

# 句法树（需要远程服务）
result = gpf.Parse("自然语言处理", Structure="Tree")

# 指定用户词表
result = gpf.Parse("自然语言处理", Structure="Segment", Table="myDict")

# 通过 Web 服务解析
result = gpf.Parse("自然语言处理", Structure="POS", Web=True)
```

| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `text` | `str` | （必填） | 待解析文本（自动截断至 1024 字符） |
| `Structure` | `str` | `"POS"` | 解析类型：`"Segment"` / `"POS"` / `"Tree"` / `"Chunk"` 等 |
| `Web` / `IsWeb` | `bool` | `False` | 是否通过 Web 服务解析 |
| `Table` | `str` | `""` | 用户词表名称 |

**返回值**：`str`，JSON 格式字符串。
- `Segment`：`["词1", "词2", ...]`
- `POS`：`["词1/POS1", "词2/POS2", ...]`
- 其他：由远程服务返回的 JSON 结构

### 4.4 文本与网格操作

GPF 的核心数据结构是**网格（Grid）**，网格承载文本、语言单元（Unit）和关系（Relation）。

#### `SetText(text)` / `SetGridText(text)`

设置网格的文本内容。

```python
gpf.SetText("今天天气很好")
# 或
gpf.SetGridText("今天天气很好")  # 等价，但额外调用 GPFInit
```

| 参数 | 类型 | 说明 |
|------|------|------|
| `text` | `str` | 文本内容 |

**返回值**：`int`，操作状态码。

#### `GetText(begin=0, end=-1)` / `GetGridText(begin=0, end=-1)`

获取网格中的文本。

```python
text = gpf.GetText()           # 获取全部文本
text = gpf.GetText(0, 3)       # 获取位置 0 到 3 的文本
text = gpf.GetGridText()       # 等价于 GetText()
```

| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `begin` | `int` | `0` | 起始位置 |
| `end` | `int` | `-1` | 结束位置（`-1` 表示全部） |

**返回值**：`str`，文本内容。

#### `GetGrid()` / `GetStructure()`

获取完整的网格结构（JSON 格式）。

```python
grid = gpf.GetGrid()        # 返回网格的 JSON 表示
grid = gpf.GetStructure()   # 等价
```

**返回值**：`dict` / `list`，网格结构的 JSON 对象。若网格为空返回 `{}`。

### 4.5 Unit（语言单元）操作

Unit 是网格中的基本语言单元，对应一个词或短语，具有位置信息和键值属性。

#### `AddUnit(text, colNo=-1)`

向网格中添加一个 Unit。

```python
unitNo = gpf.AddUnit("天气")           # 自动定位
unitNo = gpf.AddUnit("天气", colNo=3)  # 指定列号（文本中的字符位置）
```

| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `text` | `str` | （必填） | Unit 的文本内容 |
| `colNo` | `int` | `-1` | 列号（字符位置）。`-1` 时自动查找文本位置 |

**返回值**：`str`，Unit 编号（如 `"(0,2)"`），后续操作通过此编号引用该 Unit。

#### `AddUnitKV(unitNo, key, val)`

为 Unit 添加键值属性。

```python
gpf.AddUnitKV(unitNo, "POS", "n")         # 添加词性
gpf.AddUnitKV(unitNo, "Tag", "人名;地名")  # 多值用分隔符
```

| 参数 | 类型 | 说明 |
|------|------|------|
| `unitNo` | `str` | Unit 编号 |
| `key` | `str` | 属性名 |
| `val` | `str` | 属性值。支持以空格、分号、逗号、制表符分隔的多值，每个值分别添加 |

**返回值**：`int`，固定返回 `1`。

#### `GetWord(unitNo)`

获取 Unit 的文本内容。

```python
word = gpf.GetWord(unitNo)  # 例如 "天气"
```

| 参数 | 类型 | 说明 |
|------|------|------|
| `unitNo` | `str` | Unit 编号 |

**返回值**：`str`，Unit 的文本内容。

#### `GetUnit(kv, UnitNo="", UExpress="")` / `GetUnits(kv, UnitNo="", UExpress="")`

按条件查找 Unit。

```python
units = gpf.GetUnit("POS=n")           # 查找所有词性为 n 的 Unit
units = gpf.GetUnits("POS=n")          # 同上
unit  = gpf.GetUnit(0)                 # 按 FSA 路径号获取（整数参数）
```

| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `kv` | `str` 或 `int` | （必填） | 查询条件（如 `"POS=n"`）或 FSA 路径号（整数） |
| `UnitNo` | `str` | `""` | 限定在某个 Unit 范围内查找 |
| `UExpress` | `str` | `""` | Unit 表达式 |

**返回值**：
- 查找模式：`list` / `dict`，匹配的 Unit 列表。无匹配返回 `0`
- FSA 路径号模式：`str`，Unit 编号

#### `GetUnitKV(unitNo, key="")` / `GetUnitKVs(unitNo, key="")`

获取 Unit 的属性值。

```python
kvs = gpf.GetUnitKV(unitNo)              # 获取全部属性 → dict
val = gpf.GetUnitKV(unitNo, "POS")       # 获取指定属性 → list
word = gpf.GetUnitKV(unitNo, "Word")     # 获取文本 → str
pos = gpf.GetUnitKV(unitNo, "From")      # 获取起始位置 → int
```

| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `unitNo` | `str` | （必填） | Unit 编号 |
| `key` | `str` | `""` | 属性名。为空返回全部属性 |

**返回值**：
| `key` 值 | 返回类型 | 说明 |
|-----------|----------|------|
| `""` | `dict` | 全部属性键值对 |
| `"Word"` / `"HeadWord"` | `str` | 文本内容（无匹配返回 `""` |
| `"From"` / `"To"` | `int` | 位置信息（无匹配返回 `-1`） |
| 其他 | `list` | 属性值列表 |

#### `GetFSAUnit(pathNo)`

按 FSA 路径号获取 Unit。

```python
unit = gpf.GetFSAUnit(0)  # 获取第 0 条路径对应的 Unit
```

| 参数 | 类型 | 说明 |
|------|------|------|
| `pathNo` | `int` | FSA 路径号 |

**返回值**：`str`，Unit 编号。

#### `IsUnit(unitNo, kv)`

判断 Unit 是否具有指定属性。

```python
ret = gpf.IsUnit(unitNo, "POS=n")      # 判断词性是否为 n
ret = gpf.IsUnit(unitNo, "Type=Word")   # 判断类型是否为 Word
```

| 参数 | 类型 | 说明 |
|------|------|------|
| `unitNo` | `str` | Unit 编号 |
| `kv` | `str` | 属性条件，格式为 `"Key=Value"` |

**返回值**：`int`，匹配返回非零值，不匹配返回 `0`。

### 4.6 Relation（关系）操作

Relation 表示两个 Unit 之间的有向关系（如主谓、动宾等）。

#### `AddRelation(unitNo1, unitNo2, role)`

添加关系。

```python
gpf.AddRelation(unitNo1, unitNo2, "SBJ")  # unitNo1 → unitNo2, 关系为 SBJ（主语）
```

| 参数 | 类型 | 说明 |
|------|------|------|
| `unitNo1` | `str` | 源 Unit 编号（关系起点） |
| `unitNo2` | `str` | 目标 Unit 编号（关系终点） |
| `role` | `str` | 关系类型（如 `"SBJ"`, `"OBJ"`, `"ATT"` 等） |

**返回值**：`int`，固定返回 `1`。

#### `AddRelationKV(unitNo1, unitNo2, role, key, val)`

为关系添加键值属性。

```python
gpf.AddRelationKV(unitNo1, unitNo2, "SBJ", "Confidence", "0.95")
```

| 参数 | 类型 | 说明 |
|------|------|------|
| `unitNo1` | `str` | 源 Unit 编号 |
| `unitNo2` | `str` | 目标 Unit 编号 |
| `role` | `str` | 关系类型 |
| `key` | `str` | 属性名 |
| `val` | `str` | 属性值（支持多值分隔） |

**返回值**：`int`，固定返回 `1`。

#### `GetRelation(kv="")` / `GetRelations(kv="")`

获取关系列表。

```python
rels = gpf.GetRelation()               # 获取全部关系
rels = gpf.GetRelations("Role=SBJ")    # 按条件过滤
```

| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `kv` | `str` | `""` | 过滤条件（为空返回全部） |

**返回值**：`list`，关系列表。无关系返回 `[]`。

#### `GetRelationKV(unitNo1, unitNo2, role, key="")` / `GetRelationKVs(...)`

获取关系属性。

```python
kvs = gpf.GetRelationKV(unitNo1, unitNo2, "SBJ")          # 全部属性
kvs = gpf.GetRelationKVs(unitNo1, unitNo2, "SBJ", "key")  # 指定属性
```

| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `unitNo1` | `str` | （必填） | 源 Unit 编号 |
| `unitNo2` | `str` | （必填） | 目标 Unit 编号 |
| `role` | `str` | （必填） | 关系类型 |
| `key` | `str` | `""` | 属性名（为空返回全部） |

**返回值**：`dict` / `list`，属性键值对。

#### `IsRelation(unitNo1, unitNo2, role, kv="")`

判断两个 Unit 之间是否存在指定关系。

```python
ret = gpf.IsRelation(unitNo1, unitNo2, "SBJ")
```

| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `unitNo1` | `str` | （必填） | 源 Unit 编号 |
| `unitNo2` | `str` | （必填） | 目标 Unit 编号 |
| `role` | `str` | （必填） | 关系类型 |
| `kv` | `str` | `""` | 附加属性条件 |

**返回值**：`int`，存在返回非零值，不存在返回 `0`。

### 4.7 Grid KV（网格属性）

为整个网格（非某个 Unit）添加/获取全局属性。

#### `AddGridKV(key, val)` / `AddTextKV(key, val)`

添加网格级属性。

```python
gpf.AddGridKV("Author", "张三")
gpf.AddTextKV("Source", "新闻")   # 等价
```

| 参数 | 类型 | 说明 |
|------|------|------|
| `key` | `str` | 属性名 |
| `val` | `str` | 属性值（支持多值分隔） |

**返回值**：`int`，固定返回 `0`。

#### `GetGridKV(key="")` / `GetGridKVs(key="")` / `GetTextKV(key="")`

获取网格级属性。

```python
kvs = gpf.GetGridKV()           # 全部属性
kvs = gpf.GetGridKV("Author")   # 指定属性
kvs = gpf.GetTextKV("Source")   # 等价于 GetGridKVs
```

| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `key` | `str` | `""` | 属性名（为空返回全部） |

**返回值**：`dict` / `list`，属性键值对。

### 4.8 Table（数据表）操作

Table 是 GPF 的词表/数据表系统，支持键值查询、前后缀匹配等操作。

#### `SetTable(tableName)`

设置当前活跃的数据表。

```python
gpf.SetTable("CiDian")
```

#### `CallTable(tableName, Mode=0)`

加载并设置数据表。

```python
gpf.CallTable("CiDian")
```

**说明**：先调用 `GPF_AppLexicon` 加载表，再调用 `GPF_SetLexicon` 设置为当前表。

#### `GetTable()`

获取所有已加载的表名。

```python
tables = gpf.GetTable()  # → dict/list
```

**返回值**：`dict`，所有表名及其信息。

#### `GetTableItem(tableName="", kv="")` / `GetItem(tableName="", kv="")`

获取表中的条目列表。

```python
items = gpf.GetTableItem("CiDian")           # 获取表的全部条目
items = gpf.GetTableItem("CiDian", "POS=n")  # 按条件过滤
items = gpf.GetItem("CiDian")                # 等价
```

#### `GetTableItems(tableName, kv="")`

获取指定表的条目列表（带过滤条件）。

```python
items = gpf.GetTableItems("CiDian", "POS=n")
```

| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `tableName` | `str` | （必填） | 表名 |
| `kv` | `str` | `""` | 过滤条件 |

**返回值**：`dict` / `list`，条目列表。

#### `GetTableItemKV(tableName, item="", key="")` / `GetItemKV(...)` / `GetTableItemKVs(...)`

获取表中某条目的属性。

```python
kvs = gpf.GetTableItemKV("CiDian", "天气")               # 全部属性
kvs = gpf.GetTableItemKVs("CiDian", "天气", "POS")        # 指定属性
kvs = gpf.GetItemKV("CiDian", "天气", "POS")              # 等价
```

| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `tableName` | `str` | （必填） | 表名 |
| `item` | `str` | `""` | 条目名 |
| `key` | `str` | `""` | 属性名（为空返回全部） |

**返回值**：`dict` / `list`。

#### `IsTable(tableName, item="", kv="")`

判断表中是否存在指定条目或属性。

```python
ret = gpf.IsTable("CiDian")                        # 判断表是否存在
ret = gpf.IsTable("CiDian", "天气")                # 判断条目是否存在
ret = gpf.IsTable("CiDian", "天气", "POS=n")       # 判断属性是否匹配
```

**返回值**：`int`，存在/匹配返回非零值。

#### `GetSuffix(tableName, sentence)` / `GetPrefix(tableName, sentence)`

后缀/前缀匹配。

```python
suffix = gpf.GetSuffix("CiDian", "今天天气很好")  # 从文本尾部匹配
prefix = gpf.GetPrefix("CiDian", "今天天气很好")  # 从文本头部匹配
```

| 参数 | 类型 | 说明 |
|------|------|------|
| `tableName` | `str` | 表名 |
| `sentence` | `str` | 待匹配的文本 |

**返回值**：`str`，匹配结果。

### 4.9 FSA（有限状态自动机）

FSA 是 GPF 的规则引擎，支持基于状态自动机的模式匹配和规则执行。

#### `CallFSA(fsaName, **Others)` / `RunFSA(fsaName, param="")`

执行 FSA 规则。

```python
gpf.CallFSA("myRule", Param1="val1", Param2="val2")
gpf.RunFSA("myRule")   # 等价于 CallFSA("myRule")
```

| 参数 | 类型 | 说明 |
|------|------|------|
| `fsaName` | `str` | FSA 规则名称 |
| `**Others` | 关键字参数 | FSA 参数，自动拼接为 `"Key1=Val1;Key2=Val2"` 格式 |

**返回值**：`int`，FSA 执行结果长度。

**说明**：FSA 匹配成功后，会通过 `exec()` 执行规则中嵌入的 Python 代码。

#### `GetFSAParam(key)` / `GetParam(key)`

获取 FSA 参数。

```python
val = gpf.GetFSAParam("key")
val = gpf.GetParam("key")    # 等价
```

**返回值**：`str`，参数值。

#### `GetFSANode(tag="-1")` / `GetNode(tag)`

获取 FSA 节点。

```python
pathNo = gpf.GetFSANode("tag")  # 按标签获取
pathNo = gpf.GetNode(0)         # 按整数标签获取（自动转字符串）
```

**返回值**：`int`，路径号。

### 4.10 索引创建

#### `IndexTable(table_filename)`

索引 Table 文件。

```python
gpf.IndexTable("./data/myTable.txt")
```

| 参数 | 类型 | 说明 |
|------|------|------|
| `table_filename` | `str` | Table 文件路径 |

**返回值**：若存在搭配（Collocation）信息，返回 `dict`（搭配数据）；否则返回 `0`。

**说明**：索引完成后自动调用 `GPF_ReLoad` 重新加载配置。若存在搭配关系，会递归索引搭配表。

#### `IndexFSA(rule_filename)`

索引 FSA 规则文件。

```python
gpf.IndexFSA("./data/myRule.txt")
```

| 参数 | 类型 | 说明 |
|------|------|------|
| `rule_filename` | `str` | FSA 规则文件路径 |

**返回值**：`int`，操作状态码。

### 4.11 服务调用与 Dot 可视化

#### `CallService(sentence, name)`

调用远程服务进行文本解析。

```python
result = gpf.CallService("自然语言处理", "Tree")
```

| 参数 | 类型 | 说明 |
|------|------|------|
| `sentence` | `str` | 输入文本 |
| `name` | `str` | 服务名称 |

**返回值**：`str`，服务返回的 JSON 字符串。

#### `Dot2Img(Dot, name)`

将 Dot 脚本转换为图片（通过 DLL）。

```python
img = gpf.Dot2Img(dot_string, "myGraph")
```

**返回值**：`bytes`，图片数据。

#### `DotFile(dot_filename, img_filename)`

将 Dot 文件通过远程服务转换为图片文件。

```python
gpf.DotFile("graph.dot", "graph.png")
```

**返回值**：`bool`，成功返回 `True`。

#### `DotBuff(dot_data, img_filename)`

将 Dot 字符串通过远程服务转换为图片文件。

```python
gpf.DotBuff(dot_string, "graph.png")
```

**返回值**：`bool`，成功返回 `True`。

### 4.12 结构添加

将 JSON 格式的语言结构数据添加到当前网格中。

#### `AddStructure(json_str)` / `AddGrid(json_str)` / `AddGridJS(json_str)`

添加结构，自动识别类型。

```python
gpf.AddStructure(json_str)
gpf.AddGrid(json_str)      # 等价
gpf.AddGridJS(json_str)    # 等价
```

**说明**：根据 JSON 结构自动判断类型：
- 若 JSON 包含 `"Type"` 字段：直接调用 `GPF_AddStructure`
- 否则自动识别为 Graph / Seq / Tree，调用对应的添加方法

#### `AddSeq(json_str)`

添加序列结构（如分词结果）。

```python
# 字符串列表格式
gpf.AddSeq('["自然/b", "语言/n", "处理/v"]')

# 字典列表格式（带属性）
gpf.AddSeq('[{"自然": {"POS": "b"}}, {"语言": {"POS": "n"}}]')
```

**支持的 JSON 格式**：
- 字符串列表：`["词1", "词1/POS", ...]`
- 字典列表：`[{"词": {"属性名": "属性值"}}, ...]`，或包含 `"Unit"` 和 `"Att"` 字段的字典

#### `AddTree(json_str)`

添加树结构。

```python
gpf.AddTree('{"S": ["NP", {"VP": ["V", "NP"]}]}')
```

**说明**：将 JSON 的括号语法转换为 `"Type":"Tree"` 格式后调用 `AddGridJS`。

#### `AddGraph(json_str)`

添加图结构（有向图）。

```python
# 三元组列表格式：[源, 目标, 关系]
gpf.AddGraph('[["喜欢", "我", "SBJ"], ["喜欢", "北京", "OBJ"]]')

# 字典格式：{源: {关系: 目标}}
gpf.AddGraph('{"喜欢": {"SBJ": "我", "OBJ": "北京"}}')
```

#### `Add2Grid(Txt, Head, Tail, Rel)`

底层方法，向网格中添加一条边（两个 Unit + 关系）。

```python
gpf.Add2Grid("我喜欢北京", "喜欢", "我", "SBJ")
```

### 4.13 可视化输出

GPF 提供多种可视化方法，生成 Graphviz DOT 脚本或 PNG 图片。

#### `Show(Json="", **Others)`

统一可视化接口。

```python
gpf.Show()                                  # 显示当前网格
gpf.Show(Json=json_str)                     # 显示 JSON 结构
gpf.Show(Output="output.png")               # 指定输出文件
gpf.Show(Relation=True)                     # 显示关系
gpf.Show(Grid=False, Relation=True)         # 只显示关系，不显示网格
gpf.Show(Unit=unitNo)                       # 显示单个 Unit 的属性
```

| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `Json` | `str` | `""` | JSON 结构数据 |
| `Output` | `str` | `"./gpf.png"` | 输出图片路径 |
| `Relation` / `IsShowRelation` | `bool` | `False` | 是否显示关系 |
| `Grid` / `IsShowGrid` | `bool` | `True` | 是否显示网格 |
| `Unit` | `str` | `""` | 显示指定 Unit 的详情 |

#### `ShowStructure(Json="", Img="./gpf.png")`

显示结构（自动识别类型并调用对应的可视化方法）。

```python
gpf.ShowStructure(json_str, "output.png")
```

#### `ShowGrid(Img="./gpf.png", IsShowRel=False, IsShowGrid=True)`

显示网格结构。

```python
gpf.ShowGrid("output.png")                          # 仅网格
gpf.ShowGrid("output.png", IsShowRel=True)           # 网格 + 关系
gpf.ShowGrid("output.png", IsShowRel=True, IsShowGrid=False)  # 仅关系
```

**说明**：网格中的 Unit 按类型着色：
- `Type=Char`：灰色
- `Type=Word`：绿色
- `Type=Phrase`：浅蓝色
- `Type=Chunk`：金色

#### `ShowUnit(Unit, Img="./gpf.png")`

显示单个 Unit 的属性详情图。

```python
gpf.ShowUnit(unitNo, "unit_detail.png")
```

#### `ShowRelation(Img="./gpf.png")`

显示关系图（等价于 `ShowGrid(Img, IsShowRel=True, IsShowGrid=True)`）。

```python
gpf.ShowRelation("relations.png")
```

#### `ShowTree(Json)` / `ShowSeq(Json)` / `ShowGraph(Json)`

返回 DOT 脚本字符串（不直接生成文件）。

```python
dot = gpf.ShowTree(json_str)    # 树结构 DOT（自上而下排列）
dot = gpf.ShowSeq(json_str)     # 序列结构 DOT（从左到右排列）
dot = gpf.ShowGraph(json_str)   # 图结构 DOT
```

**返回值**：`str`，Graphviz DOT 脚本。

#### `ShowCloud(Json, Output)`

生成词云图片。

```python
gpf.ShowCloud('{"自然": 10, "语言": 8, "处理": 6}', "cloud.png")
```

| 参数 | 类型 | 说明 |
|------|------|------|
| `Json` | `str` | JSON 格式的词频数据 `{"词": 频率, ...}` |
| `Output` | `str` | 输出图片路径 |

### 4.14 其他方法

#### `GetLog()`

获取 GPF 日志。

```python
log = gpf.GetLog()
```

**返回值**：`dict`，日志信息。

#### `Reduce(From=0, To=-1, Head=-1)`

归约操作，将指定范围的 Unit 归约为一个新 Unit。

```python
unitNo = gpf.Reduce(From=0, To=5, Head=0)
```

| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `From` | `int` | `0` | 归约起始位置 |
| `To` | `int` | `-1` | 归约结束位置 |
| `Head` | `int` | `-1` | 中心词的 FSA 路径号 |

**返回值**：`str`，新 Unit 编号。

#### `GPFInit()`

手动初始化 GPF 数据（线程安全）。通常无需显式调用，GPF 方法内部会自动调用。

```python
gpf.GPFInit()
```

#### `GPFPersons()`

致谢信息。

### 4.15 GPF 方法速查表

| 分类 | 方法 | 说明 |
|------|------|------|
| **NLP** | `Segment(text, table)` | CRF 分词 |
| | `POS(text, table)` | 词性标注 |
| | `Parse(text, **Others)` | 统一解析接口 |
| **文本** | `SetText(text)` / `SetGridText(text)` | 设置文本 |
| | `GetText(begin, end)` / `GetGridText(begin, end)` | 获取文本 |
| | `GetGrid()` / `GetStructure()` | 获取网格结构 |
| **Unit** | `AddUnit(text, colNo)` | 添加 Unit |
| | `AddUnitKV(unitNo, key, val)` | 添加 Unit 属性 |
| | `GetWord(unitNo)` | 获取 Unit 文本 |
| | `GetUnit(kv)` / `GetUnits(kv)` | 查找 Unit |
| | `GetUnitKV(unitNo, key)` / `GetUnitKVs(unitNo, key)` | 获取 Unit 属性 |
| | `GetFSAUnit(pathNo)` | 按路径号获取 Unit |
| | `IsUnit(unitNo, kv)` | 判断 Unit 属性 |
| **Relation** | `AddRelation(u1, u2, role)` | 添加关系 |
| | `AddRelationKV(u1, u2, role, key, val)` | 添加关系属性 |
| | `GetRelation(kv)` / `GetRelations(kv)` | 获取关系 |
| | `GetRelationKV(u1, u2, role, key)` / `GetRelationKVs(...)` | 获取关系属性 |
| | `IsRelation(u1, u2, role, kv)` | 判断关系 |
| **Grid KV** | `AddGridKV(key, val)` / `AddTextKV(key, val)` | 添加网格属性 |
| | `GetGridKV(key)` / `GetGridKVs(key)` / `GetTextKV(key)` | 获取网格属性 |
| **Table** | `SetTable(tableName)` | 设置当前表 |
| | `CallTable(tableName)` | 加载并设置表 |
| | `GetTable()` | 获取所有表名 |
| | `GetTableItem(tableName, kv)` / `GetItem(...)` | 获取表条目 |
| | `GetTableItems(tableName, kv)` | 获取表条目（带过滤） |
| | `GetTableItemKV(...)` / `GetItemKV(...)` / `GetTableItemKVs(...)` | 获取条目属性 |
| | `IsTable(tableName, item, kv)` | 判断表/条目/属性 |
| | `GetSuffix(tableName, sentence)` | 后缀匹配 |
| | `GetPrefix(tableName, sentence)` | 前缀匹配 |
| **FSA** | `CallFSA(fsaName, **Others)` / `RunFSA(fsaName)` | 执行 FSA |
| | `GetFSAParam(key)` / `GetParam(key)` | 获取 FSA 参数 |
| | `GetFSANode(tag)` / `GetNode(tag)` | 获取 FSA 节点 |
| **索引** | `IndexTable(filename)` | 索引 Table 文件 |
| | `IndexFSA(filename)` | 索引 FSA 文件 |
| **服务** | `CallService(sentence, name)` | 调用远程服务 |
| | `Dot2Img(dot, name)` | Dot 转图片 |
| | `DotFile(dot_file, img_file)` | Dot 文件转图片 |
| | `DotBuff(dot_data, img_file)` | Dot 字符串转图片 |
| **结构添加** | `AddStructure(json)` / `AddGrid(json)` / `AddGridJS(json)` | 添加结构 |
| | `AddSeq(json)` | 添加序列 |
| | `AddTree(json)` | 添加树 |
| | `AddGraph(json)` | 添加图 |
| **可视化** | `Show(Json, **Others)` | 统一可视化 |
| | `ShowStructure(Json, Img)` | 显示结构 |
| | `ShowGrid(Img, IsShowRel, IsShowGrid)` | 显示网格 |
| | `ShowUnit(Unit, Img)` | 显示 Unit 详情 |
| | `ShowRelation(Img)` | 显示关系图 |
| | `ShowTree(Json)` / `ShowSeq(Json)` / `ShowGraph(Json)` | 返回 DOT 脚本 |
| | `ShowCloud(Json, Output)` | 词云 |
| **其他** | `GetLog()` | 获取日志 |
| | `Reduce(From, To, Head)` | 归约操作 |
| | `GPFInit()` | 手动初始化 |

---

## 5. BCC 类 — 语料库查询

BCC（BCC Corpus）提供语料库查询功能，支持词频统计、上下文检索和语料索引。

### 5.1 构造函数与初始化

```python
from LangSC import BCC

bcc = BCC(dataPath="./data")
```

**参数**

| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `dataPath` | `str` | `"./data"` | 语料数据目录路径 |

**说明**
- 构造函数自动加载 `bcclib` 动态库
- 自动加载 BCC 配置文件（`BCCconfig.txt`）和 Lua 解析脚本（`Parser.lua`）
- 自动检测并索引语料数据（详见 [5.2 自动索引机制](#52-自动索引机制)）

### 5.2 自动索引机制

构造函数调用 `_init_bcc_data(dataPath)` 自动处理语料索引：

1. 检查 `dataPath` 中是否存在 `IdxLog_BCC.txt`
2. **若存在**：已是索引目录，直接使用
3. **若不存在**：视为原始语料目录
   - 扫描目录下的语料文件（排除 Table 和 FSA 格式）
   - 检查 `dataPathIdx/` 是否存在且时间戳一致（避免重复索引）
   - 若需要索引，调用 `IndexBCC` 对所有语料文件建索引
   - 写入 `IdxLog_BCC.txt` 索引日志

### 5.3 语料查询

#### `CallBCC(query)`

执行原始 BCC 查询语句。

```python
result = bcc.CallBCC("学习{}Freq(100,$Q,0)")
result = bcc.CallBCC("学习{}Context(20,0,100)")
result = bcc.CallBCC("学习{}Count()")
```

| 参数 | 类型 | 说明 |
|------|------|------|
| `query` | `str` | BCC 查询语句 |

**返回值**：`str`，查询结果（JSON 格式字符串）。

**查询语句格式**：`查询词{}操作(参数)`
- `Freq(数量, 目标字段, 上下文条数)` — 词频统计
- `Context(窗口大小, 页码, 条数)` — 上下文检索
- `Count()` — 计数统计

**说明**
- 首次调用时自动初始化 BCC 引擎（线程安全）
- 若初始化失败且查询不含 "Lua"，返回 `{}`

#### `RunBCC(Query, **Others)`

高级查询接口，通过关键字参数构造查询语句。

```python
# 词频查询（默认）
result = bcc.RunBCC("学习")

# 指定查询模式和参数
result = bcc.RunBCC("学习", Command="Freq", Number=200)
result = bcc.RunBCC("学习", Command="Context", WinSize=30, Number=100)
result = bcc.RunBCC("学习", Command="Count")

# 带逻辑运算
result = bcc.RunBCC("学习 AND 方法", Command="Freq")
result = bcc.RunBCC("学习 NOT 机器", Command="Freq")

# Lua 格式输出
result = bcc.RunBCC("学习", Command="Freq", Print="Lua")
```

**完整参数列表**

| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `Query` | `str` | （必填） | 查询词或查询表达式 |
| `Command` / `Output` | `str` | `"Freq"` | 查询模式：`"Freq"` / `"Context"` / `"Count"` 或自定义命令 |
| `Number` | `int` | `100` | 返回条数上限 |
| `Target` | `str` | `"$Q"` | 目标字段标识符 |
| `WinSize` | `int` | `20` | 上下文窗口大小（`Context` 模式） |
| `PageNo` | `int` | `0` | 分页页码（`Context` 模式） |
| `Print` | `str` | `""` | 输出格式，`"Lua"` 为 Lua 脚本格式 |
| `Speedup` | `int` | `1` | 加速选项 |
| `ContextNum` | `int` | `0` | 上下文条数（`Freq` 模式附带上下文） |

**返回值**：`str`，查询结果。

**查询语句自动生成规则**：
- 若 `Query` 不含 `{}`，自动追加 `{}`
- `Freq` 模式生成：`Query{}Freq(Number, Target, ContextNum)`
- `Context` 模式生成：`Query{}Context(WinSize, PageNo, Number)`
- `Count` 模式生成：`Query{}Count()`
- `AND` / `NOT` 运算符会在运算符位置插入操作

### 5.4 KV 操作

BCC 引擎维护一个全局键值存储，可在查询间传递上下文信息。

#### `AddBCCKV(Key, Val)`

添加键值对。

```python
bcc.AddBCCKV("Filter", "新闻")
bcc.AddBCCKV("Tags", "人名;地名;机构名")  # 多值用分隔符
```

| 参数 | 类型 | 说明 |
|------|------|------|
| `Key` | `str` | 键名 |
| `Val` | `str` | 值（支持以空格、分号、逗号、制表符分隔的多值，会合并为分号分隔） |

**返回值**：`str`，操作结果。

#### `GetBCCKV(Key="")` / `GetBCCKVs(Key="")`

获取键值。

```python
val = bcc.GetBCCKV("Filter")     # 获取指定键
all_kvs = bcc.GetBCCKV()         # 获取全部键值
all_kvs = bcc.GetBCCKVs()        # 等价
```

| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `Key` | `str` | `""` | 键名（为空返回全部） |

**返回值**：`str`，查询结果。

#### `ClearBCCKV(Key="")`

清空键值存储。

```python
bcc.ClearBCCKV()
```

**返回值**：`str`，操作结果。

### 5.5 语料索引

#### `IndexBCC(filelistname, **Others)`

索引语料文件，支持多种输入格式。

```python
# 1. 索引整个目录（递归扫描）
bcc.IndexBCC("/path/to/corpus_dir")

# 2. 索引文件列表文件（每行一个文件路径）
bcc.IndexBCC("/path/to/filelist.txt")

# 3. 索引单个文件
bcc.IndexBCC("/path/to/corpus.txt")

# 4. 索引 Python 列表
bcc.IndexBCC(["file1.txt", "file2.txt", "file3.txt"])

# 5. 指定索引结构
bcc.IndexBCC("/path/to/corpus", Structure="Segment")
bcc.IndexBCC("/path/to/corpus", Structure="HZ")
```

| 参数 | 类型 | 说明 |
|------|------|------|
| `filelistname` | `str` 或 `list` | 语料来源：目录路径、文件列表路径、单个文件路径或 Python 文件路径列表 |
| `Structure` | `str` | 索引结构：`"HZ"`（默认，按汉字）、`"Segment"`（按分词） |

**返回值**：`int`，操作状态码。

**处理流程**：
1. 若输入为目录：递归收集所有文件路径
2. 若输入为文件列表：直接读取
3. 若输入为 Python 列表：写入临时文件
4. 对每个文件检测是否为原始文本（非 Table/Doc 格式），若是则预处理为 Doc 格式
5. 检测文件编码，非 GBK 编码的文件自动转换
6. 调用 `BCC_IndexBCC` 构建索引

### 5.6 BCC 方法速查表

| 方法 | 说明 |
|------|------|
| `CallBCC(query)` | 执行原始查询 |
| `RunBCC(Query, **Others)` | 高级查询接口 |
| `AddBCCKV(Key, Val)` | 添加键值 |
| `GetBCCKV(Key)` / `GetBCCKVs(Key)` | 获取键值 |
| `ClearBCCKV()` | 清空键值 |
| `IndexBCC(filelistname, **Others)` | 索引语料 |

---

## 6. JSS 类 — JSON 结构检索

JSS（JSON Structure Search）提供对 JSON 格式数据的结构化检索能力，支持 SQL 风格查询语法和多表管理。

### 6.1 构造函数与初始化

```python
from LangSC import JSS

jss = JSS(dataPath, log_level=0, log_filename='')
```

**参数**

| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `dataPath` | `str` | （必填） | 数据目录路径 |
| `log_level` | `int` | `0` | 日志级别（0=关闭） |
| `log_filename` | `str` | `""` | 日志文件路径（空字符串=不写日志） |

**说明**
- 构造函数自动加载 `jsslib` 动态库
- 使用 10MB 缓冲区（`1024 * 1024 * 10` 字节）
- 支持同时管理多个表，通过 `self.handles` 字典存储
- 析构函数（`__del__`）自动调用 `Terminate()` 释放资源，并卸载 DLL

### 6.2 自动索引机制

构造函数调用 `_init_jss_data(dataPath)` 自动处理索引：

```
步骤 1: 检查 dataPath/IdxLog_JSS.txt
         ├── 存在 → 已是索引目录 → 加载全部子目录中的表 → 完成
         └── 不存在 → 步骤 2

步骤 2: 扫描 dataPath 中的文件
         ├── 无文件 → 完成
         └── 有文件 → 步骤 3

步骤 3: 检查 dataPathIdx/IdxLog_JSS.txt
         ├── 存在且时间戳一致 → 从 dataPathIdx 加载 → 完成
         └── 不一致或不存在 → 步骤 4

步骤 4: 执行索引
         ├── 创建 dataPathIdx/ 目录
         ├── 扫描 *.json / *.jsonl 文件（跳过 cfg_* 前缀）
         ├── 查找对应的 cfg_<name>.txt（不存在则自动生成）
         ├── 为每个文件创建索引到 dataPathIdx/<name>/
         ├── 记录到 IdxLog_JSS.txt
         └── 加载全部表 → 完成
```

### 6.3 数据目录结构

**原始数据目录**（以 `./table` 为例）：

```
./table/
├── ci.jsonl              # 原始数据（JSONL 格式，每行一条 JSON）
├── cfg_ci.txt            # ci 的配置文件（可选，缺失时自动生成）
├── audio.jsonl           # 另一张表
├── cfg_audio.txt         # audio 的配置文件（可选）
├── hunxiao.json          # 也支持 JSON 数组格式
└── (cfg_hunxiao.txt)     # 可选，缺失时自动生成
```

**命名规则**
- 数据文件：`<name>.json` 或 `<name>.jsonl`
- 配置文件：`cfg_<name>.txt`
- 以 `cfg_` 开头的文件在扫描数据文件时会被跳过

**索引后自动生成的目录**：

```
./tableIdx/
├── IdxLog_JSS.txt        # 索引日志（记录已索引文件及时间戳）
├── ci/                   # ci 表的索引文件
│   ├── (索引数据文件...)
├── audio/                # audio 表的索引文件
│   ├── (索引数据文件...)
└── hunxiao/              # hunxiao 表的索引文件
    ├── (索引数据文件...)
```

### 6.4 cfg 配置文件格式

cfg 文件为 JSON 格式，定义表结构和索引策略。

**示例**（`cfg_ci.txt`）：

```json
{
    "table_name": "ci",
    "record_format": "jsonl",
    "content": {
        "id": "id",
        "ci": "ci",
        "pos": "pos",
        "pinyin": "pinyin",
        "shiyi": "shiyi",
        "freq": "freq"
    },
    "index": {
        "number": ["id"],
        "kv": ["ci", "pos", "pinyin"],
        "affix": [],
        "bm25": ["shiyi"]
    }
}
```

**字段详细说明**

| 字段 | 类型 | 说明 |
|------|------|------|
| `table_name` | `string` | 表名，用于查询时的 `FROM` 子句 |
| `record_format` | `string` | 数据格式：`"jsonl"`（每行一条 JSON 记录）或 `"json"`（JSON 数组） |
| `content` | `object` | 字段映射。键为显示名，值为数据源字段名。数组类型字段需加 `[]` 后缀 |
| `index.number` | `array` | 数值索引字段列表，支持范围查询（`>`, `<`, `>=`, `<=`） |
| `index.kv` | `array` | 键值索引字段列表，支持精确匹配查询（`=`） |
| `index.affix` | `array` | 词缀索引字段列表，支持前后缀匹配 |
| `index.bm25` | `array` | 全文检索字段列表，支持 BM25 相关性排序 |

**content 字段的数组映射**

若原始数据中某字段为数组类型，映射时需加 `[]` 后缀：

```json
// 原始数据记录
{"id": 1, "word": "测试", "tags": ["tag1", "tag2"]}

// content 配置
{
    "id": "id",
    "word": "word",
    "tags[]": "tags[]"
}
```

### 6.5 cfg 自动生成规则

当 cfg 文件缺失时，JSS 自动读取数据文件的首条记录推断 schema：

1. **读取首条记录**
   - `.jsonl` 文件：读取第一行解析为 JSON
   - `.json` 文件：解析 JSON，若为数组取第一个元素，若为对象直接使用

2. **生成 `content` 映射**
   - 遍历首条记录的所有字段
   - 普通字段（string, number, bool）：直接映射 `"field": "field"`
   - 数组字段（list）：加 `[]` 后缀 `"field[]": "field[]"`

3. **生成 `index` 配置**
   - `number`：仅当字段名为 `"id"` 且值为整数或浮点数时加入
   - `kv`：所有字符串类型字段
   - `affix`：留空 `[]`
   - `bm25`：留空 `[]`

4. **写入文件**
   - 保存到 `cfg_<name>.txt`，UTF-8 编码，4 空格缩进

**示例**

假设 `books.jsonl` 首条记录为：
```json
{"id": 1, "title": "自然语言处理", "author": "张三", "price": 59.8, "tags": ["NLP", "AI"]}
```

自动生成的 `cfg_books.txt`：
```json
{
    "table_name": "books",
    "record_format": "jsonl",
    "content": {
        "id": "id",
        "title": "title",
        "author": "author",
        "price": "price",
        "tags[]": "tags[]"
    },
    "index": {
        "number": ["id"],
        "kv": ["title", "author"],
        "affix": [],
        "bm25": []
    }
}
```

> 注意：`price` 字段虽为数值但不是 `"id"`，不会被加入 `number` 索引。若需要对 `price` 进行范围查询，应手动编写 cfg 文件并将 `price` 加入 `number`。

### 6.6 查询

#### `RunJSS(sql_statement, table="")`

执行 SQL 风格的查询。

```python
# 单表时无需指定 table
results = jss.RunJSS("SELECT TOP 10 id, ci FROM ci WHERE pinyin = 'ai4';")

# 多表时通过 table 参数指定
results = jss.RunJSS("SELECT * FROM audio WHERE tags = 'pop';", table="audio")
results = jss.RunJSS("SELECT * FROM ci WHERE pos = 'n';", table="ci")
```

| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `sql_statement` | `str` | （必填） | SQL 查询语句 |
| `table` | `str` | `""` | 指定查询的表名。为空时使用第一个已加载的表 |

**返回值**：`list`，查询结果列表。每个元素为一条记录（`dict`）。未初始化时返回 `[]`。

**SQL 语法**

支持的子句：

| 子句 | 说明 | 示例 |
|------|------|------|
| `SELECT` | 指定返回字段（`*` 返回全部） | `SELECT id, ci, pinyin` |
| `TOP n` | 限制返回条数 | `SELECT TOP 10 *` |
| `FROM` | 指定表名 | `FROM ci` |
| `WHERE` | 过滤条件 | `WHERE pos = 'n'` |
| `AND` | 条件与 | `WHERE pos = 'n' AND freq > 100` |
| `OR` | 条件或 | `WHERE pos = 'n' OR pos = 'v'` |

支持的运算符：

| 运算符 | 说明 | 适用索引类型 |
|--------|------|-------------|
| `=` | 等于 | `kv`, `number` |
| `>` | 大于 | `number` |
| `<` | 小于 | `number` |
| `>=` | 大于等于 | `number` |
| `<=` | 小于等于 | `number` |

**查询示例**

```python
# 精确匹配
results = jss.RunJSS("SELECT * FROM ci WHERE ci = '爱';")

# 范围查询（需要字段在 index.number 中）
results = jss.RunJSS("SELECT * FROM ci WHERE id > 100 AND id < 200;")

# 多条件
results = jss.RunJSS("SELECT id, ci, pos FROM ci WHERE pos = 'n' AND pinyin = 'ai4';")

# 限制返回数量
results = jss.RunJSS("SELECT TOP 5 * FROM ci WHERE pos = 'v';")

# 遍历结果
for row in results:
    print(row['id'], row['ci'], row.get('pinyin', ''))
```

### 6.7 资源释放

#### `Terminate()`

释放所有已加载表的句柄。

```python
jss.Terminate()
```

**说明**
- 遍历 `self.handles` 中所有句柄，逐一调用 `JL_Terminate`
- 清空句柄字典并重置 `is_init` 为 `False`
- 析构函数 `__del__` 中会自动调用，但建议显式调用以确保资源及时释放

### 6.8 JSS 方法速查表

| 方法 | 说明 |
|------|------|
| `JSS(dataPath, log_level, log_filename)` | 构造函数，自动索引+加载 |
| `RunJSS(sql_statement, table)` | SQL 查询 |
| `Terminate()` | 释放资源 |

---

## 7. 完整示例

### 7.1 GPF 分词与词性标注

```python
from LangSC import GPF

gpf = GPF("./data")

# 分词
seg = gpf.Parse("自然语言处理是人工智能的重要方向", Structure="Segment")
print(seg)  # ["自然", "语言", "处理", "是", "人工", "智能", "的", "重要", "方向"]

# 词性标注
pos = gpf.Parse("自然语言处理是人工智能的重要方向", Structure="POS")
print(pos)  # ["自然/b", "语言/n", "处理/v", "是/v", ...]

# 带用户词表的分词
gpf.CallTable("myDict")
seg = gpf.Segment("自然语言处理", table="myDict")
print(seg)
```

### 7.2 GPF 网格操作与关系构建

```python
from LangSC import GPF

gpf = GPF("./data")
gpf.SetText("我喜欢北京")

# 添加 Unit
u1 = gpf.AddUnit("我")
u2 = gpf.AddUnit("喜欢")
u3 = gpf.AddUnit("北京")

# 添加属性
gpf.AddUnitKV(u1, "POS", "r")
gpf.AddUnitKV(u2, "POS", "v")
gpf.AddUnitKV(u3, "POS", "ns")

# 添加关系
gpf.AddRelation(u2, u1, "SBJ")   # 喜欢 → 我（主语）
gpf.AddRelation(u2, u3, "OBJ")   # 喜欢 → 北京（宾语）

# 获取网格
grid = gpf.GetGrid()
print(grid)

# 可视化
gpf.Show(Output="result.png", Relation=True)
```

### 7.3 GPF 结构可视化

```python
from LangSC import GPF
import json

gpf = GPF("./data")

# 可视化序列（分词结果）
seq = '["自然/b", "语言/n", "处理/v", "是/v", "重要/a", "方向/n"]'
gpf.ShowStructure(seq, "seq.png")

# 可视化树结构
tree = json.dumps({"S": {"NP": ["自然", "语言"], "VP": ["处理"]}})
gpf.ShowStructure(tree, "tree.png")

# 可视化图结构
graph = json.dumps([["喜欢", "我", "SBJ"], ["喜欢", "北京", "OBJ"]])
gpf.ShowStructure(graph, "graph.png")

# 可视化词云
cloud = json.dumps({"自然": 10, "语言": 8, "处理": 6, "智能": 5, "学习": 4})
gpf.ShowCloud(cloud, "cloud.png")
```

### 7.4 GPF Table 查询

```python
from LangSC import GPF

gpf = GPF("./data")

# 加载词表
gpf.CallTable("CiDian")

# 查询表中的条目
items = gpf.GetTableItem("CiDian")
print("表中条目数:", len(items))

# 查询指定条目的属性
kvs = gpf.GetTableItemKV("CiDian", "天气")
print(kvs)

# 前缀匹配
prefix = gpf.GetPrefix("CiDian", "自然语言处理")
print("前缀匹配:", prefix)

# 判断条目存在性
if gpf.IsTable("CiDian", "天气"):
    print("天气 在词典中")
```

### 7.5 GPF FSA 规则执行

```python
from LangSC import GPF

gpf = GPF("./data")
gpf.SetGridText("张三是北京大学的教授")

# 执行 FSA 规则
gpf.CallFSA("PersonRecognize", Input="张三是北京大学的教授")

# 获取匹配结果
result = gpf.GetFSAParam("Result")
print(result)
```

### 7.6 BCC 语料查询

```python
from LangSC import BCC

bcc = BCC("./corpus")

# 词频统计
freq = bcc.RunBCC("学习", Command="Freq", Number=50)
print("词频结果:", freq)

# 上下文检索
ctx = bcc.RunBCC("学习", Command="Context", WinSize=30, Number=20)
print("上下文:", ctx)

# 计数
count = bcc.RunBCC("学习", Command="Count")
print("总频次:", count)

# 复合查询
result = bcc.RunBCC("学习 AND 方法", Command="Freq", Number=20)
print("AND 查询:", result)
```

### 7.7 BCC 语料索引

```python
from LangSC import BCC

bcc = BCC("./corpus_idx")

# 索引整个目录
bcc.IndexBCC("./raw_corpus/")

# 索引文件列表
bcc.IndexBCC("./filelist.txt")

# 索引 Python 列表
files = ["./doc1.txt", "./doc2.txt", "./doc3.txt"]
bcc.IndexBCC(files)

# 以分词模式索引
bcc.IndexBCC("./raw_corpus/", Structure="Segment")
```

### 7.8 JSS 数据检索

```python
from LangSC import JSS

# 自动索引 + 加载（首次会自动建索引，之后直接加载）
jss = JSS("./table")

# 查询
results = jss.RunJSS("SELECT TOP 5 id, ci, pinyin FROM ci WHERE ci = '爱';")
for row in results:
    print(f"id={row['id']}, ci={row['ci']}, pinyin={row['pinyin']}")

# 多表查询
audio_results = jss.RunJSS("SELECT * FROM audio WHERE tags = 'pop';", table="audio")
ci_results = jss.RunJSS("SELECT * FROM ci WHERE pos = 'n';", table="ci")

# 范围查询
results = jss.RunJSS("SELECT * FROM ci WHERE id >= 100 AND id <= 200;")

# 清理
jss.Terminate()
```

### 7.9 JSS 手动编写 cfg

```python
import json

# 为 products.jsonl 编写 cfg，包含价格范围查询和描述全文检索
cfg = {
    "table_name": "products",
    "record_format": "jsonl",
    "content": {
        "id": "id",
        "name": "name",
        "category": "category",
        "price": "price",
        "description": "description",
        "tags[]": "tags[]"
    },
    "index": {
        "number": ["id", "price"],          # 支持 id 和 price 的范围查询
        "kv": ["name", "category"],          # 支持 name 和 category 的精确匹配
        "affix": ["name"],                   # 支持 name 的前后缀匹配
        "bm25": ["description"]              # 支持 description 的全文检索
    }
}

with open("./table/cfg_products.txt", "w", encoding="utf-8") as f:
    json.dump(cfg, f, indent=4, ensure_ascii=False)

# 然后使用 JSS 加载
from LangSC import JSS
jss = JSS("./table")
results = jss.RunJSS("SELECT * FROM products WHERE price < 100;", table="products")
```

---

## 8. 数据文件格式说明

### 8.1 GPF Table 文件格式

Table 文件是纯文本格式，以 `Table <表名>` 开头，每行一个条目及其属性：

```
Table CiDian
天气
  POS n
  拼音 tianqi
北京
  POS ns
  类型 地名
```

### 8.2 GPF FSA 文件格式

FSA 文件以 `FSA <规则名>` 开头，定义状态转换规则：

```
FSA PersonRecognize
State Start
  Transition ...
```

### 8.3 BCC 语料文件格式

BCC 支持两种格式：

**Doc 格式**（结构化）：
```
Doc 文档名
Item:这 是 第 一 句
Item:这 是 第 二 句
```

**原始文本格式**：纯文本，IndexBCC 会自动预处理为 Doc 格式（按句号分句，每个字用空格分隔）。

### 8.4 JSS 数据文件格式

**JSONL 格式**（`*.jsonl`）：每行一条 JSON 记录
```
{"id": 1, "ci": "爱", "pos": "v", "pinyin": "ai4"}
{"id": 2, "ci": "被", "pos": "p", "pinyin": "bei4"}
```

**JSON 格式**（`*.json`）：JSON 数组
```json
[
    {"id": 1, "ci": "爱", "pos": "v", "pinyin": "ai4"},
    {"id": 2, "ci": "被", "pos": "p", "pinyin": "bei4"}
]
```

---

## 9. 平台与依赖

### 9.1 动态库

| 平台 | GPF 库 | BCC 库 | JSS 库 |
|------|--------|--------|--------|
| Windows | `gpflib.dll` | `bcclib.dll` | `jsslib.dll` |
| Linux | `libgpflib.so` | `libbcclib.so` | `libjsslib.so` |
| macOS | `libgpflib.dylib` | `libbcclib.dylib` | `libjsslib.dylib` |

### 9.2 包内数据文件

| 文件 | 所属类 | 用途 |
|------|--------|------|
| `GPFconfig.txt` | GPF | GPF 引擎配置 |
| `Segment.dat` | GPF | CRF 分词模型 |
| `idxPOS.dat` | GPF | 词性标注数据 |
| `BCCconfig.txt` | BCC | BCC 引擎配置 |
| `Parser.lua` | BCC | Lua 查询解析脚本 |
| `base.lex` | JSS | 分词词典（用于 affix/bm25 索引） |
| `Graph/dot` | GPF | Graphviz DOT 工具（可视化） |

### 9.3 线程安全

GPF 和 BCC 的初始化操作使用 `threading.Lock()` 保护，确保多线程环境下不会重复初始化：

| 全局状态 | 所属类 | 说明 |
|----------|--------|------|
| `IsCRFInit` | GPF | CRF 分词模型初始化状态 |
| `IsPOSInit` | GPF | POS 词性标注数据初始化状态 |
| `IsGPFInit` | GPF | GPF 数据初始化状态 |
| `IsBCCInit` | BCC | BCC 引擎初始化状态 |

### 9.4 索引日志文件

每个类使用独立的索引日志文件，避免并发冲突：

| 日志文件 | 所属类 | 存放位置 |
|----------|--------|----------|
| `IdxLog_GPF.txt` | GPF | `dataPath` 或 `dataPathIdx/` |
| `IdxLog_BCC.txt` | BCC | `dataPath` 或 `dataPathIdx/` |
| `IdxLog_JSS.txt` | JSS | `dataPath` 或 `dataPathIdx/` |

日志格式为 TSV（制表符分隔）：
```
文件名\t时间戳1\t时间戳2
```

---

## 10. 参考文献

说明文档及使用示例：http://gpf.blcu.edu.cn

> 荀恩东. 自然语言结构计算——GPF结构分析框架 [M]. 北京: 人民邮电出版社. 2022
