Split メソッドによる分割

タグの編集
投稿者 N88-BASIC  (社会人) 投稿日時 2016/10/5 13:12:24
基本的な事柄になるかもしれませんが、Split メソッドの動作を教えてください。

Split メソッドで文字列を分割したいと考えております。

市販の解説書によると引数として文字列が指定できるようですが、配列に格納されるデータには、引数の一文字目だけが削除された結果が格納されるようです。引数として指定した文字列全体が削除された結果が格納されるような動作を得ることができる方法があればご教授ください。
Replace ではうまく変換されるのが少し不思議です。

試したコードは
  Dim sArray() As String
  sArray = sBunsho.Split(vbCrLf)

結果は
    sArray(0) = "1行目"
    sArray(1) = vbLf + "2行目"
      ..
    sArray(n) = vbLf          -> vbCrLf が頭に入る

私の期待は
    sArray(0) = "1行目"
    sArray(1) = "2行目"
      ..
    sArray(n) = "n行目"               -> vbCrLf が削除された状態

私の対策
  sBunsho = sBunsho.Replace(vbCrLf , vbTab)
  sArray = sBunsho.Split(vbTab)

マイクロフトさんの解説では [stop] を区切り文字としての例があったのですが、理解不足の影響かもしれませんが、再現できませんでした(stop] が残ります)。

システムは Windows 10 Pro + Visual Studio 2015 Community です。

以上 よろしくお願いします。
投稿者 shu  (社会人) 投稿日時 2016/10/5 14:00:19
String.Splitで文字列1つのみを引数とするメソッドはありません。
Stringを渡せるのは

Split(String(), Int32, StringSplitOptions)
Split(String(), StringSplitOptions)
の2つとなります。

   https://msdn.microsoft.com/ja-jp/library/system.string.split(v=vs.110).aspx



今回の事を行うには
sArray = sBunsho.Split({vbCrLf}, StringSplitOptions.None)
などとする必要があります。

記述されたコードで問題なく動作したのはOption InferがOffになっているため
縮小変換が行われている為かと思います。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2016/10/5 15:47:25
Option Infer ではなく、Option Strict ですね。
今回のケースでは、「ParameArray separator() As Char」のオーバーロードに渡されています。


Option Strict が Off の場合、sBunsho.Split(vbCrLf) が
エラー無く呼べてしまいますが、その結果は
 sBunsho.Split(CChar(vbCrLf))
へと解決されます。これは .Split(ControlChars.Cr) と同義です。

Option Strict On で動くコードとしては
 sArray = sBunsho.Split(vbCrLf.ToCharArray())
 sArray = sBunsho.Split(ControlChars.Cr, ControlChars.Lf)
などがありますが、これだと結果が変わってきてしまいますので、
shu さんが書かれたように、String の配列で渡すようにします。


なお、shuさんが書かれたコードを試す場合は、
VB2010 以上が必要になります。VB2005/2008 の場合は
 sArray = sBunsho.Split(New String() {vbCrLf}, StringSplitOptions.None)
というコードにしてあげてください。

また、VB.NET 2002/2003 の場合は、
String.Split では対応できないので、
Strings.Split で代用することになります。

sArray = Strings.Split(sBunsho, vbCrLf)



なお、sBunsho の値が Nothing だった場合、
sArray = sBunsho.Split({vbCrLf}, StringSplitOptions.None) はエラーになるので注意して下さい。
sArray = Strings.Split(sBunsho, vbCrLf) あるいは
sArray = CStr(sBunsho).Split({vbCrLf}, StringSplitOptions.None) なら大丈夫です。

VB2015の場合は、下記のような回避策もあります。
  sArray = sBunsho?.Split({vbCrLf}, StringSplitOptions.None)
ただし sBunsho が空の場合、sArray にも Nothing がセットされます。



> マイクロフトさんの解説では
「マイクロフトさん」だと、シャーロックホームズのお兄さんになってしまう…。


> sBunsho = sBunsho.Replace(vbCrLf , vbTab)
> sArray = sBunsho.Split(vbTab)
vbTab だと、文中に含まれていてもおかしくないので、置き換えるのであれば、
 sArray = sBunsho.Replace(vbCrLf, vbNullChar).Split(ControlChars.NullChar)
あるいは、
 sArray = sBunsho.Replace(vbCrLf, vbCr).Split(ControlChars.Cr)
