ソフトウェア/Igor/文字列関連

(4071d) 更新


ソフトウェア/Igor

ファイル名操作用のユーティリティ関数

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

同名の 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

文字列から両端の空白を取り除く

使用例

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

末尾の改行文字を取り除く

使用例

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

複雑な文字列から必要部分を取り出す方法をいくつか

区切り文字で区切られた文字列から、個々の文字列を取り出すにはいくつか方法があります。

標準のリスト・ペア関数

もっとも一般的なのは、
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)などを処理するのはこれらで十分です。

リスト処理関数のうち使用頻度の高いもの

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

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

ペア処理関数のうち使用頻度の高いもの

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

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

正規表現を使うなら SplitString

また、同様の目的で 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

地道に切り分けるならこんな方法も

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

この関数は、

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

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

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

使用例

LANG:Igor(linenumber)
function TestTakeUntil()

    // 任意フォーマットの文字列
    string s = "key1 = value1, value2 ; comment1"
    printf "[%s]\r", s
    // prints "[key1 = value1, value2 ; comment1]"
    
    // 始めて見付かる区切り文字までを取り去って返す
    // 区切り文字前後の空白は消去される
    printf "[%s]  [%s]\r", TakeUntil(s, "="), s
    // prints "[key1]  [value1, value2 ; comment1]"

    // 指定した以外の区切り文字は無視する
    // 内部の空白は保存される
    printf "[%s]  [%s]\r", TakeUntil(s, ";"), s
    // prints "[value1, value2]  [comment1]"

    // 指定した区切り文字が見付からなければすべてを取り去って返す
    printf "[%s]  [%s]\r", TakeUntil(s, ","), 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: 16344 (from 2010/06/03), today: 2, yesterday: 0