2010/02/26

UTF8-MAC [2]

UTF8-MAC みなおし。

要は \\ があればいいんでしょ?とお気楽な発想を展開してみました。
が、以下は非常によろしくないことをしている可能性あり。
あくまでも実験なので試さない方がいいです。おい

macustr.rb

  1 #!/usr/local/bin/ruby19
  2 # coding: utf-8
  3 
  4 exit unless Encoding.default_external.name == 'UTF-8'
  5 
  6 require 'nkf'
  7 def macustr(line)
  8   str = String.new # p str.encoding
  9   line.chars.entries.each{|x| str << "\\" + x}
 10   #line.codepoints.entries.each{|x| str << "\\" + [x].pack("U*")}
 11   mustr = NKF::nkf('--ic=UTF-8 --oc=UTF8-MAC', str)
 12   return mustr
 13 end
 14 
 15 ARGF.each{|line|
 16   exit unless line.encoding.name == "UTF-8"
 17   next unless line.valid_encoding?
 18   mustr = macustr(line.strip)
 19   next unless  mustr.valid_encoding?
 20   sh = system("mkdir #{mustr}")
 21   print "Maked Directory: #{line}" if sh
 22 } 
 23 


line 4 : 環境変数LANGが UTF-8 でない場合はおいかえす。
line 16: 読み込んだ文字列が UTF-8 でなければすぐ終了。
line 17: valid でなければ次
line 18: def macustr に一行わたす
line 9 : 冒頭に"\\" を付け加えて一文字ずつ str に追加。最初は line 10 にしたのだが line 9 にしておいた。
line 11: str を UTF8-MAC に変換
line 19: valid でなければ次
line 20: mkdir コマンド
line 21: コマンドが成功したらメッセージ表示

./macustr.rb utf8_text.txt

を実行すると utf8_text.txt を一行ずつ読み込み
同名のDirectory を同じ階層に作成します。
utf8_text.txt は UTF-8 で保存されたテキストファイル。

[2010-02-27]
s/chomp/strip/g

[2010-03-08]
追記
# 失敗例

  1 #!/usr/local/bin/ruby19
  2 # coding: utf-8
  3 # test-encode.rb
  4 
  5 text = 'utf8-text.txt'
  6 # % cat utf8-text.txt 
  7 # and two &three
  8 
  9 File.open(text, "r:utf-8"){|f|
 10   f.each_line{|x|
 11     #  行末\n や行頭\s を削除
 12     line = x.strip
 13     # print out
 14     puts "Line: \"#{line}\"| Encoding: #{line.encoding}| Valid: #{line.valid_encoding?}\n"
 15     puts "# Calling encode\n"
 16     # encode で変換
 17     mustr = line.encode("UTF8-MAC")
 18     # print out
 19     puts "String: \"#{mustr}\"| Encoding: #{mustr.encoding}| Valid: #{mustr.valid_encoding?}\n"
 20     print "# Calling system\n"
 21     # Directory 作成
 22     sh = system("mkdir #{mustr}")
 23     puts "Result: #{sh}\n--\n"
 24   }
 25 }
 26 
 27 =begin
 28 % ./test-encode.rb 
 29 Line: "and two &three"| Encoding: UTF-8| Valid: true
 30 # Calling encode
 31 String: "and two &three"| Encoding: UTF8-MAC| Valid: true
 32 # Calling system
 33 sh: three: command not found
 34 Result: false
 35 
 36 % ls -F
 37 and/            test-encode.rb* two/            utf8-text.txt
 38 =end


17行目

mustr = ine.encode("UTF8-MAC")

mustr = line.force_encoding("UTF8-MAC")

にしても同様の結果だし

require 'nkf'
mustr = NKF::nkf('--ic=UTF-8 --oc=UTF8-MAC', line)

としてみても結果は同じ