の方が良いと思います。
投稿者 N88-BASIC  (社会人) 投稿日時 2016/10/6 10:35:10
shu さん、魔界の仮面弁士 さん、ご回答ありがとうございました。

shu さん、
>String.Splitで文字列1つのみを引数とするメソッドはありません。
参考書籍のサンプルでは一文字が区切り文字でしたが、”文字列”と記載されていたので何も考えず”文字通り”に受っとってしまっていたようです。

魔界の仮面弁士 さん、
shu さんへの報告にあるように”文字列”の解釈に誤解があったようです。
この場合の文字列は私の想像ですが配列に格納された文字列では?と解釈しました。
マイクロソフトさんの例では、以下のようなものでした。
 
Dim stringSep() As String = {"[stp]"}
sArray = sBunsho.Split(stringSep,StringSplitOptions.RemoveEmptyEntries)

これを Dim stringSep() As String = {"[stp]"} を無視して、"[stp]" をそのままコード化したようです。
Dim stringSep() As String = {"[stp]"} を参考にして vbCrLf を"[stp]" に単純に置き換えましたが、考えが甘かったようでエラーとなりました。ただ、"[stp]" を使用して試したらうまくいきました。

結果としては、
 sArray = CStr(sBunsho).Split({vbCrLf}, StringSplitOptions.None) 
が、適応力がありそうで、こちらを採用させていただきました。

今後ともよろしくお願いいたします。

PS。「マイクロフトさん」、ネットで検索しました。
投稿者 shu  (社会人) 投稿日時 2016/10/7 08:08:41
> 参考書籍のサンプルでは一文字が区切り文字でしたが
サンプルの記述が正確でないと思うのですが例えばカンマで区切りたい場合は
~.Split(",")ではなく
~.Split(","c)が正しい記述となります。


> この場合の文字列は私の想像ですが配列に格納された文
想像しなくても既に回答しているようにMSDNに載っています。

https://msdn.microsoft.com/ja-jp/library/system.string.split(v=vs.110).aspx

String.Splitの定義は
Split(Char())         => Charの配列ですが、ParamArrayのため配列渡ししなくても使えます。
Split(Char(), Int32)  => Charの配列です
Split(Char(), Int32, StringSplitOptions)  => Charの配列です
Split(Char(), StringSplitOptions) => Charの配列です 
Split(String(), Int32, StringSplitOptions)  => Stringの配列です
Split(String(), StringSplitOptions) => Stringの配列です

があります。


> これを Dim stringSep() As String = {"[stp]"} を無視して、"[stp]" をそのままコード化したようです。
この部分はだれがそうしてしまったのでしょうか?


> sArray = CStr(sBunsho).Split({vbCrLf}, StringSplitOptions.None) 
> が、適応力がありそうで、こちらを採用させていただきました。
sBunshoは文字列ではないのでしょうか?文字列ならCStrは不要です。
適応力というのはStringSplitOptions.Noneの事を言っているのでしょうか?
これは空文字列になった部分も省略しないという事なので用途に合わせ
省略するならRemoveEmptyEntries、しないならNoneを指定するようになります。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2016/10/7 11:42:58
> マイクロソフトさんの例では、以下のようなものでした。
> Dim stringSep() As String = {"[stp]"}
> sArray = sBunsho.Split(stringSep,StringSplitOptions.RemoveEmptyEntries)

今回のケースなら、こうなるべきでしたね。
Dim stringSep() As String = {vbCrLf}
sArray = sBunsho.Split(stringSep, StringSplitOptions.RemoveEmptyEntries)



> sArray = CStr(sBunsho).Split({vbCrLf}, StringSplitOptions.None) 
> が、適応力がありそうで、こちらを採用させていただきました。
すみません、先の解説に間違いがありました。
Nothing 対応のコードは、下記のように書きなおしておいて下さい。

'sBunsho が Nothing の時に、「sArray = New String(0) {""}」相当の結果にする場合 
Dim sArray1() As String = If(sBunsho, "").Split({vbCrLf}, StringSplitOptions.None)
Dim sArray2() As String = (sBunsho & "").Split({vbCrLf}, StringSplitOptions.None)
Dim sArray3() As String = Strings.Split(sBunsho, vbCrLf)


