ruby - 在 Ruby 中随机生成一个有效的 unicode 字符

标签 ruby random unicode

如何在 Ruby 中生成由给定数量的 unicode 字符组成的随机 unicode 字符串?

以下有效,但包括控制字符(0x00-0x1F 等),例如:

20.times.map{ Random.rand(0xFFFF).chr('UTF-8')}.join

最佳答案

该范围内的许多字符不可打印(正如您所指出的),或者它们是替代字符、自定义字符或其他无效字符。最好的方法(我能想到的)是生成一个字符序列,测试每个字符以确保它有效且可打印,然后取前 20 个字符。

一些注意事项。在这种情况下,我们想要执行 rand(0x10000),而不是 rand(0xFFFF),因为 Random#randKernel#rand 将返回一个小于其参数的数字,并且您想在采样中包含 U+FFFF。我们还应该给自己一些灵 active 来处理一字节、两字节、三字节或四字节 UTF-8。

让我们从一个基本的序列生成器开始,称为 Enumerator在 ruby 中。该对象一次生成一个值,可以表示有限或无限序列。在这种情况下,我们想要枚举无限序列的随机三字节 UTF-8 字符,同时跳过无效字符。

random_utf8 = Enumerator.new do |yielder|
  loop do
    yielder << rand(0x10000).chr('UTF-8')
  rescue RangeError
  end
end

您可以使用 #next 从 Enumerator 中提取值以查看其运行情况:

irb(main):007:0> random_utf8.next
=> "\u9FEB"
irb(main):008:0> random_utf8.next
=> "槇"
irb(main):009:0> random_utf8.next
=> "엛"

(您会注意到其中一个没有“呈现”,因为它不是可打印字符。这说明了为什么我们需要在选择其中的 20 个值之前过滤值。)

现在我们可以从这个序列中取出字符并检查每个字符是否可打印。唯一的问题是我们想懒惰地这样做,以避免在继续链中的下一步之前检查无限序列中的每个字符(这是不可能的)。最后,我们将获取前 20 个可打印字符并将它们连接成一个字符串。

random_utf8
  .lazy
  .grep(/[[:print:]]/) # or [[:alpha:]] or \p{L} or whatever test you want here
  .first(20)
  .join # => "醸긍ᅋꝇ꼏捁㨃농鳹䝛ㆅ⇂擒璝缀챼砶"

现在让我们把它抽象成一个方法,这样我们就可以参数化一些东西。 Ruby 为我们提供了一种从方法返回枚举器的简洁方法,该方法通过返回 Object#enum_for(又名 Object#to_enum)和方法符号以及传递的任何其他参数来产生值到功能。

def random_utf8(mb=3)
  return enum_for(__callee__, mb) unless block_given?

  # determine the maximum codepoint based on the number of UTF-8 bytes
  max = [0x80, 0x800, 0x10000, 0x110000][mb.pred]

  loop do
    yield rand(max).chr('UTF-8') # note the `yield` here
  rescue RangeError
  end
end

我们可以像使用上面的 Enumerator 一样使用这个方法,可以选择传入所需的 UTF-8 字节数。

这种方法还让我们可以选择使用 block 调用我们的方法,而不是从 block 中链接操作:

random_utf8(2) do |char|
  next unless char.match?(/[[:print:]]/)

  puts "Got >#{char}<!"

  break # don't loop infinitely
end

诚然,这在这种特殊情况下并不是很有用。

关于此解决方案实现的附加注意事项:您可以轻松地将可打印检查移到方法主体中,或将 RangeError 异常处理移出方法主体。您还可以让该方法默认返回惰性枚举器。围绕您的应用需求设计方法完全取决于您。

def lazy_printable_random_utf8(mb=3)
  return enum_for(__callee__, mb).lazy unless block_given?

  # determine the maximum codepoint based on the number of UTF-8 bytes
  max = [0x80, 0x800, 0x10000, 0x110000][mb.pred]

  loop do
    char = rand(max).chr('UTF-8')

    yield char if char.match?(/[[:print:]]/)
  rescue RangeError
  end
end

关于ruby - 在 Ruby 中随机生成一个有效的 unicode 字符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52654509/

相关文章:

ruby - 这个 ruby 代码有什么问题?语法错误,意外的 tIDENTIFIER,期待 keyword_end

mysql - Grails 域类。随机场

javascript - 获取当前现代 Javascript 中字符串的最后一个字符,允许使用代理对(两个代码单元)的星体字符,例如表情符号

java - 如何建立编码字符的代码点?

r - 带有 knit 和 Rmarkdown 的 Unicode

ruby - 如果记录重复且总和值为 :val,如何删除记录

arrays - 比较数组元素(包括重复项)

ruby-on-rails - url_paths - 理解它们的问题

java - 随机数测试(直方图)

c++ - 如何创建具有随机数据成员值的对象? C++