[#49148] Ruby 1.9で、バイト長の部分文字列を作りたくて悩んでます — Daisuke Yokotsuka <yokots_d@...3-net.ne.jp>

はじめまして。横塚と申します。

16 messages 2013/01/29

[ruby-list:49163] Re: Ruby 1.9で、バイト長の部分文字列を作りたくて悩んでます

From: Takeshi Iogawa <alpha@246.ne.jp>
Date: 2013-01-30 10:30:41 UTC
List: ruby-list #49163
いおがわです。

とみたさんご指摘の通り、force_encoding は破壊的動作ですから、元文字列を
後で再利用するならdup等が必要ですね。失念しておりました。すみません。

で、そうすると下記引用のモンキーパッチももう少しシンプルになりますね。
いっそ、外部ファイルにしてrequire しちゃうと

-- requireするString#byteslice の定義ファイル
#! ruby
# -*- coding: US-ASCII -*-

# Define String#byteslice.(Back port from 1.9.3)
# When Ruby is upgraded to 1.9.3 or later, this will be able to delete.

unless String.instance_methods.find {|m| m == :byteslice}
  $stderr.print "Ruby Version #{RUBY_VERSION}: "\
                "Define String#byteslice.(Back port from 1.9.3)\n"
 class String
   def byteslice *a, &b
     enc = self.encoding
     self.dup.force_encoding("BINARY").slice(*a, &b).force_encoding(enc)
   end
 end
end
# -- End of define String#byteslice
__END__

ついでに『るりま』のString#byteslice のページの使用例を参照してユニット
テストもしてみました。

-- ユニットテスト(上記ファイルをbslice.rb として)
#! ruby
# -*- coding: UTF-8 -*-

require 'test/unit'
require './bslice.rb'

class Test_Byteslice < Test::Unit::TestCase
  def test_nth
    assert_equal "e", "hello".byteslice(1)
    assert_equal "o", "hello".byteslice(-1)
    assert_equal "\xE3", "\u3042".byteslice(0)
    assert_equal "\x81", "\u3042".byteslice(1)
  end

  def test_nth_len
    assert_equal "el", "hello".byteslice(1, 2)
    assert_equal "\u3042", "\u3042\u3044\u3046".byteslice(0, 3)
  end

  def test_range
    assert_equal "el", "hello".byteslice(1..2)
    assert_equal "\u3042", "\x03\u3042\xff".byteslice(1..3)
  end
end
__END__

1.9.2p136-mswin32、1.9.3p374-mswin32(野良ビルド)、2.0.0RC1-mswin32(野良
ビルド)でテスト通りました。複雑な使い方しなきゃたぶん使えるんじゃないか
と。。。
# 本当は元文字列を破壊してないか? 等もっとちゃんといろいろテストしなきゃ
# いけないですけど

1.8系を考慮するともっと複雑になりますが、ちょっと書きはじめて、どうせ1.8
系で動かそうとすると他のところでボコボコエラーが出ることに気づいて止めま
した(苦笑)。

・・・などと書いていたらご本人から投稿がありましたので、この辺で失礼いた
します



(2013/01/29 23:29), Takeshi Iogawa wrote:
> いおがわです。
> 
>> で、encodingの問題も、実行性能も、無事に解決できました。
> 
> お役に立てたようでよかったです。
> 
> 
>> 他人が見て、何をやっているのか一目で理解できるコードかと言うと、ちょっ
> と悩ましい。
> 
> Ruby は既存のクラスの挙動を変更できますから(組み込みのコアクラスでも!!)
> 以下のようなコードをスクリプトの最初のほうに記述しておく(もしくは別ファ
> イルにしてrequire)って方法もあるにはあります。。。
> 
> # Stringクラスを拡張し、bytesliceメソッドを定義する
> unless String.instance_methods.find {|m| m == :byteslice}
>    class String
>      def byteslice *a,&b
>       enc = self.encoding
>       r = self.force_encoding("BINARY").slice(*a,&b).force_encoding(enc)
>       self.force_encoding enc
>       r
>      end
>    end
> end
> # -- ここまで
> 
> 
> そーすると、1.9.2 でも(1.9.1でも)
> target = data.byteslice 0, 72
> 
> って書けますよ。(やってることは前回と変わらないですがforce_encoding が隠
> 蔽されるので見通しはよくなるかと。。。実行性能的には若干不利かな)
> # 将来的に1.9.3以上にバージョン上げたときは標準のbytesliceが使われるよう
> # になります。
> 
> (注) 1.9.3 のbyteslice の引数・戻り値をちゃんと調べ切れていないで上記
> コード書いてますので、1.9.3に上げた途端にピギャー、となる可能性があります。
> Try するなら1.9.3のbyteslice の仕様をきちんと確認ください
> 
> いわゆる『モンキーパッチ』という奴なのでむやみやたらと手を出さないほうが
> よいと思います。
> あくまで、「こんな手もある」というコトで。。。
> 

--
 Takeshi Iogawa

In This Thread

Prev Next