'sBunsho が Nothing の時に、「sArray = New String(-1) {}」相当の結果にする場合 
Dim sArray4() As String = If(sBunsho Is NothingNew String(-1) {}, sBunsho.Split({vbCrLf}, StringSplitOptions.None))
Dim sArray5() As String = If(sBunsho?.Split({vbCrLf}, StringSplitOptions.None), New String(-1) {})


'sBunsho が Nothing の時に、「sArray = Nothing」相当の結果にする場合 
Dim sArray6() As String = If(sBunsho Is NothingNothing, sBunsho.Split({vbCrLf}, StringSplitOptions.None))
Dim sArray7() As String = sBunsho?.Split({vbCrLf}, StringSplitOptions.None)
投稿者 N88-BASIC  (社会人) 投稿日時 2016/10/7 19:23:59
shu さん、魔界の仮面弁士 さん、フォローアップアップありがとうございます。

shu さんへ、

>~.Split(",")ではなく、~.Split(","c)が正しい記述となります。
VS2013 の解説書は.Split(",")となっており、VS2015の解説書は.Split(","c)となっておりました。VS2013用のほうが印刷の説明があるので、専らこちらを参考にしていました(ちなみに出版社は同じです。実例では"/" で、文字列の場合の例はありませんでした)。

>配列の件
恥ずかしながら、Strings() の () の意味を正しく理解していませんでした。

>"[stp]" をそのままコード化
私が行いました。

>適応力がありそうで
魔界の仮面弁士の説明内の”なお、sBunsho の値が Nothing だった場合”を適応力があると判断し参考にいたしました。

魔界の仮面弁士さんへ

>今回のケースなら…、Dim stringSep() As String = {vbCrLf}
実はこの方法は試していたのですが、いかんせん、第二引数を質問時の例にもあるように省いてしまっており、エラーが表示されたのであきらめていました(マイクロソフトさんの説明にはたどり着いていたのに、正しく、説明通りにしなかったのが悔やまれます。ただ、sBunsho の後ろの ? は記述していないでしょう(今も意味がよくわかっていません))。

>Nothing 対応のコードは、下記のように書きなおしておいて下さい。
恥ずかしながら理解できないコードがありますが、sArrya7 を利用して続くコードを検討させていただきます。ただ、StringSplitOptions は処理の都合上 RemoveEmptyEntries を採用したいと思います(簡単なテストですがおかげさまで目的は達成されています)。

今後ともよろしくお願いします。

投稿者 shu  (社会人) 投稿日時 2016/10/8 12:31:57
> >~.Split(",")ではなく、~.Split(","c)が正しい記述となります。
> VS2013 の解説書は.Split(",")となっており、VS2015の解説書は.Split(","c)となっておりました。VS2013用のほうが印刷の説明があるので、専らこちらを参考にしていました(ちなみに出版社は同じです。実例では"/" で、文字列の場合の例はありませんでした)。

解説書の内容が全て正しいとは限らないので注意された方がよいです。
先の回答で間違えましたがOption Strict On を指定するようにした方が
このような間違えを探しやすくなります。Option ExplicitやOption InferもOnに
された方が意図しない動きを軽減するのに役立ちます。



> sBunsho の後ろの ? は記述していないでしょう(今も意味がよくわかっていません))。
?.は新しく追加された機能でsBunshoがNothingの場合後の処理を行わずNothingを返すという
機能になります。次の処理とほぼ同じと考えてよいかと思います。

Dim sArray7 As String
If sBunsho Is Nothing Then
    sArray7 = Nothing
Else
    sArray7 = sBunsho.Split({vbCrLf}, StringSplitOptions.None)
End If





投稿者 N88-BASIC  (社会人) 投稿日時 2016/10/9 08:33:41
shu さん、ご説明ありがとうございます。

Option Explicit (Stric ?) は変数が宣言されていないや使用されていない、値が代入されていないなど勝手に表示されていたので特段気にしておりませんでした。
今後は積極的にコードに反映させていただきます。

文字列名の後ろの「?」については便利そうです。と言いながら、動作は理解できますが、いまいち、現実の利用方法がはっきりとしたイメージとしてわいてきておりません。ただ、今回の例を参考に Nothing となるデータを扱う場合に利用していきたいと思います。私的利用しか行わないのでなかなか例外的なデータへの対処は甘くなりがちなのは否めません。今後の課題にしたいと思います。

今後ともよろしくお願いします。