検索して次を読んでみたけど よくわかってないというか...。
<http://redmine.ruby-lang.org/issues/show/1411>
<http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-dev/40233?help>

+++ 感想 +++

通常 Mac OS X を使用する上で自ら日本語名のフォルダを作る人はあまりいないだろうし、スペースが混入した文字列をわざわざフォルダの名前にする人もあまりいないはずなので、じゃあ一体どういった場面で上のような必要が生じるかというと
ずばり
iTunes のための iTunes による Folder 生成 | File 生成なのかな? と。
2010/03/08 現在ではこれ以外の場面で UTF8-MAC への変換なんて一般的には必要とされてないことなのかもしれません。

ただ、今後 iPad が登場しまた新しくiのつく名前のアプリケーションがでてきたりすると UTF8-MAC への変換に対する需要は増えるかもしれませんね?電子書籍を買って読むための iBooks.app が OS X に搭載される日は案外近い将来かもしれません。
iPad

それで、例えば iBooks.app が生成するであろう著書名のついたファイルにアクセスする際、もしも著書名に\& や\s が含まれていたり日本語だったりしたらRuby からそれらのファイルにアクセスしようとするのはちょっと困難がありそうです。いまの iTunes ファイル群と同様に。
# Ruby からはそういうファイル群にアクセスしないから大丈夫...といった意見の人もいるかもしれません。
# あるいは上に書いた失敗例を[ちゃんとした]成功例にする方法を私が知らないだけのことなのかもしれません。
# macustr.rb は一見成功してるようにみえるけどもちゃんとはしていないだろうし。

はたまた違う観点からみた場合、セキュリティ的にどうなんだろう?って問題も私にはよくわからないけどきっとあるんだろうと思います。そっちが優先された上で現在こうなっているのであればこのままの方がうれしい人も多いのかもしれません?。

ということで、幾日経過後のまとまりのない&とりとめないのない感想でした。

# 私が間違った認識をしている可能性は多分にあります。何かお気付きの方はコメントお願いします。

2010/02/24

mdiary 最近の変更点

  5     def initialize(arg)
  6       @err = false
  7       k, @h = nil, Hash.new
  8       arg.each{|x| m = /^-(.*)/.match(x); (m) ? (k = m[1].to_sym; @h[k] = nil) : @h[k] ||= x}
  9     end


arg は ARGV の値

% .mdiary-run.rb -d 2010-01 -s music

を実行した場合 arg は

arg = ["-d", "2010-01", "-s", "music"]

になる。line 8 はこの配列を

@h = {:d=>"2010-01", :s=>"music"} 

のようなハッシュにするためのもの。

arg = ["-d", "2010-01", "-l", "3"] だった場合
@h={:d=>"2010-01", :l=>"3"}

arg = ["-d", "2010-01", "-l"] だった場合
@h={:d=>"2010-01", :l=>nil}
になる。

line 7-8 をくだいて書くと

  1 # arg = ["-d", "2010-01", "-s", "music"]
  2 k, @h = nil, Hash.new
  3 arg.each{|x| 
  4   m = /^-(.*)/.match(x)
  5   if m
  6     k = m[1].to_sym
  7     @h[k] = nil
  8   else
  9     @h[k] ||= x
 10   end
 11 } 


line 4: x が '-' で始まっていたら m の値は #<MatchData "-d" 1:"d">
line 5: m の値に何か入っていたら(マッチしたら)
line 6: k に m の1番目のマッチの値を代入。
line 7: ハッシュキーが k で 値が nil のペアを @h に格納。
line 8: m の値に何もはいっていなければ(マッチしなかったら)
line 9: @h[k] が nil の場合は x を代入。@h[k] の値が nil でなく何か値が入っていたら何もしない。

+++ memo +++

1) @h[k] ||= x

line 9 の @h[k] ||= x を @h[k] = x にしてしまうと
あやまって
arg = ["-d", "2010-01", "xxx", "-s", "music"]
が与えられてしまった時
@h = {:d=>"xxx", :s=>"music"}
になってしまう。これでは意図した結果と異なる為 ||= にしている。

