ソフトウェア/Igor/文字列関連 のバックアップの現在との差分(No.3)

更新


  • 追加された行はこの色です。
  • 削除された行はこの色です。
[[ソフトウェア/Igor]]

#contents

* ファイル名操作用のユーティリティ関数 [#x15fe647]

":" 区切りで渡されたファイルパスを操作する関数。

同名の Delphi の関数を模したものです。

使用例

 LANG:Igor(linenumber)
 // ファイル名部分のみを取り出す
 print ExtractFileName("c:folder1:folder2:file.ext")
 // prints "file.ext"
 
 // ファイル名が空の場合には空になる
 print ExtractFileName("c:folder1:folder2:")
 // prints ""
 
 // パス部分のみを取り出す
 print ExtractFilePath("c:folder1:folder2:file.ext")
 // prints "c:folder1:folder2:"
 
 // 末尾に ":" が付くのでファイル名とそのまま結合できる
 print ExtractFilePath("c:folder1:folder2:file.ext") + "newfile.ext"
 // prints "c:folder1:folder2:newfile.ext"
 
 // ":" が含まれなければ "" を返す
 print ExtractFilePath("file.ext")
 // prints ""
 
 // 拡張子は "." 付きで渡す
 print ChangeFileExt("c:folder1:folder2:file.ext", ".txt")
 // prints "c:folder1:folder2:file.txt"
 
 // ext に "" を渡せば拡張子を取り除ける
 print ChangeFileExt("c:folder1:folder2:file.ext", "")
 // prints "c:folder1:folder2:file"
 
 // "." が2つあれば後ろが拡張子を表わすものと考える
 print ChangeFileExt("c:folder1:folder2:file.ext1.ext2", ".txt")
 // prints "c:folder1:folder2:file.ext1.txt"

コード

 LANG:Igor(linenumber)
 // ファイル名を返す
 function/T ExtractFileName(filename)
 string filename
    return filename[ strsearch(filename, ":", inf, 3)+1, inf]
 end
 
 // ファイルパスを返す (末尾には : が付く)
 // ":" が含まれない文字列では "" が返る
 function/T ExtractFilePath(filename)
 string filename
     return filename[0, strsearch(filename, ":", inf, 3)]
 end
 
 // filename の最後の . 以降を取り除いたものに ext を付ける
 function/T ChangeFileExt(filename, ext)
 string filename, ext
     variable i = strsearch(filename, ".", inf, 3)
     if(i<0)
         return filename + ext
     else
         return filename[0,i-1] + ext
     endif
 end

* 文字列から両端の空白を取り除く [#cce50adb]

使用例

 LANG:Igor(linenumber)
 print Trim("  abc    ")
 // prints "abc"
 
 print Trim("     ")
 // prints ""

コード

 LANG:Igor(linenumber)
 // 両端の空白文字を取り除く
 function/T Trim(s)
 string s
     variable i, l = strlen(s)
     for(i=0; i < l && char2num(s[i]) == char2num(" "); i+=1)
         ;
     endfor
     s = s[i, inf]
     for(i=strlen(s)-1; i >= 0 && char2num(s[i]) == char2num(" "); i-=1)
         ;
     endfor
     return s[0, i]
 end

* 末尾の改行文字を取り除く [#xcf60978]

使用例

 LANG:Igor(linenumber)
 // テキストファイルの内容をテキストウェーブに読み込む
 function ReadTextFileIntoTextWave(filename, textwave)
 string filename
 wave/t textwave
 
    variable f
    open/R/Z=2 f as file_name
    if(!f)
        print "File not found."
        return 0
    endif
 
    Redimension/N=0 textwave    // 一旦クリア
    for(;;)
        freadline f, s          // 一行読む
        if(strlen(s)==0)        // eof に到達したら終了
            break
        endif
        Redimension/N=(numpnts(textwave)+1) textwave     // 一行増やす
 
        // FReadLine の結果は gets と同様末尾に改行文字が付くので、
        // それを取り去ってから格納する
        textwave[numpnts(textwave)-1] = RemoveNewLine(s) // 最終行に格納
    endfor
 
    close f
 
    return 1
 end
 
 function test()
     make/T/O textwave
     ReadTextFileIntoTextWave("somefile.txt", textwave) 
 end

コード

 LANG:Igor(linenumber)
 // 末尾の改行文字を取り除く
 function/T RemoveNewLine(s)
 string s
     variable i
     for(i=strlen(s)-1; i >= 0 && IsNewLine(s[i]) ; i-=1)
         ;
     endfor
     return s[0, i]
 end
 
 // 与えられた文字が改行文字かどうかを判別
 function IsNewLine(c)
 string s
     return char2num(c)==13 || char2num(c)==10
 end

* 文字列の先頭から指定した区切り文字までを取り去って返す [#iad4b18b]
* 複雑な文字列から必要部分を取り出す方法をいくつか [#iad4b18b]

文字列が十分に構造化されていれば、
区切り文字で区切られた文字列から、個々の文字列を取り出すにはいくつか方法があります。

StringFromList, NumberFromList などのリスト関数や、~
StringByKey, NumberByKey などのペア関数を使った方が楽ですが、
** 標準のリスト・ペア関数 [#q05de1e0]

そうでない場合には役に立ちます。
もっとも一般的なのは、~
StringFromList, NumberFromList, ItemsInList などのリスト関数や、~
StringByKey, NumberByKey などのペア関数を使う方法です。

区切り文字の前後の空白は無視されます。
例えば、

 LANG:Igor
 print ItemsInList("test1,test2,test3,test4", ",")
 // prints 4
 print StringFromList(1, "test1,test2,test3,test4", ",")
 // prints "test2"
 print StringByKey("key2", "key1:value1\nkey2:value2\n", ":", "\n")
 // prints "value2"

と言った具合です。

カンマ区切り値(CSV)などを処理するのはこれらで十分です。

*** リスト処理関数のうち使用頻度の高いもの [#yae801e0]

"value1;value2;value3;value4;" のような文字列を、~
他の言語の Array や List の用に使います。~
文字列ベースなので、当然高速動作は期待できません。

- ''ItemsInList'' : 区切り文字列に含まれる項目数を返す
- ''StringFromList'' : 区切り文字列の n 番目を文字列として取り出す
- ''NumberFromList'' : 区切り文字列の n 番目を文字列として取り出す
- ''AddListItem'' : 区切り文字列の n 番目に項目を追加する
- ''RemoveListItem'' : n 番目の文字列をリストから取り除く
- ''WhichListItem'' : 指定した文字列がリストの何番目に出てくるか検索する
- ''RemoveFromList'' : 文字列を指定してリストから取り除く
- ''ListMatch'' : リストの中にワイルドカードで指定した形の文字列があればその文字列を返す

*** ペア処理関数のうち使用頻度の高いもの [#n79abe3b]

"key1:value1;key2:value2;" のような文字列に対して、~
他の言語の Dictionary とか Hash に当たる使い方ができます。~
文字列ベースなので、当然高速動作は期待できません。

- ''ItemsInList'' : キー・値のペアが何個含まれるか返す
- ''StringByKey'' : キーに対応する値を文字列として得る
- ''NumberByKey'' : キーに対応する値を数値として得る
- ''RemoveByKey'' : キーに対応する(複数あれば最初の)値を取り除く
- ''ReplaceStringByKey'' : キーに対応する文字列を置き換える or 無ければ追加する
- ''ReplaceNumberByKey'' : キーに対応する文字列を置き換える or 無ければ追加する

** 正規表現を使うなら SplitString [#s89d8ba1]

また、同様の目的で SplitString が使えることも多いです。

SplitString は名前に反して、
通常の正規表現の後方参照を文字列変数に受け取るための関数なので、
GrepString でマッチした部分を取り出せずにがっかりしている場合に便利です。

多くの言語で Split は与えられた文字列をデリミタで分けて
配列にして返す関数なので、Igor でのこの名前の付け方はどうかと思うのですが・・・

例として、"key1 = value1, value2 ; comment1" という文字列を、~
"=" および ";" を区切り文字として、~
"key1", "valu1, value2", "comment1"~
の3つに分けることを考えます。

SplitString を使うとこんな風に簡単にできます。

 LANG:Igor
 function TestSplitString()
     string k, v, c
     string input = "key1 = value1, value2 ; comment1"
     SplitString/E="^(.*?)\s*=\s*(.*?)\s*;\s*(.*?)$" input, k, v, c
     print V_Flag
     // prints 3
     printf "[%s] [%s] [%s]", k, v, c
     // prints "[key1] [value1, value2] [comment1]"
 end

** 地道に切り分けるならこんな方法も [#o4a1e589]

もっとややこしい文字列を分解するには、
以下の TakeUntil が便利に使えることもあります。

この関数は、
- 与えた文字列の先頭から、
- 指定した区切り文字の直前までを返り値とし、
- 元の文字列変数には区切り文字までを取り去った結果が残ります

長い文字列に対して連続して呼び出すことで次々に値を取り出せます。

- 値を返す際、区切り文字の前後の空白は取り除かれます
-- そのため、上で定義した Trim() という関数を中で使っています

使用例

 LANG:Igor(linenumber)
 function TestTakeUntil()
 
     // 任意フォーマットの文字列
     string s = "text1 : text2  ;text3, text4"
     string s = "key1 = value1, value2 ; comment1"
     printf "[%s]\r", s
     // prints "[text1 : text2  ;text3, text4]"
     // prints "[key1 = value1, value2 ; comment1]"
     
     // 始めて見付かる区切り文字までを取り去って返す
     // 区切り文字前後の空白は消去される
     printf "[%s]  [%s]\r", TakeUntil(s, ":"), s
     // prints "[text1]  [text2  ;text3, text4]"
     printf "[%s]  [%s]\r", TakeUntil(s, "="), s
     // prints "[key1]  [value1, value2 ; comment1]"
 
     // 指定した以外の区切り文字は無視する
     // 内部の空白は保存される
     printf "[%s]  [%s]\r", TakeUntil(s, ","), s
     // prints "[text2  ;text3]  [text4]"
     printf "[%s]  [%s]\r", TakeUntil(s, ";"), s
     // prints "[value1, value2]  [comment1]"
 
     // 指定した区切り文字が見付からなければすべてを取り去って返す
     printf "[%s]  [%s]\r", TakeUntil(s, ","), s
     // prints "text4", s == ""
     // prints "[comment1] []"
 
 end

コード

 LANG:Igor(linenumber)
 function/T TakeUntil(s, delim)
 string &s, delim
     variable d = StrSearch(s, delim, 0)
     string result
     if (d < 0)
         result = Trim(s)
         s = ""
         return result
     endif
     result = Trim(s[0, d-1])
     s = Trim(s[d+strlen(delim), inf])
     return result
 end

この関数、s を変更するのではなく検索開始位置を i に与えることとして、
次のようにする手もありますね。~
文字列の再確保が必要ない分、この方が動作が高速かもしれません。

 LANG:Igor(linenumber)
 function/T TakeUntil2(s, delim, i)
 string s, delim
 variable &i
     variable d = StrSearch(s, delim, i)
     string result
     if (d < 0)
         result = Trim(s[i, inf])
         i = strlen(s)
         return result
     endif
     result = Trim(s[i, d-1])
     i = d + strlen(delim)
     return result
 end
 
 function TestTakeUntil2()
 
     // 任意フォーマットの文字列
     string s = "key1 = value1, value2 ; comment1"
     printf "[%s]\r", s
     // prints "[key1 = value1, value2 ; comment1]"
     
     // 始めて見付かる区切り文字までを取り去って返す
     // 区切り文字前後の空白は消去される
     variable i = 0
     printf "[%s]  [%s]\r", TakeUntil2(s, "=", i), s[i, inf]
     // prints "[key1]  [ value1, value2 ; comment1]"
 
     // 指定した以外の区切り文字は無視する
     // 内部の空白は保存される
     printf "[%s]  [%s]\r", TakeUntil2(s, ",", i), s[i, inf]
     // prints "[value1, value2]  [ comment1]"
 
     // 指定した区切り文字が見付からなければすべてを取り去って返す
     printf "[%s]  [%s]\r", TakeUntil2(s, ",", i), s[i, inf]
     // prints "[comment1] []"
 
 end


Counter: 10933 (from 2010/06/03), today: 2, yesterday: 0