Rime 输入法定制五笔

Rime 是一款Hacker级的输入法,有非常强大的定制性及不错的性能,输入法家族的神一般的存在。
Rime 支持不同Win, Mac, Linux三大操作平台,并且分别有不一样的名字:中州韵,小狼毫,鼠须管。
之前主要用Ubuntu时,Rime是相比iBus, Fcitx 更好用的输入法,现在用Mac之后,也试过其它一些五笔输入法,都有一些不太方便的地方。

Rime没有图形化的配置界面,需要手动修改yml配置文件,但这基本不影响它的简洁强大。

主要目标:

  • 五笔输入,编码反查
  • 支持临时用拼音
  • 中英文切换
  • 输入特殊符号
  • Shift编码直接上屏
  • 输入Emoji表情
  • 可以创建自定义词组
  • 配置和字库同步

五笔输入, 编码反查

默认安装完Rime后是没有五笔输入法的,但添加非常简单:

修改~/Library/Rime/default.custom.yml 文件:

patch:
    schema_list:
      - schema: wubi_pinyin

把其它无用的schema都删掉了。  
这里也可以使用wubi86, 如果你只想输入五笔。
默认支持编码反查。

临时用拼音

因为用了wubi_pinyin 这套方案,所以是可以五笔拼音混输的。
另外,还发现Rime输入法是支持五笔整行输入的,只要不重码,可以一直输入完整段话。

输入特殊符号

symbols.yaml文件中有定义一些特殊符号,同样可以像配置Emoji一样,将不同词库结合起来.
修改wubi_pinyin.custom.yaml文件,添加如下两行配置:
yaml
'punctuator/import_preset': symbols
'recognizer/patterns/punct': "^/([a-z]+|[0-9])$"

比如,可以输入/dn(dian nao),  ❖ ⌘

Shift编码直接上屏

patch:
    ascii_composer/switch_key:
        Shift_L: commit_code
        Shift_R: commit_text

输入Emoji表情

默认输入Emoji是需要按Ctrl + ~进行切换的, 比较麻烦. Rime 的强大之处在于可以组合词库.

由于我们用的是wubi_pinyin这个词库, 因此创建一个wubi_pinyin.custom.yaml文件,在里面填入:

patch:
    schema/dependencies:
        - emoji
        - symbols
    reverse_lookup:
        dictionary: emoji
        enable_completion: true
        prefix: "`"
        tips: [emoji]

然后重新Deploy. 即可直接输入Emoji了, 非常方便. 😊

可以创建自定义词组

Rime 默认会自己学习词组, 像其它一些现代输入法一样, 陌生词只要输入一次, 下次就可以用简码输入了.

配置和字库同步

Rime 有简单的本地文件夹同步, 可以把它配置到iCloud上面.

修改installation.yaml文件, 添加:
yaml
sync_dir: "/Users/ijse/Library/Mobile Documents/com~apple~CloudDocs/Rime"

然后Deploy即可.

团队技术选型和工具选择标准

六个要素 和 五个误区

SPLSCM六个要素:

  • 战略性 Strategic
    • 站在行业和业务需求角度思考,我们的技术战略是跨平台例如
      • 我们选择ES6而非CoffeeScript, 因为ES6是前端行业内的标准;
      • 我们的产品是需要跨终端的,所以选择Web这种技术实现
  • 先进性 Progressiveness
    • 要符合当前主流的技术标准
      • 例如我们选择ES6, Vue 2.0(尽管目前还是RC版本)
  • 易学性 Learnability
    • 简单易学且适用的技术和工具 远比 功能强大且不能在项目中灵活运用的工具更有效
    • 不要用整体解决方案,因为那些通常是局部最优
      • 我们应该选择最优组合,选择多个成功案例验证的结果
  • 扩展性 Scalability
    • 意味着一旦需求发生变化 ,系统能够尽快得到扩充
  • 兼容性 Compatibility
    • 可以有效地节约投资,方便数据 和信息共享
  • 成熟性 Maturity
    • 技术和工具被使用得越广泛说明其越成熟
    • stackshare.io 参考其它团队的技术和工具栈

五个误区:

  1. 决策时考虑沉没成本
  2. 追求完美
  3. 迷信整体解决方案
  4. 把试验田进行商业推广
  5. 不自知团队的学习曲线

