Ruby 如何在不影响日语字符集的情况下过滤掉不可见字符?

Ruby 如何在不影响日语字符集的情况下过滤掉不可见字符?,ruby,character,cjk,Ruby,Character,Cjk,我注意到我的一些输入是U+2028。我不知道这是什么,但考虑到UTF-8和英文/日文字符,我如何防止这种情况发生?字符U+2028是空格字符之一 在Unicode空间中,仅选择日文字符恐怕是相当棘手的,因为CJK字符分布在如此多的平面上,即使Ruby在Regexp(如\p{Hiragana})中支持广泛的Unicode类别格式。但是,如果您只对日语和ASCII感兴趣,那么NKF库很有用。以下是一个例子: require 'nkf' orig = "b2αÇ()あ相〜\u2028\u3000_━●

我注意到我的一些输入是U+2028。我不知道这是什么,但考虑到UTF-8和英文/日文字符,我如何防止这种情况发生?

字符U+2028是空格字符之一

在Unicode空间中,仅选择日文字符恐怕是相当棘手的,因为CJK字符分布在如此多的平面上,即使Ruby在Regexp(如\p{Hiragana})中支持广泛的Unicode类别格式。但是,如果您只对日语和ASCII感兴趣,那么NKF库很有用。以下是一个例子:

require 'nkf'
orig = "b2αÇ()あ相〜\u2028\u3000_━●★】"
p orig
p NKF.nkf('-w -E', NKF.nkf('-e', orig))

# =>
# "b2αÇ()あ相〜\u2028 _━●★】"
# "b2α()あ相〜 _━●★】"
如您所见,unicode字符U+2028被过滤掉,而希腊字符α被保留,因为它包含在日文JIS-X-0208代码中。请注意,重音字母如Ç会被过滤掉,因为它们不包括在内。所谓的hankaku假名集将从中过滤出来,并在该公式中编辑为zenkaku假名。JIS-X-0212字符集也不受支持

特定情况下的解决方案。

字符U+2028是空格字符之一

在Unicode空间中,仅选择日文字符恐怕是相当棘手的,因为CJK字符分布在如此多的平面上,即使Ruby在Regexp(如\p{Hiragana})中支持广泛的Unicode类别格式。但是,如果您只对日语和ASCII感兴趣,那么NKF库很有用。以下是一个例子:

require 'nkf'
orig = "b2αÇ()あ相〜\u2028\u3000_━●★】"
p orig
p NKF.nkf('-w -E', NKF.nkf('-e', orig))

# =>
# "b2αÇ()あ相〜\u2028 _━●★】"
# "b2α()あ相〜 _━●★】"
如您所见,unicode字符U+2028被过滤掉,而希腊字符α被保留,因为它包含在日文JIS-X-0208代码中。请注意,重音字母如Ç会被过滤掉,因为它们不包括在内。所谓的hankaku假名集将从中过滤出来,并在该公式中编辑为zenkaku假名。JIS-X-0212字符集也不受支持


针对您的具体情况的解决方案。

除了NKF库的解决方案之外,我还为Ruby 2提出了其他解决方案。下面所描述的比较在某种程度上是有趣的,因为它们彼此略有不同。这是一个重大的修改,所以我把它作为一个单独的答案发布。在这篇文章的结尾,我也描述了这方面的背景。 我假设原始输入是UTF-8编码,除了第一部分,如果不是,首先将其转换为UTF-8以应用任何示例

过滤非法字符的解决方案 非法是指未包含在为字符串实例定义的编码中的字符代码。 在Ruby 2中,这样的字符串通常应该具有编码ASCII-8BIT。但是,有些可能错误地使用UTF-8编码

如果它的编码为ASCII-8BIT,但如果您希望获得合法的UTF-8字符串

过滤非日语字符的解决方案 以下所有方法都用于过滤非日语字符。 基本上,非日文字符是指不包括在一个或多个日文字符集的传统标准中的字符。 有关非日语字符定义的详细背景,请参见下一节

这里的策略是将原始字符串的编码转换为称为ISO-2022-JP或EUC-JP的日本JIS编码;基本上是JIS-X-0208,并转换回UTF-8

使用字符串编码 Ruby-2内置的Stringencode完成了确切的工作

