目次


Ruby によるテキスト処理

Ruby の威力を利用して効率的にテキストを処理する

Comments

Perl や Python と同じ目線で見ると、Ruby には強力なテキスト処理言語となるに値する優れた機能が備わっています。この記事では Ruby のテキスト・データ処理機能について概説した後、Ruby を利用して多様なテキスト・データ (それが CSV データであるか XML データであるかに関わらず) を効率的に処理する方法を説明します。

Ruby の文字列

Ruby の文字列 (String) は、テキスト・データを保持、比較、操作するのに有効な手段です。Ruby では、String::new を呼び出すか、単にリテラル値を割り当てるだけで String クラスをインスタンス化することができます。

文字列に値を割り当てる際には、単一引用符 (') または二重引用符 (") の対を使って値を囲むことができます。単一引用符と二重引用符のどちらを使用するかによって、文字列の指定方法にいくつかの違いが出てきます。二重引用符では、バックスラッシュ (\) で始まるエスケープ・シーケンスを使用できるだけでなく、#{} 演算子を使って文字列に含まれる式を評価することもできます。一方、単一引用符で囲んだ文字列は、単純なそのままのリテラルとなります。

リスト 1 に、一例を示します。

リスト 1. Ruby 文字列を扱う: 文字列の定義
message = 'Heal the World…'

puts message

message1 = "Take home Rs #{100*3/2} "

puts message1

Output :

# ./string1.rb

# Heal the World…

# Take home Rs 150

上記の最初の文字列は、単一引用符の対で定義されています。2 番目の文字列では二重引用符の対を使用しているため、この文字列は #{} 内の式が評価されてから表示されます。

文字列を定義するには、もう 1 つ便利な方法があります。この方法は通常、複数の行からなる文字列を定義する場合に用いられます。

ここからは、対話型 Ruby コンソール、irb>> を使用して説明します。このコンソールは、Ruby のインストール時にインストールされているはずです。インストールされていない場合は、是非とも irb Ruby gem を入手して、インストールしてください。Ruby コンソールは、Ruby とそのモジュールを学ぶためのツールとして大いに役立ちます。インストール後は、irb>> コマンドを使って実行することができます。

リスト 2. Ruby 文字列を扱う: 複数行にわたる文字列の定義
irb>> str = >>EOF

irb>> "hello world

irb>> "how do you feel?

irb>> "how r u ?

irb>> EOF

"hello, world\nhow do you feel?\nhow r u?\n"

irb>> puts str

hello, world
how do you feel?
how r u?

リスト 2 では、\n (改行) 文字を含め、>>EOF から EOF までのすべての文字が文字列に含まれるとみなされます。

Ruby の String クラスには、保存されたデータの操作および処理に使用できるメソッドが充実して揃っています。以降のリスト 3リスト 4リスト 5 で、そのうちのいくつかを抜粋して説明します。

リスト 3. Ruby 文字列を扱う: 文字列の連結
irb>> str = "The world for a horse"    # String initialized with a value

The world for a horse

irb>> str*2                     # Multiplying with an integer returns a 
                                         # new string containing that many times
                                         # of the old string.

The world for a horseThe world for a horse

irb>> str + " Who said it ? "      # Concatenation of strings using the '+' operator

The world for a horse Who said it ?

irb>> str<<" is it? "	  # Concatenation using the '<<' operator

The world for a horse is it?

サブストリングを抽出し、文字列の一部を操作する

リスト 4. Ruby 文字列を扱う: 抽出および操作
irb>> str[0]	# The '[]' operator can be used to extract substrings, just 
                        # like accessing entries in an array.
                        # The index starts from 0.
84			# A single index returns the ascii value
                        # of the character at that position

irb>> str[0,5]	# a range can be specified as a pair. The first is the starting 
                        # index , second is the length of the substring from the
                        # starting index.

The w

irb>> str[16,5]="Ferrari"   # The same '[]' operator can be used
                                  # to replace substrings in a string
                                  # by using the assignment like '[]='
irb>>str

The world for a Ferrari

Irb>> str[10..22]		# The range can also be specified using [x1..x2] 

for a Ferrari

irb>> str[" Ferrari"]=" horse"	# A substring can be specified to be replaced by a new
                               # string. Ruby strings are intelligent enough to adjust the
                               # size of the string to make up for the replacement string.

irb>> s

The world for a horse

irb>> s.split	       # Split, splits the string based on the given delimiter
                               # default is a whitespace, returning an array of strings.

["The", "world", "for", "a", "horse"]

irb>> s.each(' ') { |str| p str.chomp(' ') }

                               # each , is a way of block processing the
			       # string splitting it on a record separator
			       # Here, I use chomp() to cut off the trailing space

"The"
"world"
"for"
"a"
"horse"

Ruby の String クラスには上記の他にも、例えば大文字/小文字の置換、文字列長の取得、レコード・セパレーターの除去、文字列のスキャン、文字列の暗号化と暗号化解除などの操作を行う数多くの実用的なメソッドがあります。さらに、文字列を変更不可能にする freeze という便利なメソッドもあります。このメソッドを文字列 str で呼び出すと (str.freeze)、str は変更できなくなります。

Ruby にはデストラクターと呼ばれるメソッドもあります。これは、文字列を恒久的に変更する、感嘆符 (!) で終わるメソッドのことです。標準メソッド (末尾に感嘆符がないメソッド) を文字列で呼び出した場合、メソッドが変更して返すのは文字列のコピーですが、感嘆符で終わるメソッドは文字列自体を変更します。

リスト 5. Ruby 文字列を扱う: 文字列の恒久的変更
irb>> str = "hello, world"

hello, world

irb>> str.upcase

HELLO, WORLD

irb>>str		       # str, remains as is.

Hello, world

irb>> str.upcase!	       # here, str gets modified by the '!' at the end of 
                               # upcase.
HELLO, WORLD

irb>> str

HELLO, WORLD

リスト 5 では、str 内の文字列は upcase! メソッドによって変更されますが、単なる upcase メソッドだと、大文字に置換された文字列のコピーが返されます。これらの ! メソッドは、場合によっては非常に重宝します。

Ruby の String は極めて有力です。String オブジェクトにいったんデータを取り込めば、あとは豊富なメソッドを意のままに使って、至って簡単かつ効率的にデータを処理することができます。

CSV ファイルの操作

CSV ファイルは表形式のデータを表現する極めて一般的な方法で、スプレッドシートからエクスポートしたデータ (連絡先とその詳細情報のリストなど) の形式として最もよく使用されています。

Ruby には、このようなファイルの操作と処理に力を発揮するライブラリーがあります。それが、CSV ファイルを扱う Ruby モジュール csv です。このモジュールには、CSV のようなファイルの作成、読み取り、構文解析を行うメソッドが用意されています。

リスト 6 の例で、CSV ファイルを作成した後に、Ruby の csv モジュールを使ってファイルを構文解析する方法を説明します。

リスト 6. CSV ファイルを扱う: CSV ファイルの作成と構文解析
require 'csv'

writer = CSV.open('mycsvfile.csv','w')

begin

	print "Enter Contact Name: "

	name = STDIN.gets.chomp

	print "Enter Contact No: "

	num = STDIN.gets.chomp

	s = name+" "+num

	row1 = s.split

	writer << row1

	print "Do you want to add more ? (y/n): "

	ans = STDIN.gets.chomp

end while ans != "n"

writer.close

file = File.new('mycsvfile.csv')

lines = file.readlines

parsed = CSV.parse(lines.to_s)

p parsed

puts ""

puts "Details of Contacts stored are as follows..."

puts ""

puts "-------------------------------"

puts "Contact Name | Contact No"

puts "-------------------------------"

puts ""

CSV.open('mycsvfile.csv','r') do |row|

	puts row[0] + " | " + row[1]	

	puts ""
end

リスト 7 に、上記による出力を記載します。

リスト 7. CSV ファイルを扱う: CSV ファイルの作成および構文解析による出力
Enter Contact Name: Santhosh

Enter Contact No: 989898

Do you want to add more ? (y/n): y

Enter Contact Name: Sandy

Enter Contact No: 98988

Do you want to add more ? (y/n): n

Details of Contacts stored are as follows...

---------------------------------
Contact Name | Contact No
---------------------------------

Santhosh | 989898

Sandy | 98988

この例の内容を以下に概略します。

まず、csv モジュールを組み込みます (require 'csv')。

mycsvfile.csv という名前の新規 CSV ファイルを作成するために、CSV.open() を呼び出してファイルを開きます。これによって、ライター・オブジェクトが返されます。

この例で作成するのは、個人の名前とその電話番号からなる単純な連絡先リストを保持する CSV ファイルです。ループでは、ユーザーが連絡先の名前と電話番号を入力するように求められます。名前と電話番号は 1 つの文字列に連結された後、2 つに分割されて文字列の配列になります。この配列がライター・オブジェクトに渡されて、CSV ファイルに書き込まれます。その結果、CSV 値の対がそれぞれ 1 つの行としてファイルに格納されます。

ループを抜けると、すべて完了です。この時点でライターをクローズし、ファイル内のデータを保存します。

次のステップは、作成された CSV ファイルの構文解析です。

ファイルを開いて構文解析する 1 つの方法は、この新しい CSV ファイルの名前を使用した File オブジェクトを新規に作成することです。

readlines メソッドを呼び出して、ファイル内のすべての行を lines という名前の配列に読み込みます。

lines.to_s を呼び出して lines 配列を String オブジェクトに変換し、この文字列を CSV.parse メソッドに渡します。すると、CSV データが構文解析されて、その内容が配列の配列として返されます。

これに続いて、ファイルを開いて構文解析するもう 1 つの方法も示されています。今度は読み取りモードで CSV.open を呼び出してファイルを開きます。すると行の配列が返されるので、何らかのフォーマット設定を使用して各行を出力し、連絡先の詳細を表示します。この場合、それぞれの行はファイル内での 1 行に対応します。

このように、Ruby は CSV ファイルおよびデータを扱うのにも強力なモジュールを提供します。

XML ファイルを扱う

Ruby には、XML ファイルを扱うための強力な組み込みライブラリー REXML があります。XML 文書の読み取りと構文解析には、このライブラリーを使用することができます。

Ruby と REXML を使って、以下に記載するサンプル XML ファイルを構文解析してみてください。

これは、オンライン・ショッピング・モールの典型的なショッピング・カートの内容を記載する単純な XML ファイルで、要素には以下のものがあります。

  • cart – ルート要素
  • user – 買い物をしているユーザー
  • item – ユーザーが自分のカートに追加した商品
  • id、price、quantity – item のサブ要素

リスト 8 に、この XML の構造を示します。

リスト 8. XML ファイルを扱う: サンプル XML ファイル
<cart id="userid">

<item code="item-id">

	<price>

		<price/unit>

	</price>

	<qty>

		<number-of-units>

	</qty>

</item>

</cart>

このサンプル XML ファイルは、「ダウンロード」セクションから入手することができます。ここで、この XML ファイルをロードして、REXML を使用したツリーで構文解析します。

リスト 9. XML ファイルを扱う: XML ファイルの構文解析
require 'rexml/document'

include REXML

file = File.new('shoppingcart.xml')

doc = Document.new(file)

root = doc.root

puts ""

puts "Hello, #{root.attributes['id']}, Find below the bill generated for your purchase..."

puts ""

sumtotal = 0

puts "-----------------------------------------------------------------------"

puts "Item\t\tQuantity\t\tPrice/unit\t\tTotal"

puts "-----------------------------------------------------------------------"

root.each_element('//item') { |item| 

code = item.attributes['code']

qty = item.elements["qty"].text.split(' ')

price = item.elements["price"].text.split(' ')

total = item.elements["price"].text.to_i * item.elements["qty"].text.to_i

puts "#{code}\t\t  #{qty}\t\t          #{price}\t\t         #{total}"

puts ""

sumtotal += total

}

puts "-----------------------------------------------------------------------"

puts "\t\t\t\t\t\t     Sum total : " + sumtotal.to_s

puts "-----------------------------------------------------------------------"

上記による出力は、リスト 10 のとおりです。

リスト 10. XML ファイルを扱う: XML ファイルの構文解析による出力
Hello, santhosh, Find below the bill generated for your purchase...

-------------------------------------------------------------------------
Item            Quantity                Price/unit              Total
-------------------------------------------------------------------------
CS001             2                          100                      200

CS002             5                          200                     1000

CS003             3                          500                     1500

CS004             5                          150                      750

-------------------------------------------------------------------------
                                                         Sum total : 3450
--------------------------------------------------------------------------

リスト 9 の例では、ショッピング・カートの XML ファイルを構文解析し、商品ごとの小計と購入総額で請求書を生成しています (リスト 10)。

以下に、この例の内容を簡単に説明します。

まず、Ruby の REXML モジュールを組み込みます。このモジュールには、XML ファイルを構文解析するためのメソッドがあります。

shoppingcart.xml ファイルを開き、このファイルから Document オブジェクトを作成します。この Document オブジェクトが、構文解析された XML ファイルを含めるオブジェクトとなります。

文書のルートを要素オブジェクト root に割り当てます。これで、文書のルートは XML内の cart タグを指すことになります。

それぞれの要素オブジェクトには、属性オブジェクトがあります。このオブジェクトは要素の属性名とその値のハッシュで構成され、属性名がキー、属性の値がキーの値となります。ここでは、root.attributes['id']root 要素の id 属性の値を提供します。この例の場合、値は userid です。

次に、sumtotal を 0 に初期化し、ヘッダーを出力します。

各要素オブジェクトには、elements という名前のオブジェクトもあります。そのサブ要素には、each および [] メソッドでアクセスします。このブロックは root 要素のサブ要素である item をすべて処理します。これを指定しているのは、//item という XPath 式です。各要素オブジェクトには、その要素のテキスト値を保持する text 属性もあります。

続いて item 要素の code 属性、そして price 要素と qty 要素のテキスト値を取得し、商品ごとの小計を計算します。そして詳細を請求書に出力し、さらに商品の小計を sumtotal に追加します。

最後に、総額を出力します。

以下の例から、REXML と Ruby では簡単かつ単純に XML ファイルを構文解析できることがわかるはずです。XML ファイルを即座に生成するのも、要素とその属性を追加、削除するのもわけありません。

リスト 11. XML ファイルを扱う: XML ファイルの生成
doc = Document.new

doc.add_element("cart1", {"id" => "user2"})

cart = doc.root.elements[1]

item = Element.new("item")

item.add_element("price")

item.elements["price"].text = "100"

item.add_element("qty")

item.elements["qty"].text = "4"

cart .elements << item

リスト 11 に記載したスニペットは、cart 要素と item 要素およびそのサブ要素を作成することで、XML 構造を作成します。そして、これらの要素に値を取り込んだ上で、Document ルートに追加します。

同様に、要素と属性を削除するには Element オブジェクトの delete_element メソッドと delete_attribute メソッドをそれぞれ使用します。

上記の例は、ツリー方式の構文解析と呼ばれるものですが、XML 文書を構文解析する方法としてはストリーム方式の構文解析もあります。ストリーム方式の構文解析はツリー方式よりも早く処理が終わるため、速度が重視される場合には、ストリーム方式の構文解析を使用することができます。ストリーム方式の構文解析はイベントをベースであり、リスナーと連動します。タグが検出されるとリスナーが呼び出され、リスナーが処理を行うという仕組みです。

リスト 12 に、一例を示します。

リスト 12. XML ファイルを扱う: ストリーム方式の構文解析
require 'rexml/document'

require 'rexml/streamlistener'

include REXML

class Listener

  include StreamListener

  def tag_start(name, attributes)

    puts "Start #{name}"

  end

  def tag_end(name)

    puts "End #{name}"

  end

end

listener = Listener.new

parser = Parsers::StreamParser.new(File.new("shoppingcart.xml"), listener)

parser.parse

リスト 13 に、上記による出力を記載します。

リスト 13. XML ファイルを扱う: ストリーム方式の構文解析による出力
Start cart

Start item

Start price

End price

Start qty

End qty

End item

Start item

Start price

End price

Start qty

End qty

End item

Start item

Start price

End price

Start qty

End qty

End item

Start item

Start price

End price

Start qty

End qty

End item

End cart

以上のように、REXML と Ruby の強力な組み合わせにより、XML データを極めて効率的に、そして直観的に処理、操作することができます。

まとめ

Ruby には、迅速かつ強力で効率的なテキスト処理を可能にする充実した組み込みライブラリーと外部ライブラリーが揃っています。この機能を利用すれば、必要となるさまざまなテキスト・データ処理を単純化し、強化することができます。この記事では、Ruby に備わったテキスト・データ処理機能のいくつかの側面に触れただけに過ぎません。Ruby には、これより遙かに多くの機能があります。

Ruby がツール・ボックスに欠かせない素晴らしいツールであることに、疑問の余地はありません。


ダウンロード可能なリソース


関連トピック

  • IBM XML 認定: XML や関連技術の IBM 認定技術者になる方法について調べてください。
  • developerWorks podcasts: ソフトウェア開発者向けの興味深いインタビューとディスカッションを聞いてください。
  • IBM 製品の評価版: DB2®、Lotus®、Rational®、Tivoli®、および WebSphere® のアプリケーション開発ツールとミドルウェア製品を体験するには、IBM SOA Sandbox のオンライン試用版をダウンロードするか、詳しく調べてみてください。

コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=XML
ArticleID=428122
ArticleTitle=Ruby によるテキスト処理
publish-date=08182009