团队的三种管理工具:

  1. 项目管理工具(Trello)
    1. 任务分配:任务拆解结果,计划和分配
    2. 任务状态跟踪:团队及时更新Trello任务状态,记录必要信息
    3. 项目进度展示:Trello看板要能够始终呈现当前的团队任务完成情况
    4. 基本问题/风险管理:重要问题要显著标注、受到关注
  2. 沟通管理工具(Slack, Email)
    1. Slack做信息的汇总呈现
    2. 团队成员间的即时交流
    3. 重要决定、事项通过Email备忘
  3. 知识管理工具(Quip)
    1. 团队要不断学习和总结,这样才能持续保持高效
    2. 不注重积累的团队往往知识就存在某几个人的手中,无法使团队进步

有关HTTPS的一些介绍

HTTPS(Hypertext Transfer Protocol Secure),即超文本传输安全协议,是HTTP与SSL/TLS的组合,用以提供加密通讯及对网络服务器身份的鉴定。

由于http是明文传输的,所以在互联风上传输隐私信息非常不安全,目前国外大多数网站都已经全面支持https, 但国内情况很差,不仅很少有大型网站全站支持https,甚至有些网站使用https不规范,安全协议过时,留下了一些安全隐患。

https的原理介绍

https在不安全的网络上创建了一个安全信道,通过使用加密套件和服务器证书可被验证且可被信任,从而防止信息被截获或中间人攻击,实现保证TCP协议之上的传输层安全。

目前浏览器中都会内置一些根证书(常见的如Microsoft、VeriSign等),基于这些根证书以及这些根证书下签署的证书是可被信任的,通常证书是由这些根证书颁发机构来颁发。

用户访问https协议的网址时,客户端浏览器与服务器握手时,会验证服务器的证书有效性,即是否由一个被信任的证书颁发机构签发;如果不是,浏览器会对用户进行警告。然后浏览器会根据握手时约定好的加密信息加密和解密与服务器之间传输的数据,从而保障数据在传输层的安全。

详细的握手过程下面介绍。

https加密协议版本

主要有TLS和SSL两种协议。

TLS/SSL介绍

SSL(Secure Sockets Layer)由网景(Netscape)公司设计,用于对http协议传输的数据进行加密,由此诞生了https. SSL最新的版本是3.0, 之后IETF对SSL 3.0进行了升级,设计了TLS(Transport Layer Security) 1.0, 目前最新版本是1.2, 但还未被广泛支持。

有关SSL/TLS协议的运行机制,可以看阮老师的文章:SSL/TLS协议运行机制的概述

加密方式

https协议使用了三种加密方式:对称加密、非对称加密和HASH算法:

  • 非对称加密算法:RSA,DSA/DSS
    用于在握手时加密生成的密码
  • 对称加密算法:AES,RC4,3DES
    用于对传输的数据进行加密
  • HASH算法:MD5,SHA1,SHA256
    用于验证数据的完整性、有无被篡改

非对称加密算法时生成公钥只能用于加密数据,是公开的;而网站的私钥用于服务器端对数据进行解密,所以需要特别保管,不能泄漏。

终端的支持情况

据统计,目前大部分网站使用的TLS 1.0协议,其次是TLS 1.1TLS 1.2,SSL协议已经支持的很少了,而且被认为是不安全和低效率的。(数据来自Wikipedia)

目前决大部分浏览器都已经对https支持很好了,国外Google、Twitter、Facebook都已经全站支持了,因此可以放心使用。除一些旧的浏览器如IE6等,如使用最广泛的TLS 1.0协议

  • 在IE6及以下版本中是默认Disabled或根本不支持的;

  • 在Opera 4以下版本不支持

除此之外,最新版本的浏览器支持情况还是不错的。

https解决的安全问题

  • 保护隐私数据,由于数据传输是加密的,因此黑客很难截获和破解信息;因此很多网站至少登陆页面使用的是https。
  • 保障数据完整性,确保数据在传输过程中不会被修改,跟邮件的签名是一样的,比如可以防止运营商劫持、篡改网页内容。

  • 防止钓鱼网站,可以通过在浏览器上查看证书确定所访问的网站是否是钓鱼网站

