【迅搜17】SCWS分词(二)自定义字典及分词器

SCWS分词(二)自定义字典及分词器

经过上篇文章的学习,相信大家对分词的概念已经有了更深入的了解了吧。我们也知道了,SCWS 是 XS 中的一个重要组成部分,但它也是可以单独拿出来使用的。而对于分词器来说,不管是 SCWS 还是现在流行的 IK、Jieba ,其实概念方面都是差不多的。比如说它们都需要字典来做为分词的依据,也会有停用词库这一类的附加字典。今天,我们主要来学习的就是 SCWS 字典相关的一些配置。此外,还有自定义分词器的实现。

自定义字典

上回已经说过,SCWS 有提供一个非常小的,但词汇量非常大的字典。不过现在的网络社会,各种新鲜词汇层出不穷,总会有超出默认字典的新词出现。同时,还有一种情况就是一些专业领域的专业词汇,比如医学或者工程上面的,也不会在通用的字典库中。像这类的词项,我们就可以通过自定义字典库来添加。

XS 的字典库分为两种,一种是全局的,一种是针对某个项目的。对于全局字典库,我们可以在 XS 的安装目录中的 etc 目录下找到。

# /usr/local/xunsearch/etc
[root@localhost etc]# ll
total 14832
-rw-r--r--. 1  501 games 14315504 May 23  2022 dict.utf8.xdb
-rw-r--r--. 1 root root       447 May 23  2022 dict_user.txt
-rw-r--r--. 1 root root    843730 Nov 11 20:55 py.xdb
-rw-r--r--. 1 root root      3729 May 23  2022 rules.ini
-rw-r--r--. 1 root root      4424 May 23  2022 rules.utf8.ini
-rw-r--r--. 1 root root      4383 May 23  2022 rules_cht.utf8.ini
-rw-r--r--. 1 root root       272 May 23  2022 stopwords.txt

默认情况下,dict.uft8.xdb 就是 XS 自带的那个字典库。而 dict_user.txt 则是我们可以自己定义的字典库。py.xdb 是拼音库、rules开头的文件名全都是规则文件,也就是我们各种词要如何组合,词性变化与评分配置等。最后还有一个 stopwords.txt 停用词库。好了,咱们先用之前的数据来测试吧,就是最早那篇文章里的数据。

> php vendor/hightman/xunsearch/util/Indexer.php --source=csv --clean ./config/demo3_56.101.ini
1,关于 xunsearch 的 DEMO 项目测试,项目测试是一个很有意思的行为!,1314336158
2,测试第二篇,这里是第二篇文章的内容,1314336160
3,项目测试第三篇,俗话说,无三不成礼,所以就有了第三篇,1314336168> php ./vendor/hightman/xunsearch/util/Quest.php ./config/demo3_56.101.ini ''            
在 3 条数据中,大约有 3 条包含  ,第 1-3 条,用时:0.0014 秒。1. 关于 xunsearch 的 DEMO 项目测试 #1# [100%,0.00]
项目测试是一个很有意思的行为! 
Chrono:1314336158  Author:  2. 测试第二篇 #2# [100%,0.00]
这里是第二篇文章的内容 
Chrono:1314336160  Author:  3. 项目测试第三篇 #3# [100%,0.00]
俗话说,无三不成礼,所以就有了第三篇 
Chrono:1314336168  Author:

我们先来测试一个不正常的词,比如说“无三”。很明显,它不是一个传统意义上的正常的单词,但就像很多专业词汇一样,我们假设“无三”就是一个专业词汇。目前直接搜索“无三”肯定是没有数据的。

> php ./vendor/hightman/xunsearch/util/Quest.php ./config/demo3_56.101.ini --show-query '无三'
--------------------
解析后的 QUERY 语句:Query(无三@1)
--------------------
在 3 条数据中,大约有 0 条包含 无三 ,第 0-0 条,用时:0.0009 秒。

好了,那么咱们就打开 dict_user.txt 进行配置。