orig = "b2◇〒α()あ相〜\u3000_8D━●★】$£€Ç♡㌔③\u2028ハンカク"
print "Orig:"; p orig
print "Enc: "; p orig.encode('ISO-2022-JP', undef: :replace, replace: '').encode('UTF-8')
特征

尖阁岛:保存完好 hankaku kana:过滤掉 欧元区标志:过滤掉 拉丁语1:过滤掉 JISX0212:过滤掉 CJK兼容性:过滤掉 带圆圈的数字:过滤掉 Unicode:过滤掉 使用NKF库 NKF库是Ruby官方发行版附带的标准库之一。 图书馆是传统的,已经使用了几十年;参见,NKF代表。 它与Ruby编码的工作非常相似,但略有不同

orig = "b2◇〒α()あ相〜\u3000_8D━●★】$£€Ç♡㌔③\u2028ハンカク"
require 'nkf'
print "NKF: "; p NKF.nkf('-w -E', NKF.nkf('-e', orig))
特征

尖阁岛:保存完好 hankaku假名:全宽转换为尖阁假名 欧元区标志:过滤掉 拉丁语1:过滤掉 JISX0212:过滤掉 CJK兼容性:保留 圆圈数字:保留 Unicode:过滤掉 使用iconv Gem RubyGem不再和标准Ruby一起提供了,我想它以前是这样的,直到Ruby2.1或者其他版本。但您可以使用gem命令(如gem install iconv)轻松安装它

它可以处理ISO-2022-JP-2,与上面提到的两种方法不同,这两种方法可能很方便,注意,编码ISO-2022-JP-2实际上是在Ruby编码中定义的,但是默认情况下,Ruby中不定义它的转换。安装后,下面是一个示例

orig = "b2◇〒α()あ相〜\u3000_8D━●★】$£€Ç♡㌔③\u2028ハンカク"
require 'iconv'
output = ''
Iconv.open('iso-2022-jp-2', 'utf-8') do |cd|
  cd.discard_ilseq=true
  output = cd.iconv orig << cd.iconv(nil)
end
s2 = Iconv.conv('utf-8', 'iso-2022-jp-2', output)
print "Icon:"; p s2
为了方便起见,这里提供了上面的所有代码片段-下载或git克隆并运行它

出身背景 什么是无效字符?例如,如问题中所述,字符U+2028是合法的UTF-8字符。那么,第 没有一般理由过滤掉这些字符,尽管某些情况可能需要这样做

什么是英语字符?大写字母和小写字母总共52个,可能是。那么,美元符号$怎么样?磅符号?欧元符号€?美元符号是ASCII字符,而英镑和欧元符号都不是。英镑符号包含在传统的拉丁语-1 ISO-8859-1字符集中,而欧元符号则没有。因此,什么是英语字符不是一个简单的问题。 您可以定义ASCII或Latin-1,或者定义中唯一的英文字符集,但这有点随意

什么是日本人?好的,平假名和片假名是日本人独有的。汉字怎么样?你们接受日本不使用的简体汉字作为汉字吗?符号呢?好的,一些符号,比如.U+3002;和「U+300c;」是日文文本中的基本标点符号。但是,有什么理由认为▼ , 几十年来,它在日语计算机用户中被广泛使用,是日语特有的吗?也许不是。它们只是可以在世界任何地方使用的符号。更糟糕的是,它不是一个明确的符号;例如,尽管可以公平地说〒是日语特有的,但它不像句号那样是一个基本的标点符号,而只是一个在日本相当普遍使用的符号。如果这个非常相似的符号在世界其他地方被使用,我不会感到惊讶,因为我不知道

与英语字符的ASCII和拉丁语-1参数类似,您可以将JIS X 0208字符集中包含的传统使用的字符定义为有效的日语字符。同样,它不可避免地是任意的。例如,其中包含英镑符号,而不包含欧元符号。菱形标记◇ 包括在内,而心脏标记♡ 不是。或者,那些所谓的zenkaku,也就是全宽字符,它们只是字母表和ASCII 0到9的阿拉伯数字的复制品呢