https的缺点

  • 费用问题
    https证书颁发是需要一些费用的,一般一年几十美元至上百美元,单域名证书便宜一些,而泛域名证书会很贵。不过也有一些免费证书。

  • 响应问题
    由于增加了握手时的密钥生成和检查过程,因此建立连接时间相比http协议会更长一些,约200ms, 不过有优化的空间,(见下文)

  • 缓存问题
    虽然https协议的数据是可以被缓存的,只要设置好max-age等http缓存头,但是仍有部分浏览器或者网络结点服务器不能够很好地处理https缓存。

  • 性能问题
    由于增加了服务器的加密、解密过程,因此会消耗额外的cpu资源。

  • 收录问题
    截止目前,百度对https的网站的抓取仍然不支持。

https的握手过程

  1. 浏览器将自己支持的一套加密规则发送给网站。

  2. 网站从中选出一组加密算法与HASH算法,并将自己的身份信息以证书的形式发回给浏览器。证书里面包含了网站地址,加密公钥,以及证书的颁发机构等信息。

  3. 获得网站证书之后浏览器要做以下工作:
    a) 验证证书的合法性(颁发证书的机构是否合法,证书中包含的网站地址是否与正在访问的地址一致等),如果证书受信任,则浏览器栏里面会显示一个小锁头,否则会给出证书不受信的提示。
    b) 如果证书受信任,或者是用户接受了不受信的证书,浏览器会生成一串随机数的密码,并用证书中提供的公钥加密。
    c) 使用约定好的HASH计算握手消息,并使用生成的随机数对消息进行加密,最后将之前生成的所有信息发送给网站。

  4. 网站接收浏览器发来的数据之后要做以下的操作:
    a) 使用自己的私钥将信息解密取出密码,使用密码解密浏览器发来的握手消息,并验证HASH是否与浏览器发来的一致。
    b) 使用密码加密一段握手消息,发送给浏览器。

  5. 浏览器解密并计算握手消息的HASH,如果与服务端发来的HASH一致,此时握手过程结束,之后所有的通信数据将由之前浏览器生成的随机密码并利用对称加密算法进行加密。

获取证书

  • 免费渠道startsslwosign
    不需要多域名支持,个人使用,一般免费的就够用了,通用性不错,一般浏览器都支持。ijser.cn使用的就是wosign的免费证书。

  • 收费渠道
    收费的就很多了,如godaddy什么的。

  • 自己签署
    如果是app自用,可以选择自建CA,自己给自己签署证书。好处是方便,不必花钱;坏处是需要自己维护CA,保障安全。

证书通常有三种, 购买的时候可根据需要选择:

  1. 单域名证书,只签署一个域名,通常会包括如(ijser.cn, www.ijser.cn)
  2. 多域名证书,可以签署多个静态子域名, 如(blog.ijser.cn, cdn.ijser.cn), 每个域名需要单独配置。
  3. 泛域名证书,可以签署多个动态子域名,如(*.ijser.cn)

配置服务器

  • nginx
    一般我们会用nginx作为服务器http服务的最外层代理,处理或转发http请求,相应的配置方法如下:
server {
  ...
  ssl on;
  ssl_certificate /yours.crt;
  ssl_certificate_key /yours.key;
}

以上是最基本的配置,但还可以再优化些, 在server段中添加:

keepalive_timeout 70;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;

以上配置是对https进行优化,让https链接保持,并且设置各进程间的session缓存。

强制使用https

我的做法是将http请求全部301重定向到相应的https url上。https使用的是443端口,而http用的是80端口。下面是主要nginx配置:

server {
listen 80;
server_name ijser.cn www.ijser.cn;
return 301 https://www.ijser.cn$request_uri;
}

以上配置会将所有来自ijser.cn, www.ijser.cn 的http请求重定向到 https://www.ijser.cn

Mixed Content问题

当浏览器地址栏上的https认证图标是黄色三角号警告时,表示当前https页面中含有http协议的资源(未加密内容),如'css,js,image'等外链资源。潜在的安全隐患是:

  1. 这些内容可能会被中间人篡改
  2. 通过http请求发送的cookies可能会被截获

解决的办法也是有的,如Github允许用户在发布的Markdown内容中插入站外图片,如果站外图片的url地址是http的,直接在页面上显示是会导致黄色警告的,Github的解决方案是通过服务器抓取这些图片到自己的服务器上,然后用https访问它们。