// vim /usr/local/xunsearch/etc/dict_user.txt
# Custom dictionary for scws (UTF-8 encoding)
# 每行一条记录,以 # 开头的号表示注释忽略
# 每行最多包含 4 个字段,依次代表 "词条" "TF" "IDF" "词性"
# 字段之间用空格或制表符分开,特殊词性 "!" 用于表示删除该词
# 参见 scws 自定义词典帮助:
# http://bbs.xunsearch.com/showthread.php?tid=1303
# $Id$
#
# WORD          TF              IDF             ATTR
# ------------------------------------------------------
无三

可以看到文件上方的注释已经很清晰了,四个字段,分别是词条、TF词频、IDF逆文档频率和词性,用逗号分隔,并按行划分。后面三个字段属性其实是可以不用写的,它会有默认值。

我们直接添加一个“无三”,后面的不用填,然后重新索引添加数据。再次查询,就可以看到“无三”可以被搜索到了。

> php ./vendor/hightman/xunsearch/util/Quest.php ./config/demo3_56.101.ini --show-query '无三'
--------------------
解析后的 QUERY 语句:Query(无三@1)
--------------------
在 3 条数据中,大约有 1 条包含 无三 ,第 1-1 条,用时:0.0020 秒。1. 项目测试第三篇 #3# [100%,0.62]
俗话说,无三不成礼,所以就有了第三篇 
Chrono:1314336168  Author:

后面的三个参数,我也不知道怎么填,查了一些资料也没讲得特别明白的。所以咱们保持默认就好,或者说你明确的知道这个新词的词性,那就把 TF 和 IDF 都设置成 1 ,然后指定词性就好了。大部分专业词汇其实都是名词居多的,直接设置一个 n 就行了。

项目字典

接下来我们来自定义一个项目字典。这种字典就是针对某一个具体项目的,比如说针对我们的 demo 项目,那么就直接找到安装目录的 data 目录,然后找到 demo 文件夹,在这个文件夹中创建一个 dict_user.txt 文件。关于项目数据目录的问题,我们之前在学习索引管理时就说过,后面学习 Xapian 的时候也会再说一下。

好了,直接在项目目录下面的 dict_user.txt 文件中添加新词吧,词条规则和全局文件是一样的。

// vim /usr/local/xunsearch/data/demo/dict_user.txt
无三不

对于项目字典,我们在在代码中也可以直接操作,比如使用 XSIndex 对象的 getCustomDict() 方法就可以获取到自定义字典的内容,使用 setCustomDict() 就可以设置当前项目的自定义字典信息。

print_r($xs->index->getCustomDict()); // 无三不$dict = <<<EOF无三不无三不成 0 0 nEOF;
print_r($xs->index->setCustomDict($dict));print_r($xs->index->getCustomDict());
// 无三不
// 无三不成 0 0 n

在上面的代码中,我们通过 PHP 来设置了自定义字典,大家可以再回到项目目录下,看看 dict_user.txt 文件是不是也已经被重写成了新的内容,多了“无三不成”这样一个单词。这种功能有个什么好处呢?那就是我们的字典也可以通过在 MySQL 或其它数据库中进行存储,然后直接在 PHP 代码中操作字典,是不是非常方便。

当然,txt 格式的明文字典效率其实不高的,而且如果单词数量比较多,占用的空间也会比较大。SCWS 在命令行还提供了一个 scws-gen-dict 工具。和上篇文章中我们命令行操作 scws 的工具是放在一起的。这个工具可以将 txt 文件转换为 xdb 文件,也就是 SCWS 的默认词典文件一样的压缩格式。如果确实有非常大量的专业词汇,建议还是转换一下哦。这里我就不演示了,SCWS 还是比较智能的,普通的 txt 文件其实大部分情况下还是能满足需求的。

接下来咱们测试一下。

php ./vendor/hightman/xunsearch/util/Quest.php ./config/demo3_56.101.ini --show-query '无三不成'
--------------------
解析后的 QUERY 语句:Query((无三不成@1 SYNONYM (无三不@78 AND 不成@79)))
--------------------
在 1 条数据中,大约有 1 条包含 无三不成 ,第 1-1 条,用时:0.0018 秒。1. 项目测试第三篇 #3# [100%,0.15]
俗话说,无三不成礼,所以就有了第三篇 
Chrono:1314336168  Author:

“无三不成”这个新词没问题。那么“无三不”呢?

