ソフトウェア/Igor/文字列関連 の履歴(No.4)
更新ファイル名操作用のユーティリティ関数†
":" 区切りで渡されたファイルパスを操作する関数。
同名の 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