至于图片木马问题,可以通过在http response中添加header X-Content-Type-Options: nosniff解决。

https的一些最佳实践

  1. 将所有http请求301重定向为https
  2. 设置保持链接keepalive, 并调整合适的timeout值
  3. 保证页面中的其它资源链接都来自https
  4. 将静态资源放到单独的域名下,隔离cookies

https了就确定安全吗?No.

因为https是基于默认信任根证书的,所以如果CA被黑掉,则其下签发的证书都有安全问题,如最近的CNNIC证书问题。

另外,SSL/TLS加密算法的实现也可能有Bug,如去年的心脏出血。

最后,https只是保证了传输过程中不被中间人攻击和服务端是否伪造,其它安全问题仍是无法保证的,如XSS、XSRF、DDos等等。

结尾

可能我们开发网站的大多数都是无证程序员,但最好还是让我们的网站有证上岗,至少可以保障部分安全,于我们的服务器、于我们的用户信息。

参考网站

Docker 笔记 打造node.js开发环境 安装nvm

通过制作一个Docker镜像,统一管理开发环境,使不同开发者可以在任何平台下开发,同时拥有相同的开发环境配置。

文章所用的示例代码发布在Github上了,点击此处查看

创建Dockerfile文件


###########
# Usage:
#
#   docker run -it -v <project-resource-directory>:/workspace \
#                  --name <container-name> [image-name]
#

FROM centos
MAINTAINER ijse

RUN yum -y update
RUN yum -y install tar git

# 配置环境变量
ENV NVM_DIR /usr/local/nvm
ENV NODE_VERSION 0.10.32
ENV WORK_DIR /workspace

# 下载和配置Node.js环境
# 这些命令一定要写在一起, 否则`nvm`命令会找不到
RUN curl https://raw.githubusercontent.com/creationix/nvm/v0.24.0/install.sh | bash \
    && source $NVM_DIR/nvm.sh \
    && nvm install v$NODE_VERSION \
    && nvm use v$NODE_VERSION \
    && nvm alias default v$NODE_VERSION

ENV NODE_PATH $NVM_DIR/v$NODE_VERSION/lib/node_modules
ENV PATH      $NVM_DIR/v$NODE_VERSION/bin:$PATH

# 设置工作目录
WORKDIR $WORK_DIR

# 公开镜像的80端口
EXPOSE 80

CMD node
  • 每条RUN命令即是一层(Layer),Docker会将其缓存,对提高存储加快build速度很有用。
  • 每次RUN命令,都被分配了一个单独的进程环境执行,因此一些环境变量等是不同的。
  • CMD命令相当于docker run中执行的命令,

构建出image

$ docker build --force-rm -t ijse/nvm .
$ docker images

构建成功后,会看到出现了一个ijse/nvm的镜像。

注意,build 的时候到如下一步时:
“`
=> Close and reopen your terminal to start using nvm

不要动,这时候正在下载安装Node.js,可能会花费比较长时间,如果等得不耐烦,可以另开一个终端,用`docker ps`查到容器Id, 然后`docker attach <容器Id>`来查看进度。

#### 试着跑一下~
镜像构建成功后,就应该出现在`docker images`列表中了,用如下命令试着运行一下容器内的终端:

$ docker run -it –name test-nvm ijse/nvm

接着便进入了node的交互命令行下,可以执行
process.version`查看容器中node.js的版本。

把代码装载

由于代码会经常更新,将它们打包进镜像中不太合适,一般通过目录挂载的方式,将代码目录放在宿主机上,这样可以分开管理。

启动的时候添加-v参数挂载代码目录:

$ docker run -it --name test-nvm -p 80 -v /myPorject:/workspace ijse/nvm bash

运行后即进入容器的bash下,此时可以继续执行开发相关的命令了,如npm install && npm start

-p 3000:80参数,将容器的80端口绑定到宿主机的3000端口上,于是我们可以访问http://localhost:3000来访问容器的80端口服务了。

注意:

  • 如果是在Windows或Mac系统下,由于Docker是运行在虚拟机里的,所以访问时localhost要换为虚拟机的ip地址。
  • npm install的时候,由于挂载机制,在Windows下可能需要添加--no-bin-links参数

全部代码

示例的代码放到了Github上: http://github.com/ijse/nodejs-docker-image