毕竟,Unicode是世界上使用的统一的字符集,不管使用哪种语言——好吧,至少在理想情况下是这样,尽管你可能会认为真正的Unicode并不理想。从这个意义上说,没有明确的答案可以过滤掉非英语或非日语字符。因此,关于过滤的原始问题out U+2028是来自某些特定情况的任意要求之一,尽管事实上它很可能是一个受欢迎的要求,因此我给出了答案

你唯一能做的确定的事情是过滤掉所选字符编码的非法字符,如UTF-8,如本答案的第一部分所述。其余部分实际上取决于每个人在特定情况下的需要

日语字符集的背景 日语字符集传统上是在JIS标准的官方术语中定义的。具体来说,不太流行,通常被随意称为補助漢字 这两个标准是非强制性的,它们有其具体的细节,如1983年和1990年。不幸的是,在实践中,NEC、微软和苹果采用了它们自己的变体,称为广义Shift_JIS或SJIS,尽管它们都有自己的变体。由于它们的OSs的流行,它们过去和现在在某种程度上都在日本得到了更广泛的应用在UTF-8被广泛接受的时代之前,一个严格的官方标准

请注意,它们至少都接受ASCII。因此,在几乎任何情况下使用ASCII都是安全的,除了80年代早期或之前的一些情况

Unicode是非常包容的,包含了几乎所有在这些字符码集中定义的字符。这意味着任何一个字符都曾经引发过激烈的争论,你现在是否应该在任何Unicode编码中使用,或者你是否可以合法使用,我指的是就字符而言合法就编码而言


我认为这种混乱的实际情况导致了上面所示的结果略有不同,这取决于您使用的方法。根据您的需要,选择您最喜欢的方法!

除了使用NKF库的解决方案之外,我还为Ruby 2提出了其他解决方案。下面所述的比较以w是的,很有趣,因为它们彼此略有不同。这是一个重大修订,所以我将其作为一个单独的答案发布。我还在本文末尾描述了这方面的背景。 我假设原始输入是UTF-8编码,除了第一部分,如果不是,首先将其转换为UTF-8以应用任何示例

过滤非法字符的解决方案 非法是指未包含在为字符串实例定义的编码中的字符代码。 在Ruby 2中,这样的字符串通常应该具有编码ASCII-8BIT 可能错误地使用UTF-8编码

如果它的编码为ASCII-8BIT,但如果您希望获得合法的UTF-8字符串

过滤非日语字符的解决方案 以下所有方法都用于过滤非日语字符。 基本上,非日文字符是指不包括在一个或多个日文字符集的传统标准中的字符。 有关非日语字符定义的详细背景,请参见下一节

这里的策略是将原始字符串的编码转换为称为ISO-2022-JP或EUC-JP的日本JIS编码;基本上是JIS-X-0208,并转换回UTF-8

使用字符串编码 Ruby-2内置的Stringencode完成了确切的工作

orig = "b2◇〒α()あ相〜\u3000_8D━●★】$£€Ç♡㌔③\u2028ハンカク"
print "Orig:"; p orig
print "Enc: "; p orig.encode('ISO-2022-JP', undef: :replace, replace: '').encode('UTF-8')
特征

尖阁岛:保存完好 hankaku kana:过滤掉 欧元区标志:过滤掉 拉丁语1:过滤掉 JISX0212:过滤掉 CJK兼容性:过滤掉 带圆圈的数字:过滤掉 Unicode:过滤掉 使用NKF库 NKF库是Ruby官方发行版附带的标准库之一。 图书馆是传统的,已经使用了几十年;参见,NKF代表。 它与Ruby编码的工作非常相似,但略有不同

orig = "b2◇〒α()あ相〜\u3000_8D━●★】$£€Ç♡㌔③\u2028ハンカク"
require 'nkf'
print "NKF: "; p NKF.nkf('-w -E', NKF.nkf('-e', orig))
特征

尖阁岛:保存完好 hankaku假名:全宽转换为尖阁假名 欧元区标志:过滤掉 拉丁语1:过滤掉 JISX0212:过滤掉 CJK兼容性:保留 圆圈数字:保留 Unicode:过滤掉 使用iconv Gem RubyGem不再和标准Ruby一起提供了,我想它以前是这样的,直到Ruby2.1或者其他版本。但您可以使用gem命令(如gem install iconv)轻松安装它