2) k = nil

line 2 の k = nil を書き忘れ
line 2 は @h = Hash.new だけだった場合
arg = ["-d", "2010-01", "-s", "music"] を与えると
@h={:d=>nil, nil=>"2010-01", :s=>nil}
になってしまう。
なぜなら...
{} の外側に k が存在していなければ k は {} の中だけで使える変数となり
m == nil でなければ
k = m[1].to_sym なので k には m[1].to_sym が代入されるが

m == nil だった場合、すなわち else 節においては
k = nil が代入されてしまうから。そして
line 9: @h[k] ||= x
で nil を ハッシュキーとしたペア(nil=>"2010-01")を @h に格納することになってしまう。

だから{} の外側の k = nil はとても大切。
外側で定義された k があることによって else 節においても k は m[1].to_sym を保持していてくれる。

[2010-02-26]
arg.each{|x| m = /(^-(.*))/.match(x); (m) ? (k = m[2].to_sym; @h[k] = nil) : @h[k] ||= x}
arg.each{|x| m = /^-(.*)/.match(x); (m) ? (k = m[1].to_sym; @h[k] = nil) : @h[k] ||= x}

2010/02/22

UTF8-MAC

ここを読んで

私も perl でためしてみました。
% cat test.pl

#!/usr/bin/perl
while(<>) {
    chop;
    print "Making a folder $_...\n";
    mkdir();
}

% cat 02.txt

ABC_a'bc 織田 信長
ABC_e'fg 豊臣 秀吉
ふげふが リファレンス& ドロー


% ./test.pl 02.txt
Making a folder ABC_a'bc 織田 信長...
Making a folder ABC_e'fg 豊臣 秀吉...
Making a folder ふげふが リファレンス& ドロー...

わざと\s や ' や濁点や & を入れてみましたがちゃんとおかしな名前のフォルダが出来上がりました。

で、Ruby ではどうなのでしょ。
% cat test.rb

#!/path/to/ruby19
# coding: utf-8
ARGF.each{|line|
  system ("mkdir #{line}")
}


% ./test.rb 02.txt

% ./test.rb 02.txt 
sh: -c: line 0: unexpected EOF while looking for matching `''
sh: -c: line 1: syntax error: unexpected end of file
sh: -c: line 0: unexpected EOF while looking for matching `''
sh: -c: line 1: syntax error: unexpected end of file
sh: ドロー: command not found

こうなるのは予測していたので

http://gihyo.jp/dev/serial/01/ruby/0004?page=2 を読み直し

#!/path/to/ruby19
# coding: utf-8
require 'nkf'
ARGF.each{|line|
  ustr = NKF::nkf('--ic=UTF-8 --oc=UTF8-MAC', line)
  system ("mkdir #{ustr}")
}

nkf を使ってみました。が、最初と同じエラーが現れ ふげふが/ リファレンス/ の2のフォルダができました。

結果をみると \s のところでパスが区切られてしまったようなので
gustr = ustr.gsub(/\s/, "\\ ")
を挟んでみたものの ”&” でまたパスを区切られてしまいました。結局エラーになりそうな文字(て何?)を全て gsub しないとだめなのかもしれません。いやいやそんなことじゃなく正しい指定(nkf の)さえ分かればいいだけのことなのかもしれません。
あーわからない。

このままで話が終わるのはなんだか後味が悪いので AppleScript で Ruby を呼んでみました。