后记

这个示例很简单,而通常情况下我们的项目会更复杂些,不仅环境会有很多配置修改,还会需要其它服务,如MongoDB, Redis,甚至其它一些Web Services等。我们可以把这些服务都打包进一个镜像中,也可以分开。使用差不多的方式编写相应的Dockerfile,并配置环境即可。

于是便可以看出Docker的方便之处:

  1. 直接分发Dockerfile即可,其它开发者可以自己构建出镜像,并且与大家开发环境一致
  2. 跨平台开发的方便,无论宿主机是什么平台,Docker都可以保证代码运行的环境与线上环境一致
  3. 维护方便,只需要维护Dockerfile即可随时管理更新开发环境

Docker上手很快,一般使用的话,运行几条命令即可满足需求。简直是程序开发的福音。

package.json for NPM 文件详解

package.json文件描述了一个NPM包的所有相关信息,包括作者、简介、包依赖、构建等信息。格式必须是严格的JSON格式。

通常我们在创建一个NPM程序时,可以使用npm init命令,通过交互式的命令,自动生成一个package.json文件,里面包含了常用的一些字段信息,但远不止这么简单。通过完善package.json文件,我们可以让npm命令更好地为我们服务。

name

nameversion是package.json中最重要的两个字段,也是发布到NPM平台上的唯一标识,如果没有正确设置这两个字段,包就不能发布和被下载。

下面是官方给出的一些建议:

  • 名字里不要再包含"js"和"node",因为默认NPM包就是node.js程序,不过你可以通过engines字段来指定。
  • 名字将会被作为url的一部分,所有要符合http url的一般命名规则,不能包含url非法字符,也不能以._开头。
  • 名字也将作为require()命令的参数,所以应该尽量简明。
  • 如果包要发布到NPM平台上的话,最好先检查下有没有重名, 并且字母只能全部小写。

新版本的NPM可以指定scope, 名字可以加前缀标识,如@ijse/mypackage

version

这个字段的取值需要符合node-semver的规则,详细可以见其文档。

description

包的描述信息,将会在npm search的返回结果中显示,以帮助用户选择合适的包。

keywords

包的关键词信息,是一个字符串数组,同上也将显示在npm search的结果中。

homepage

包的主页地址

bugs

包的bug跟踪主页地址,应该如下设置:

bugs: {
  "url": "http://github.com/ijse/project/issues",
  "i@ijser.cn": "my@ijser.cn"
}
license

包的开源协议名称

author

包的作者,可以是字符串或对象:

author: {
  "name": "ijse",
  "i@ijser.cn": "my@ijse.cn",
  "url": "http://www.ijser.cn"
}

或者:

author: "ijse <my@ijser.cn> (http://www.ijser.cn)"
contributors, maintainers

包的贡献者,是一个数组。

files

包所包含的所有文件,可以取值为文件夹。

通常我们还是用.npmignore来去除不想包含到包里的文件。

main

包的入口文件,如index.js

bin

如果你的包里包含可执行文件,通过设置这个字段可以将它们包含到系统的PATH中,这样直接就可以运行,很方便。如:

"bin": {
  "iapp": "./cli.js"
}

当包被安装后,NPM将创建一个cli.js文件的链接到/usr/local/bin/iapp下。

man

为系统的man命令提供帮助文档, 如:

"man": "./man/doc.1"

帮助文件的文件名必须以数字结尾,如果是压缩的,需要以.gz结尾。

如果是字符串数组:

"name": "foo",
"man": ["./man/foo.1", "./man/bar.1", "./man/foo.2" ]

则分别可以man foo, man foo-bar, man 2 foo来查看。

directories

CommonJS包所要求的目录结构信息,目前除了告诉别人你的程序目录结构,貌似没有别的什么用。
下级字段可以是:lib, bin, man, doc, example。
每个都是字符串

repository

包的仓库地址。如:

"repository": {
  "type": "git",
  "url": "http://github.com/ijse/project.git"
}
scripts

通过设置这个可以使NPM调用一些命令脚本,封装一些功能。

config

添加一些设置,可以供scripts读取用,同时这里的值也会被添加到系统的环境变量中。

"name": "foo",
"config": {
  "port": "8080"
}

npm start的时候会读取到npm_package_config_port环境变量。

