3个版本 (重大更新)
0.9.0 | 2019年3月3日 |
---|---|
0.8.0 | 2019年1月2日 |
0.7.0 | 2018年2月7日 |
#362 in 文本处理
364,066 monthly downloads
用于 886 个crate (直接使用18个)
12KB
70 行
UNIC:Rust的Unicode和国际化组件
https://github.com/open-i18n/rust-unic
UNIC 是一个旨在为 Rust 编程语言开发组件的项目,以提供高质量的、易于使用的 Unicode 和国际化数据及算法的 crate。换句话说,它就像是 Rust 的 ICU,完全用 Rust 编写,主要在 安全 模式下运行,但在可能的情况下,也会从 不安全 模式的性能提升中受益。
查看 UNIC 变更日志 了解最新版本详情。
项目目标
UNIC 的目标是提供访问 Unicode 和国际化功能的各个级别,从 Unicode 字符属性开始,到处理文本的 Unicode 算法,以及基于 Unicode Common Locale 数据库 (CLDR) 的更高级(基于区域设置)过程。
根据需要,还实现了其他标准和最佳实践,如 IETF RFCs。
项目状态
目前 UNIC 正在快速发展:API 在 master
分支上频繁更新,每个 0.x
版本之间都可能发生 API 断裂。请参见 公开问题 了解变更计划。
我们预计将在 2018 年发布 1.0
版本,之后保持 API 的稳定性,在最初的几年里,可能每年进行一到两次 API 更新。
设计目标
-
UNIC 的主要目标是通过易于使用的 API 提供可靠的功能。因此,新添加的组件可能没有很好地优化性能,但将包含足够的测试以展示对标准的符合性,以及示例以展示用户如何使用它们来满足常见需求。
-
UNIC 组件的下一个主要目标是性能和低二进制和内存占用。特别是,优化运行时对 ASCII 和其他常见情况将鼓励适应,而无需担心减慢常规开发过程。
-
在可能的情况下,组件将保证提供一致的数据和算法。使用跨组件测试来捕捉实现之间的任何不一致,而不会减慢开发过程。
组件及其组织
UNIC 组件 采用分层组织结构,从 unic
根开始,包含 主要组件。每个主要组件反过来可以包含一些 次要组件。
主要组件的API是为库的最终用户设计的,预计将广泛进行文档编写并伴随代码示例。
与主要组件相反,次要组件作为数据和高层次算法的提供者,其API预计性能更高,并提供多种访问数据的方式。
UNIC超级组件
unic
超级组件是一组所有UNIC(主要)组件的集合,提供了一种方便的方式访问所有功能,当需要所有或大多数功能时,而不是逐个导入组件。此组件确保所有导入的组件在算法和数据上都是兼容的。
主要的代码示例和跨组件集成测试是在此组件下实现的。
主要组件
-
unic-char
:Unicode字符工具。 -
unic-normal
:Unicode规范化形式(《UAX#15》)。 -
unic-segment
:Unicode文本分割算法(《UAX#29》)。 -
unic-emoji
:Unicode表情符号(《UTS#51》)。
应用
unic-cli
:UNIC命令行工具
代码组织:合并仓库
以下是一些将组件合并到单个仓库的原因:
-
加快开发速度。实现新的Unicode/i18n组件通常依赖于其他(较低级别)组件,而这些组件反过来可能需要调整——暴露新的API、修复错误等——这些可以在更少的周期和更短的时间内开发、测试和审查。
-
实现完整性。多个组件对其他组件的依赖意味着这些组件在某种程度上需要相互一致。许多由较小的算法组成的Unicode算法假设算法的所有部分都使用相同的Unicode数据版本。违反这一假设可能导致不一致和难以捕捉的bug。在一个联合仓库中,可以在开发过程中以及跨组件(集成)测试期间达到更好的完整性。
-
按需付费。小型组件(基本crate),仅交叉依赖它们所需要的东西,允许用户仅将他们在项目中消耗的内容引入其中。
-
共享引导。大量扩展Unicode/i18n功能依赖于将源Unicode/地区数据转换为目的地编程语言的格式化格式。在一个联合仓库中,更容易维护这些引导工具,扩大覆盖范围,并使用更好的数据结构以实现更高的效率。
文档
- Unicode和Rust
- UNIC版本
- UNIC Unicode API
- UNIC API指南
- UNIC API参考(由docs.rs自动生成)
如何使用UNIC
在Cargo.toml
[dependencies]
unic = "0.9.0" # This has Unicode 10.0.0 data and algorithms
以及在main.rs
extern crate unic;
use unic::ucd::common::is_alphanumeric;
use unic::bidi::BidiInfo;
use unic::normal::StrNormalForm;
use unic::segment::{GraphemeIndices, Graphemes, WordBoundIndices, WordBounds, Words};
use unic::ucd::normal::compose;
use unic::ucd::{is_cased, Age, BidiClass, CharAge, CharBidiClass, StrBidiClass, UnicodeVersion};
fn main() {
// Age
assert_eq!(Age::of('A').unwrap().actual(), UnicodeVersion { major: 1, minor: 1, micro: 0 });
assert_eq!(Age::of('\u{A0000}'), None);
assert_eq!(
Age::of('\u{10FFFF}').unwrap().actual(),
UnicodeVersion { major: 2, minor: 0, micro: 0 }
);
if let Some(age) = '🦊'.age() {
assert_eq!(age.actual().major, 9);
assert_eq!(age.actual().minor, 0);
assert_eq!(age.actual().micro, 0);
}
// Bidi
let text = concat![
"א",
"ב",
"ג",
"a",
"b",
"c",
];
assert!(!text.has_bidi_explicit());
assert!(text.has_rtl());
assert!(text.has_ltr());
assert_eq!(text.chars().nth(0).unwrap().bidi_class(), BidiClass::RightToLeft);
assert!(!text.chars().nth(0).unwrap().is_ltr());
assert!(text.chars().nth(0).unwrap().is_rtl());
assert_eq!(text.chars().nth(3).unwrap().bidi_class(), BidiClass::LeftToRight);
assert!(text.chars().nth(3).unwrap().is_ltr());
assert!(!text.chars().nth(3).unwrap().is_rtl());
let bidi_info = BidiInfo::new(text, None);
assert_eq!(bidi_info.paragraphs.len(), 1);
let para = &bidi_info.paragraphs[0];
assert_eq!(para.level.number(), 1);
assert_eq!(para.level.is_rtl(), true);
let line = para.range.clone();
let display = bidi_info.reorder_line(para, line);
assert_eq!(
display,
concat![
"a",
"b",
"c",
"ג",
"ב",
"א",
]
);
// Case
assert_eq!(is_cased('A'), true);
assert_eq!(is_cased('א'), false);
// Normalization
assert_eq!(compose('A', '\u{030A}'), Some('Å'));
let s = "ÅΩ";
let c = s.nfc().collect::<String>();
assert_eq!(c, "ÅΩ");
// Segmentation
assert_eq!(
Graphemes::new("a\u{310}e\u{301}o\u{308}\u{332}").collect::<Vec<&str>>(),
&["a\u{310}", "e\u{301}", "o\u{308}\u{332}"]
);
assert_eq!(
Graphemes::new("a\r\nb🇺🇳🇮🇨").collect::<Vec<&str>>(),
&["a", "\r\n", "b", "🇺🇳", "🇮🇨"]
);
assert_eq!(
GraphemeIndices::new("a̐éö̲\r\n").collect::<Vec<(usize, &str)>>(),
&[(0, "a̐"), (3, "é"), (6, "ö̲"), (11, "\r\n")]
);
assert_eq!(
Words::new(
"The quick (\"brown\") fox can't jump 32.3 feet, right?",
|s: &&str| s.chars().any(is_alphanumeric),
).collect::<Vec<&str>>(),
&["The", "quick", "brown", "fox", "can't", "jump", "32.3", "feet", "right"]
);
assert_eq!(
WordBounds::new("The quick (\"brown\") fox").collect::<Vec<&str>>(),
&["The", " ", "quick", " ", "(", "\"", "brown", "\"", ")", " ", " ", "fox"]
);
assert_eq!(
WordBoundIndices::new("Brr, it's 29.3°F!").collect::<Vec<(usize, &str)>>(),
&[
(0, "Brr"),
(3, ","),
(4, " "),
(5, "it's"),
(9, " "),
(10, "29.3"),
(14, "°"),
(16, "F"),
(17, "!")
]
);
}
您可以在examples
和tests
目录下找到更多示例。[(随着UNIC的发展,还将添加更多内容)](https://github.com/open-i18n/rust-unic/blob/41cd2b01e2e51a2b8d7d32fd13b14c0c092ac014/examples/) 和 [(随着UNIC的发展,还将添加更多内容)](https://github.com/open-i18n/rust-unic/blob/41cd2b01e2e51a2b8d7d32fd13b14c0c092ac014/tests/)
许可证
根据以下任一许可证授权:
- Apache License,版本2.0([LICENSE-APACHE](https://github.com/open-i18n/rust-unic/blob/41cd2b01e2e51a2b8d7d32fd13b14c0c092ac014/LICENSE-APACHE) 或 [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0))
- MIT许可证([LICENSE-MIT](https://github.com/open-i18n/rust-unic/blob/41cd2b01e2e51a2b8d7d32fd13b14c0c092ac014/LICENSE-MIT) 或 [http://opensource.org/licenses/MIT](http://opensource.org/licenses/MIT))
由您选择。
贡献
除非您明确声明,否则任何有意提交给作品以供包含的贡献,根据Apache-2.0许可证的定义,应以上述双重许可,不附加任何额外条款或条件。
行为准则
UNIC项目遵循Rust行为准则
。您可以在[CODE_OF_CONDUCT.md](https://github.com/open-i18n/rust-unic/blob/41cd2b01e2e51a2b8d7d32fd13b14c0c092ac014/CODE_OF_CONDUCT.md)或在线[https://www.rust-lang.net.cn/conduct.html](https://www.rust-lang.net.cn/conduct.html)找到其副本。
lib.rs
:
UNIC — UCD — 核心
是unic
的一个组件:Rust的Unicode和国际化和Crate。
核心crate指示Unicode字符数据库的版本。