tell application "Finder"
  activate
  set cf to choose file with prompt "読み込みたいテキストファイル_UTF-8_を選択してください。"
  set dst to choose folder with prompt "保存先フォルダを選択してください。"
  set f to quoted form of POSIX path of cf
  set rubyfile to quoted form of POSIX path of (((folder of file cf) as text) & "ioread.rb")
  # ---------------------------
  try
    set str to do shell script "/usr/bin/ruby" & space & rubyfile & space & f
    set ary to paragraphs of str
    if (count of ary) > 3 then # this line
      return display dialog "体力の限界です。" buttons {"OK"} default button 1 with title "Oh, NO "
    end if
    repeat with fname in ary
      make new folder at (dst) with properties {name:fname}
    end repeat
  on error error_str
    display dialog error_str buttons {"OK"} default button 1 with title "Error"
  end try
end tell
# return AppleScript's version => "2.1.1"

% cat ioread.rb 
#!/usr/bin/ruby
f = ARGV[0]
print nil unless f
ary = IO.readlines(f)
ary.delete("\n")
print ary

% ls -F
ABC_a'bc 織田 信長/                                  ふげふが リファレンス& ドロー/
ABC_e'fg 豊臣 秀吉/


ちゃんとおかしな名前のフォルダができました。
テストなんでフォルダの数は3つまでに制限かけておきました。# this line のところ。


+++ 追記 +++
2010-02-22
しばらくしたら思いつきました。文字ごとに gsub してみようと。

% cat 02.txt

ABC_&a'bc 織田 信長
ABC_e'fg 豊臣 秀吉
ふげふが リファレンス& ドロー


test.rb

  1 #!/path/to/ruby19
  2 # coding: utf-8
  3 
  4 require 'nkf'
  5 def ex(line)
  6   mu = String.new("")
  7   ary = line.each_char{|x|
  8     gx = x.gsub(
  9       /[\s\'\(\)\+\!\&\,\[\]\;]/,
 10       " "=>"\\ ", "'"=>"\\'",
 11       "("=>"\\(", ")"=>"\\)",
 12       "+"=>"\\+", "!"=>"\\!",
 13       "&"=>"\\&", "."=>"\\.",
 14       "ï"=>"\\i", ","=>"\\,",
 15       "["=>"\\[", "]"=>"\\]",
 16       ";"=>"\\;"
 17     ) 
 18     mu << gx
 19   } 
 20   p macustr = NKF::nkf('--ic=UTF-8 --oc=UTF8-MAC', mu)
 21   p macustr.encoding
 22   system ("mkdir #{macustr}")
 23 end
 24 
 25 ARGF.each{|line| ex(line)}


% ./test.rb 02.txt

"ABC_\\&a\\'bc\\ 織田\\ 信長"
#<Encoding:UTF8-MAC>
"ABC_e\\'fg\\ 豊臣\\ 秀吉"
#<Encoding:UTF8-MAC>
"ふげふが\\ リファレンス\\&\\ ドロー"
#<Encoding:UTF8-MAC>


% ls -F

02.txt                                               test.rb*
ABC_&a'bc 織田 信長/                                 ふげふが リファレンス& ドロー/
ABC_e'fg 豊臣 秀吉/


line 10 "'" を "\\'" とスラッシュバックスラッシュ2つにしてみたらうまくいった。それがなぜなのかは分かっていない。
こういうのは力技と呼ばれることなんだろう。

UTF8-MAC [2] へ続く


[2011-02-19]

2010-02 時点での敗因はただ単にchomp しわすれとsystem コマンド発行する引数を'' で囲まなかったこと。それだけ。びっくりする愚かなおち。
nkf とかね関係ありません...。
それにしても今読み返してつくづく...単純ミスから泥沼していく様は自分でもあきれました。

% cat a.txt
ふげふが &どろー
% cat test.rb
# coding: utf-8

ARGF.each{|x|
  s =  "\'#{x.strip}\'"
  p sh = system("mkdir #{s}")
}
% ruby -v test.rb a.txt
ruby 1.8.7 (2009-06-12 patchlevel 174) [universal-darwin10.0]
true
% ls
a.txt                            test.rb                          ふげふが &どろー


ruby 1.9.2
でも同様の結果。