同时也可以使用npm config命令来修改设置:

npm config set foo:port 8001
dependencies

指定依赖的其它包,这些依赖是指包发布后正常执行时所需要的,如果是开发中依赖的包,可以在devDependencies设置。

通常使用下面命令来安装:

npm install --save otherpackage

形式可以有如下多种:

  • version 严格匹配某个版本
  • >version 必须大于某个版本
  • >=version
  • <version
  • <=version
  • ~version 大概匹配某个版本
  • ^version 兼容某个版本
  • 1.2.x 可以是1.2.0, 1.2.1等等,但不能是1.3.0
  • http://... 指定tarball的url地址
  • * 任何版本都可以
  • "" 同上
  • version1 - version2 >=version1 <=version2
  • range1 || range2 满足range1 或range2
  • git://... git地址
  • user/repo 同上
  • tag 指定某个tag的版本
  • path/path 本地包所有文件夹

下面都是可以用的:

{ "dependencies" :
  { "foo" : "1.0.0 - 2.9999.9999"
  , "bar" : ">=1.0.2 <2.1.2"
  , "baz" : ">1.0.2 <=2.3.4"
  , "boo" : "2.0.1"
  , "qux" : "<1.0.0 || >=2.3.1 <2.4.5 || >=2.5.2 <3.0.0"
  , "asd" : "http://asdf.com/asdf.tar.gz"
  , "til" : "~1.2"
  , "elf" : "~1.2.3"
  , "two" : "2.x"
  , "thr" : "3.3.x"
  , "lat" : "latest"
  , "dyl" : "file:../dyl"
  }
}

Git URL可以有如下种形式:

git://github.com/user/project.git#commit-ish
git+ssh://user@hostname:project.git#commit-ish
git+ssh://user@hostname/project.git#commit-ish
git+http://user@hostname/project/blah.git#commit-ish
git+https://user@hostname/project/blah.git#commit-ish
devDependencies

这些依赖只有在开发时候才需要。

npm install --save-dev mypack
peerDependencies

相关的依赖,如果你的包是插件,而用户在使用你的包时候,通常也会需要这些依赖(插件),那么可以将依赖列到这里。

举个例子,如karma, 它的package.json中有设置:

"peerDependencies": {
  "karma-jasmine": "~0.1.0",
  "karma-requirejs": "~0.2.0",
  "karma-coffee-preprocessor": "~0.1.0",
  "karma-html2js-preprocessor": "~0.1.0",
  "karma-chrome-launcher": "~0.1.0",
  "karma-firefox-launcher": "~0.1.0",
  "karma-phantomjs-launcher": "~0.1.0",
  "karma-script-launcher": "~0.1.0"
}

这些都是karma的相关插件,一般使用karma的时候都会需要。

bundledDependencies

绑定的依赖包,发布的时候这些绑定包也会被一同发布。

optionalDependencies

即使这些依赖没有,也可以正常安装使用

engines

指定包运行的环境

"engines": {
  "node": ">=0.10.3 < 0.12",
  "npm": "~1.0.20"
}
engineStrict

设置为true强制限定 engine

os

指定你的包可以在哪些系统平台下运行。

"os": [ "darwin", "linux", "!win32" ]

即可以在darwinlinux平台下运行,而不能在win32下。这里设定的取值是来自process.platform的。

cpu

可以指定包运行的cpu架构,如

"cpu": [ "x64", "!arm" ]

取值来自process.arch

preferGlobal

如果你的包是命令行运行的,那可以将其设置为true建议用户全局(npm install -g)安装。但它并不强制用户。

private

设为true这个包将不会发布到NPM平台下。

publishConfig

这个字段用于设置发布时候的一些设定。尤其方便你希望发布前设定指定的tagregistry

也可以设定其它子字段,但只有tagregistry会影响到发布。

NPM的一些默认值说明

  • "scripts": { "start": "node server.js" }
    如果在项目根目录下含有server.js文件,则NPM会自动设置此值。

  • "scripts": { "preinstall": "node-gyp rebuild" }
    如果在项目根目录下含有binding.gyp文件,则NPM会自动设置此值。

  • "contributors": [...]
    如果项目根目录下含有AUTHORS文件,则NPM会自动将每一行以Name <i@ijser.cn> (url)的格式读取并设定此字段。