> php ./vendor/hightman/xunsearch/util/Quest.php ./config/demo3_56.101.ini --show-query ' 无三不'  
--------------------
解析后的 QUERY 语句:Query((无三不@1 SYNONYM (无三@78 AND 三不@79)))
--------------------
在 1 条数据中,大约有 0 条包含 无三不 ,第 0-0 条,用时:0.0008 秒。

嗯?咋查不到数据了?明明添加到字典里了啊!使用 SDK 的分词工具来看也是正常分词的,分词结果中有“无三不成”、“无三不”这两个单词。

$tokenizer = new XSTokenizerScws;   // 直接创建实例
$words = $tokenizer->getResult("俗话说,无三不成礼,所以就有了第三篇");
foreach($words as $w){echo $w['word']."/".$w['attr']."/".$w['off'],"  ";
}
// 话说/n/0  俗话/n/0  话说/n/3  ,/un/9  无三不成/n/12  无三不/@/12  不成/d/18  礼/n/24  ,/un/27  所以/c/30  就/d/36  就有/v/36  有了/v/39  了第/m/42  第三/m/45  三篇/q/48

好吧,自己玩出来的火,就得自己灭。原理我也没搞明白,但是从上面 getQuery() 的分析中,我们可以通过两种方式来搜索到。

  1. 使用 fuzzy 模糊查询

在 getQuery() 返回的数据中,我们看到了“无三不”以及它的同义二元词“无三”和“三不”。在同义词中,是 AND 连接,也就是必须要同时出现“无三”和“三不”(其实应该也不需要这样,比如搜索“俗话说”,是AND同义词,但是可以搜索到)。但咱们换成 setFuzzy() 效果,全部变成 OR ,就可以通过全局字典中的“无三”查到了。

> php ./vendor/hightman/xunsearch/util/Quest.php ./config/demo3_56.101.ini --show-query '无三不' --fuzzy
--------------------
解析后的 QUERY 语句:Query((无三不@1 SYNONYM (无三@78 OR 三不@79)))
--------------------
在 1 条数据中,大约有 1 条包含 无三不 ,第 1-1 条,用时:0.0018 秒。1. 项目测试第三篇 #3# [100%,0.15]
俗话说,无三不成礼,所以就有了第三篇 
Chrono:1314336168  Author:

好吧,承认我是猜的,上面的分析不是权威分析哦。希望有懂得大佬能在评论区指点一下。

  1. 删除全局字典中的那个“无三”,再重新索引数据。

很奇怪,我们直接删全局字典中的那个“无三”,重新索引添加数据之后,使用“无三不”就可以搜索到数据了。这个真的不知道原因,也不瞎猜了,反正就告诉大家,这样可以搜到。ES 类似的能力我没有测,将来或者说小伙伴有兴趣的可以使用 ES 的 IK 分词器测一下,并在评论区说下啥效果哦,就当是一个小作业啦!

停用词库

XS 的停用词库这一块,即使在官方文档上也没有详细的说明,全网也找不到什么有用的资料,真的独一份哦。

停用词的意思就是这个词不用了,不参与分词。或者说分词器如果看到这个词了,直接略过不管它。如果你学过 ES 中的 IK ,一定会知道这个东西。而且 XS 中也是使用 stopwords 这个文件名来定义停用词库的。

前面在 XS 安装目录的 etc 中目录中,大家就已经看到有一个 stopwords.txt 文件了吧,直接打开它,里面已经有一堆默认内容了,我们再添加一个。

#  vim etc/stopwords.txt
………………
俗话说

添加完之后,来测试一下吧。

>  php ./vendor/hightman/xunsearch/util/Quest.php ./config/demo3_56.101.ini --show-query '俗话说'
--------------------
解析后的 QUERY 语句:Query((俗话说@1 SYNONYM (俗话@78 AND 话说@79)))
--------------------
在 1 条数据中,大约有 1 条包含 俗话说 ,第 1-1 条,用时:0.0020 秒。1. where is which who #3# [100%,0.23]
俗话说,无三不成礼,所以就有了第三篇 
Chrono:  Author:

还是能查到?跟你说,不管你是重建索引,还是重启服务,都没用,这个停用词库感觉就跟没配一样,完全不起作用。这是为啥呢?因为默认情况下,XS 就根本没启用停用词库的功能。所以在官方文档上,你会看到有同学报怨说这个功能没用。但其实,咱们只要开启加载这个功能及停用词库就好啦。

查看我们启动服务器的 xs-ctl.sh 脚本。就是安装目录下面的 bin 目录中的那个脚本文件,我的虚拟机上是位于 /usr/local/xunsearch/bin/xs-ctl.sh 。给最后启动服务的两行代码中,加上 -s etc/stopwords.txt 就可以了。

# vim bin/xs-ctl.sh
………………
# run
case "$cmd" instart|stop|faststop|fastrestart|restart|reload)# indexif test "$server" != "search"; then# bin/xs-indexd $opt_index $opt_pub -k $cmd # 这里是之前的bin/xs-indexd $opt_index $opt_pub -s etc/stopwords.txt -k $cmdfi# searchif test "$server" != "index"; then# bin/xs-searchd $opt_search $opt_pub -k $cmd # 这里是之前的bin/xs-searchd $opt_search $opt_pub -s etc/stopwords.txt -k $cmdfi;;
*)echo "Unknown command: $cmd"show_usage;;
esac

这个参数就表明加载指定的停用词库,并启用停用词功能。然后再来测试。

> php ./vendor/hightman/xunsearch/util/Quest.php ./config/demo3_56.101.ini --show-query '俗话说'
--------------------
解析后的 QUERY 语句:Query(<alldocuments>)
--------------------
在 3 条数据中,大约有 3 条包含 俗话说 ,第 1-3 条,用时:0.0015 秒。1. 关于 xunsearch 的 DEMO 项目测试 #1# [100%,0.00]
项目测试是一个很有意思的行为! 
Chrono:1314336158  Author:  2. 测试第二篇 #2# [100%,0.00]
这里是第二篇文章的内容 
Chrono:1314336160  Author:  3. 项目测试第三篇 #3# [100%,0.00]
俗话说,无三不成礼,所以就有了第三篇 
Chrono:1314336168  Author:

注意看,我们搜索“俗话说”,getQuery() 返回的结果是“alldocument”,就和空查询条件一样。下面的搜索结果也是全部结果都出来了。也就是说,“俗话说”这个停用词已经生效了,SCWS 看到它根本就不管,直接略过。由于没有别的词了,我们的查询结果就和完全没有搜索条件一样,直接返回全部数据了。

我是怎么找到这个功能的?额,在 Github 的 XS 源码中搜 stopword ,结果就找到在 import.cc 、indexed.c 和 searchd.c 中有相关的内容。再仔细看下源码,它们在 -s 这个参数的注释中都写明了要通过这个参数加载停用词库。接着就来看 xs-ctl.sh 脚本,发现在启动索引和搜索服务时,压根就没传这两个参数。剩下的,就不用我多说了吧。

自定义分词器

接下来,我们来试试自定义分词器的功能。这一块是针对 SDK 来说的。在索引配置文件中,我们之前说过有默认的 scws、full、split、none、xlen、xstep 这几种分词类型。其实它们的源码都在 vendor/hightman/xunsearch/lib/XSTokenizer.class.php 这个文件里面。除了 scws 是请求服务端进行分词的,其它几种其实都是 PHP 代码的算法实现,比如说 split ,最终就是一个 explode() 。具体源码大家可以自己去看一下。

这些分词器,其实都是实现了一个 XSTokenizer 接口。而这个接口中,只有一个方法,那就是 getTokens() 方法。那么,如果要实现我们自己自定义的分词器,其实只要和那些自带的分词器一样,实现这个接口方法就可以了嘛。

我们在 vendor/hightman/xunsearch/lib 目录下新建一个 XSTokenizerJieba.class.php 文件,我想使用 Jieba 分词来实现一个分词器。Jiaba-PHP(结巴分词PHP版) https://github.com/fukuball/jieba-php 的文档和说明大家可以在这个 Github 链接上看一下。