它可以处理ISO-2022-JP-2,与上面提到的两种方法不同,这两种方法可能很方便,注意,编码ISO-2022-JP-2实际上是在Ruby编码中定义的,但是默认情况下,Ruby中不定义它的转换。安装后,下面是一个示例

orig = "b2◇〒α()あ相〜\u3000_8D━●★】$£€Ç♡㌔③\u2028ハンカク"
require 'iconv'
output = ''
Iconv.open('iso-2022-jp-2', 'utf-8') do |cd|
  cd.discard_ilseq=true
  output = cd.iconv orig << cd.iconv(nil)
end
s2 = Iconv.conv('utf-8', 'iso-2022-jp-2', output)
print "Icon:"; p s2
为了方便起见,这里提供了上面的所有代码片段-下载或git克隆并运行它

出身背景 什么是无效字符?例如,如问题中所述,字符U+2028是合法的UTF-8字符。因此,没有一般理由过滤掉这些字符,尽管某些情况可能需要这样做

什么是英语字符?大写字母和小写字母总共52个,可能是。那么,美元符号$怎么样?磅符号?欧元符号€?美元符号是ASCII字符,而英镑和欧元符号都不是。英镑符号包含在传统的拉丁语-1 ISO-8859-1字符集中,而欧元符号则没有。因此,什么是英语字符不是一个简单的问题。 您可以定义ASCII或Latin-1,或者定义中唯一的英文字符集,但这有点随意

什么是日本人?好的,平假名和片假名是日本人独有的。汉字怎么样?你们接受日本不使用的简体汉字作为汉字吗?符号呢?好的,一些符号,比如.U+3002;和「U+300c;」是日文文本中的基本标点符号。但是,有什么理由认为▼ , 几十年来,它在日语计算机用户中被广泛使用,是日语特有的吗?也许不是。它们只是可以在世界任何地方使用的符号。更糟糕的是,它不是一个明确的符号;例如,尽管可以公平地说〒是日语特有的,但它不像句号那样是一个基本的标点符号,而只是一个在日本相当普遍使用的符号。如果这个非常相似的符号在世界其他地方被使用,我不会感到惊讶,因为我不知道

与英语字符的ASCII和拉丁语-1参数类似,您可以将JIS X 0208字符集中包含的传统使用的字符定义为有效的日语字符。同样,它不可避免地是任意的。例如,其中包含英镑符号,而不包含欧元符号。菱形标记◇ 包括在内,而心脏标记♡ 不是。或者,那些所谓的zenkaku,也就是全宽字符,它们只是字母表和ASCII 0到9的阿拉伯数字的复制品呢

毕竟,Unicode是世界上使用的统一的字符集,不管使用哪种语言——好吧,至少在理想情况下是这样,尽管你可能会认为真正的Unicode并不理想。从这个意义上说,没有明确的答案可以过滤掉非英语或非日语字符。因此,关于过滤的原始问题out U+2028是来自某些特定情况的任意要求之一,尽管事实上它很可能是一个受欢迎的要求,因此我给出了答案

您唯一能做的确定的事情是过滤掉所选字符编码的非法字符,如UTF-8,如本手册第一节所述 回答。其余的部分实际上取决于每个人在特定情况下的需要

日语字符集的背景 日语字符集传统上是在JIS标准的官方术语中定义的。具体来说,通常被随意称为補助漢字 这两个标准都是非强制性的,它们有其具体的细节,如1983年和1990年。不幸的是,在实践中,NEC、微软和苹果采用了他们自己的变体,称为广义Shift_JIS或SJIS,尽管它们都有自己的变体。由于他们的OSs的流行,他们过去是,在某种程度上仍然是!与UTF-8被广泛接受的时代之前严格的官方版本相比,它在日本的实际应用更加广泛

请注意,它们至少都接受ASCII码。因此,除了80年代早期或之前的一些情况外,在几乎任何情况下使用ASCII都是安全的

Unicode是非常包容的,包含了几乎所有这些字符代码集中定义的字符。这意味着任何曾经引起激烈争论的字符,无论您现在是否应该在任何Unicode编码中使用,或者您是否可以合法使用,我的意思是,就字符编码而言,合法

我认为这种混乱的实际情况导致了如上所示的结果,这些结果彼此略有不同,这取决于您使用的方法。根据您的需要选择您最喜欢的