# vendor/hightman/xunsearch/lib/XSTokenizerJieba.class.phprequire_once XS_LIB_ROOT . '/../../../autoload.php';class XSTokenizerJieba implements XSTokenizer
{public function __construct($arg = null){}public function getTokens($value, XSDocument $doc = null){// composer require fukuball/jieba-php:dev-masterini_set("memory_limit", "-1");\Fukuball\Jieba\Jieba::init();\Fukuball\Jieba\Finalseg::init();return \Fukuball\Jieba\Jieba::cut($value);;}
}

为什么是在 vendor/hightman/xunsearch/lib 目录呢?能不能像 ini 文件一样放到一个我们指定的目录中呢?抱歉,我看了源码,不行。

// vendor/hightman/xunsearch/lib/XSFieldScheme.class.php
public function getCustomTokenizer()
{if (isset(self::$_tokenizers[$this->tokenizer])) {return self::$_tokenizers[$this->tokenizer];} else {if (($pos1 = strpos($this->tokenizer, '(')) !== false&& ($pos2 = strrpos($this->tokenizer, ')', $pos1 + 1))) {$name = 'XSTokenizer' . ucfirst(trim(substr($this->tokenizer, 0, $pos1)));$arg = substr($this->tokenizer, $pos1 + 1, $pos2 - $pos1 - 1);} else {$name = 'XSTokenizer' . ucfirst($this->tokenizer);$arg = null;}if (!class_exists($name)) {$file = $name . '.class.php';if (file_exists($file)) {require_once $file;} else if (file_exists(XS_LIB_ROOT . DIRECTORY_SEPARATOR . $file)) {require_once XS_LIB_ROOT . DIRECTORY_SEPARATOR . $file;}if (!class_exists($name)) {throw new XSException('Undefined custom tokenizer `' . $this->tokenizer . '\' for field `' . $this->name . '\'');}}$obj = $arg === null ? new $name : new $name($arg);if (!$obj instanceof XSTokenizer) {throw new XSException($name . ' for field `' . $this->name . '\' dose not implement the interface: XSTokenizer');}self::$_tokenizers[$this->tokenizer] = $obj;return $obj;}
}

为指定字段加载分词器的源码就是上面这段,你可以看到,它固定了分词器的名称。然后在下面 require 时,判断了名称如果在当前目录存在,就加载,如果不存在,则接上 XS_LIB_ROOT 目录去加载。可问题是,$name 在前面是通过前缀的方式来固定的,这就相当于给了我们两个选择。

一是自定义分词器要放在运行当前代码的目录路径下,也就是直接 require_once "XSTokenizerJiaba.php" 这一行的效果。比如我们在 source 目录下运行 php 代码,则这个自定义文件就要放在 source 目录下。

二是放在 vendor/hightman/xunsearch/lib 目录下,也就是第二个拼接上 XL_LIB_ROOT 的效果,require_once XS_LIB_ROOT."XSTokenizerJiaba.php"

反正怎么都不是太爽。如果确实需要用到,还是使用第一种吧,毕竟 composer 里面的东西,能不动不就要动了。

好了,不多吐槽了,这一块说不定将来也会有变动呢。我们还是先来测功能。将配置文件中的相关字段的分词器换成我们自定义的。

# config/demo3_56.101.ini
……………………
[subject]
type = title
#tokenizer=scws(15)
tokenizer=jieba[message]
type = body
#tokenizer=scws(15)
tokenizer=jieba
……………………

重新索引数据后,进行测试。

> php ./vendor/hightman/xunsearch/util/Quest.php ./config/demo3_56.101.ini --show-query '俗话说'
--------------------
解析后的 QUERY 语句:Query((俗话说@1 SYNONYM (俗话@78 AND 话说@79)))
--------------------
在 2 条数据中,大约有 1 条包含 俗话说 ,第 1-1 条,用时:0.0027 秒。1. 项目测试第三篇 #3# [100%,0.39]
俗话说,无三不成礼,所以就有了第三篇 
Chrono:1314336168  Author:

“俗话说”要是查不到,可以取消前面的停用词功能哦。我们可以直接使用分词器的 getTokens() 来进行直接的分词测试,并且看看当前默认的分词器用得是哪个。

var_dump($xs->getFieldTitle()->getCustomTokenizer());
// object(XSTokenizerJieba)#9 (0) {}var_dump((new XSTokenizerJieba())->getTokens('俗话说,无三不成礼,所以就有了第三篇'));
// array(11) {
//     [0]=>
//     string(9) "俗话说"
//     [1]=>
//     string(3) ","
//     [2]=>
//     string(6) "无三"
//     [3]=>
//     string(6) "不成"
//     [4]=>
//     string(3) "礼"
//     [5]=>
//     string(3) ","
//     [6]=>
//     string(6) "所以"
//     [7]=>
//     string(3) "就"
//     [8]=>
//     string(3) "有"
//     [9]=>
//     string(3) "了"
//     [10]=>
//     string(9) "第三篇"
//   }var_dump((new XSTokenizerScws)->getResult('俗话说,无三不成礼,所以就有了第三篇'));
//   array(16) {
//     [0]=>
//     array(3) {
//       ["off"]=>
//       int(0)
//       ["attr"]=>
//       string(4) "n"
//       ["word"]=>
//       string(9) "俗话说"
//     }
//     [1]=>
//     array(3) {
//       ["off"]=>
//       int(0)
//       ["attr"]=>
//       string(4) "n"
//       ["word"]=>
//       string(6) "俗话"
//     }
//     [2]=>
//     array(3) {
//       ["off"]=>
//       int(3)
//       ["attr"]=>
//       string(4) "n"
//       ["word"]=>
//       string(6) "话说"
//     }
//  ………………………………
//  ………………………………

看到两个分词器的效果不同了吧。当然,Jiaba 我们没有做任何配置,而 SCWS 默认是 3 也就是最短词和二元一起进行分词的,所以“话说”也会被分出来。

解决单字 Like 问题

大家还记得吧?早前的文章中,我们就说过一个问题,那就是搜索“项”这种默认不会分词的单字,也就是不会建立倒排索引的单字,是无法查询出数据的。那么在学习了分词的原理之后,特别是字典以及上期讲的复合分词等级的内容之后,大家是不是有想法,也能猜到如何来解决这个问题了吧。使用字典,可能比较麻烦,你需要将很多单字加到字典,而且我测试有的情况下还没效果。那么使用复合分词等级呢?咱们就直接设置分词复合等级为 15 。也就是配置文件中的 title 和 body 字段,都将它们的 tokenizer 设置成 scws(15)

接下来重新索引添加数据,然后查询试试。

> php ./vendor/hightman/xunsearch/util/Quest.php ./config/demo3_56.101.ini --show-query '项'    
--------------------
解析后的 QUERY 语句:Query(项@1)
--------------------
在 2 条数据中,大约有 2 条包含 项 ,第 1-2 条,用时:0.0012 秒。1. 关于 xunsearch 的 DEMO 项目测试 #1# [100%,0.16]
项目测试是一个很有意思的行为! 
Chrono:1314336158  Author:  2. 项目测试第三篇 #3# [99%,0.16]
俗话说,无三不成礼,所以就有了第三篇 
Chrono:1314336168  Author:

可以通过单个“项”字查询到数据了吧。复合分词等级的知识上次已经说过,设置成 15 其实就是各种词项、二元、重要单字、全部单字都进行拆分,拆分出来的内容非常多。其实这么做的结果大家也能想到,就是它会带来一个非常重大的问题:倒排索引库会变得非常大,而且如果 body 字段是那种文献类型的超长文章或超大文章,索引缓冲也会出问题导致索引失败。即使是在 ES 中,也没法这么玩的。

又回到当初的那个问题了。搜索引擎+分词器(倒排索引),不是 Like !!

总结

自定义字典有点意思吧?更重要的是,咱们还有全网唯一一份的 XS 中停用词的使用方法哦。这不来个赞真的对不起这篇文章啊。另外,通过自定义分词器,咱们也能看出来,XS 完全也是可以使用别的分词工具的嘛。不管是你是想用第三方的 Jieba 分词还是别的什么非常特殊的行业切分格式,都可以通过代码自己来实现。最后,关于分词的内容,在 ES 中现在最流行的是 IK 分词器,小伙伴们在学习 ES 的时候,如果学到了 IK 的部分,其实有这两篇 SCWS 的基础垫底,掌握起来还是会比较轻松的。毕竟什么词性、分词级别、字典,甚至字典名称有很多都是相同的。

分词部分的学习结束了,我们的搜索引擎整体课程也就接近尾声了。后面的两篇文章是扩展部分,也是很有意思的哦,我们会看一下 Xapian 官方文档中我发现的一些有用的内容。最后还会介绍一套非常有意思的完全 PHP 实现的搜索引擎方案哦。

测试代码:

https://github.com/zhangyue0503/dev-blog/blob/master/xunsearch/source/17.php

参考文档:

http://www.xunsearch.com/doc/php/guide/index.dict

http://www.xunsearch.com/doc/php/guide/ini.tokenizer

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/625195.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【新】Unity Meta Quest MR 开发(一):Passthrough 透视配置

文章目录 &#x1f4d5;教程说明&#x1f4d5;配置透视的串流调试功能&#x1f4d5;第一步&#xff1a;设置 OVRManager&#x1f4d5;第二步&#xff1a;添加 OVRPassthroughLayer 脚本&#x1f4d5;第三步&#xff1a;在场景中添加虚拟物体&#x1f4d5;第四步&#xff1a;设置…

202406读书笔记|《沉睡的线条世界》——翻山越岭,只为与你分享点滴的快乐

《沉睡的线条世界》登登登Dn绘著&#xff0c;简简单单的小画&#xff0c;简简单单的线条&#xff0c;简简单单的语言&#xff0c;温馨又有一点暖心。 怎样的你都好&#xff0c;做最真实的自己。 部分节选如下&#xff1a; 愿你我永远有热情&#xff0c;永远能为生活的每一个小惊…

阳光抑郁症测试

大部分人对抑郁症的理解&#xff0c;就是每天无精打采&#xff0c;死气沉沉&#xff0c;可实际上&#xff0c;还有一种阳光抑郁症&#xff0c;完全不是这个样子。这种抑郁症的人&#xff0c;做事情非常有活力&#xff0c;魅力十足&#xff0c;给人感觉十分有自信&#xff0c;但…

快速排序【hoare版本】【挖坑法】【双指针法】(数据结构)

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法&#xff0c;其基本思想为&#xff1a;任取待排序元素序列中 的某元素作为基准值&#xff0c;按照该排序码将待排序集合分割成两子序列&#xff0c;左子序列中所有元素均小于基准值&#xff0c;右子序列中所有元素均…

如何写年终汇报

每年最头疼的事儿就是写年终个人汇报&#xff08;总结&#xff09;。有时还有篇幅限制&#xff0c;疼上加疼。 根据个人经验和参考其他同事的汇报&#xff0c;做了一个简要的总结&#xff0c;方便以后写汇报使用。 个人汇报大的逻辑是&#xff1a;做了什么做的怎么样&#xff…

MySQL多表查询(改进版)

1.创建student和score表 mysql> CREATE TABLE student (-> id INT(10) NOT NULL UNIQUE PRIMARY KEY ,-> name VARCHAR(20) NOT NULL ,-> sex VARCHAR(4) ,-> birth YEAR,-> department VARCHAR(20) ,-> address VARCHAR(50)-> ); Query O…

242.【2023年华为OD机试真题(C卷)】螺旋数字矩阵(JavaPythonC++JS实现)

🚀点击这里可直接跳转到本专栏,可查阅顶置最新的华为OD机试宝典~ 本专栏所有题目均包含优质解题思路,高质量解题代码(Java&Python&C++&JS分别实现),详细代码讲解,助你深入学习,深度掌握! 文章目录 一. 题目-螺旋数字矩阵二.解题思路三.题解代码Python题解…

SpringCloud Nacos服务注册中心和配置中心

一、什么是Nacos&#xff1f; 官方介绍是这样的&#xff1a; Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集&#xff0c;帮助您实现动态服务发现、服务配置管理、服务及流量管理。 Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。 Na…

用python批量实现文件夹中所有pdf转成图片并插入到一个word文件中

要实现这个任务&#xff0c;你需要使用Python的几个库&#xff1a;PyPDF2 用于处理PDF文件&#xff0c;python-docx 用于操作Word文件&#xff0c;PIL&#xff08;或Pillow&#xff09;用于处理图片。 首先&#xff0c;确保你已经安装了这些库。如果没有&#xff0c;你可以使用…

Java 方法以及在计算机内部的调用问题

修饰符 返回值类型 方法名( 形参列表 ){ 方法体代码(需要执行的功能代码) return 返回值; } 方法在内种没有先后顺序&#xff0c;但是不能把一个方法定义在另一个方法中。 方法的返回值类型写void&#xff08;无返回申明&#xff09;时&#xff0c;方法内不能使用return返回数…

Linux中放大字体

环境&#xff1a;VMware17Pro&#xff0c;Ubuntu22.04 在显示设置外观中只看到图标放大的调整&#xff0c;没看到字体大小设置 不按照常规设置&#xff0c;点开下面的辅助功能->大号文本&#xff08;没有设置具体字号的选项&#xff0c;但是可以放大&#xff09; 效果图如下…

day-10 删除排序链表中的重复元素

思路 先统计每个值出现的次数&#xff0c;然后将出现次数为一的节点链接为一个链表即可 解题方法 while(t!null){ //统计每个值出现次数 arr[t.val100]1; tt.next; } while(t!null&&arr[t.val100]!1) tt.next;//确定返回的头结点 ttt; while(t!null&&t.next…

项目解决方案:多个分厂的视频监控汇聚到总厂

目 录 1、概述 2、建设目标及需求 2.1 建设目标 2.2 需求描述 2.3 需求分析 3. 设计依据与设计原则 3.1 设计依据 3.2设计原则 1、先进性与适用性 2、经济性与实用性 3、可靠性与安全性 4、开放性 5、可扩充性 6、追求最优化的系统设备配置…

Zynq7020 使用 Video Processing Subsystem 实现图像缩放

1、前言 没玩过图像缩放都不好意思说自己玩儿过FPGA&#xff0c;这是CSDN某大佬说过的一句话&#xff0c;鄙人深信不疑。。。 目前市面上主流的FPGA图像缩放方案如下&#xff1a;1&#xff1a;Xilinx的HLS方案&#xff0c;该方案简单&#xff0c;易于实现&#xff0c;但只能用…

MySQL语句 | 使用DATE_FORMAT()对查询结果按日、周、月分组统计

文章目录 语法举个通用的例子 按照年月日进行分组统计举个通用的例子举个实际的例子 在MySQL中&#xff0c; DATE_FORMAT() 函数可用于将日期按照指定的格式进行格式化&#xff0c;也可用于对数据记录按照不同的时间维度进行分组 语法 DATE_FORMAT(date, format)其中&#xf…

java+vue基于Spring Boot的渔船出海及海货统计系统

该渔船出海及海货统计系统采用B/S架构、前后端分离进行设计&#xff0c;并采用java语言以及springboot框架进行开发。该系统主要设计并完成了管理过程中的用户注册登录、个人信息修改、用户信息、渔船信息、渔船航班、海货价格、渔船海货、非法举报、渔船黑名单等功能。该系统操…

Elasticsearch安装Windows版

目录 1.&#xff1a;下载安装包&#xff0c;选择指定的版本&#xff0c;这里选择了7.8.0&#xff0c;官网下载地址&#xff1a; ​编辑 2&#xff1a;下载好之后解压&#xff0c;解压之后是这样的&#xff1a; 3&#xff1a;配置环境变量&#xff0c;跟JDK一样&#xff0c;…

登录页加载图片失败的默认图片占位图如何实现?<img>

问题&#xff1a; 登录页动态显示不同项目的图片的时候&#xff0c;若环境变量的图片加载失败&#xff0c;如何显示占位图logo? 解决&#xff1a; <img :οnerrοr"方法" />注意onerror的是方法 关键代码&#xff1a; <img draggable"false"…

rime中州韵小狼毫 联想词组 滤镜

教程目录&#xff1a;rime中州韵小狼毫须鼠管安装配置教程 保姆级教程 100增强功能配置教程 在 rime中州韵小狼毫 自定义词典 一文中&#xff0c;我们分享了如何在rime中州韵小狼毫须鼠管输入法中定义用户自定义词典&#xff1b;通过自定义词典&#xff0c;我们可以很方便的在…