- ベストアンサー
substringで文字を分割する方法と正規表現の活用
- substringを使用して文字列を分割する方法と、正規表現を使用してマッチさせた文字列を抜き出す方法についてご質問があります。
- 具体的には、全角80桁の文字列を全角空白を利用して4分割する方法を知りたいです。
- また、正規表現でマッチさせた文字列以降をsubstringで抜き出し、その後も同じようにして20文字以降を正規表現を利用して分割したいと考えています。どのように実装すればよいかアドバイスをお願いします。
- みんなの回答 (6)
- 専門家の回答
質問者が選んだベストアンサー
> テストデータの中で例外が発見できました。 QNo.7510971のANo.1への補足で、そのような文字列(全角空白で区切ると20文字以内で4分割にできない)は「基本ありえません」と書いていたじゃありませんか。そのような入力データを作った人に文句を言ってください。 > 条件を増やして対応出来るのでしょうか?? できるかできないかで言ったらできるでしょうけど、問題の入力に対してどのような結果を得たいのかが示されていないので、コードの書きようがありません。 他にも例外的な入力データがあるかも知れないわけで、そういうものを見つけてから泥縄式に考えるのではなく、全てのあり得る入力データを場合分けするところから始めて、それぞれの場合にどのような処理をしなければいけないのかを考えてください。 なお、このような後出しじゃんけんにはいつまでもつき合っていられないので、本件への返答はこれで最後にします。
その他の回答 (5)
- salsberry
- ベストアンサー率69% (495/711)
> out.print(result1.replaceAll("[ +$]", ""));のようにしてスペースを > スペースなしと置換することが出来たのですが resultに対してではなく、分割を行う前の文字列に適用して下さい。 > > あと、この例の場合は末尾の空白を削ると2分割で足りるわけですが、実行後のカンマは必ず3つ要るのですか? > 仕様上要ります。 でしたら、そのくらいの変更は自力でどうぞ。 空白が含まれていない場合に4分割するための判定を行えるなら難しくはないはずです。 > 例えば住所桁数maxを20桁としてフラッグをたてて > 1 文字列内のスペース数をカウントし配列要素数を決定する > (以下略) 厳しい書き方になりますが、知っている用語を並べてみているだけで、やるべきことを理解できているように読めません。 たとえば、split()を使うなら1は不要な手順です。フラッグというのも何についてどういう条件のときに立てて、何の判定に使うつもりなのかが全く不明です。
お礼
>本当ですか?以前にも テストデータの中で例外が発見できました。 実行前→アアアアアア アアアアアア 00ー00ハハハ ハハハハハハハハハハハ アアアアアアハハハハハ ハハハハハハハハハハハ ハハハハハハハハハハハ ハハハハハハハハ こういったデータの場合、スペースを利用して4分割することが出来ないのですが 20ケタを目安にして、5分割にならないように住所の途中で、区切ってでも構わないという 条件を増やして対応出来るのでしょうか??
補足
アドバイスどうもありがとうございます。 > >> あと、この例の場合は末尾の空白を削ると2分割で足りるわけですが、実行後のカンマは必ず3つ要るのですか? この質問をされた意味が resultに対してではなく、分割を行う前の文字列に適用してから 分かりました。 提示したこの文字列ですと 立川市 上砂○ 0-00-0 ハハハハハハハハハ ハハハ 2分割になってしまいますから >でしたら、そのくらいの変更は自力でどうぞ。 空白が含まれていない場合に4分割するための判定を行えるなら難しくはないはずです。 提示していただいた構文にはたして 空白に含まれてない場合4分割に出来るかは まだわかりませんが if (住所漢字.compareTo (" ") != 0) { out.print(住所漢字.substring(0,20)+","+住所漢字.substring(21,40)+","+住所漢字.substring(41,60)+","+住所漢字.substring(61,80)); とこんな単純じゃないですからね。 そういった場合の4分割も私の知識だと時間がかかりそうです。 >厳しい書き方になりますが、知っている用語を並べてみているだけで、やるべきことを理解できているように読めません その通りだと思います。 VBAのプログラマが書いたコメントを省略して書いただけですから。 ほんとはVBAのプログラムを移植できればよいのですが そんな技量私にはありませんから 長い間にわたってお世話になっているって感じになってます。
- salsberry
- ベストアンサー率69% (495/711)
> 正規表現を考えれば > 記載して頂いたコードでも > 私がイメージしてる分割も出来ると思うのですが・・・ 無理。split()では、どんな文字(文字列)を使って区切るのかを正規表現で表すだけです。今回のように「全角空白で文字列を区切りたいんだけど、その長さには条件があって、その上区切った後の文字列の中に全角空白が残っていてもよい」なんて複雑な処理をこのメソッドで行うことはできません。 split()を使うなら、ANo.3さんも書いているように「全角空白で一度分割した後、文字列長をチェックして条件に合うように再結合する」という手順を踏むことになります。 > 実行前→ 立川市 上砂○ 0-00-0 ハハハハハハハハハ ハハハ > 実行後→ 立川市 上砂○ 0-00-0(14桁), ハハハハハハハハハ ハハハ(スペースを含めた20桁,(スペース20桁),(スペース20桁),(スペース2桁) 実行前の文字列は長さが80文字になるように末尾に空白がついているなんて前提、上の例にも書かれていませんよね。 あと、この例の場合は末尾の空白を削ると2分割で足りるわけですが、実行後のカンマは必ず3つ要るのですか? > あとスペースを詰めることが出来れば > すべてが解決すると思われます。 本当ですか? 以前にも指摘したとおり20文字以下の文字列4つには分割できない入力例はいくらでも作れますから、そのような場合に対応するためのコードをわんさか足さないといけないと思います。 ちなみに、文字列の末尾にある1文字以上の全角空白にマッチする正規表現は" +$"です。Matcherクラスのreplace()メソッドでも使って消してください。
お礼
知っている単語を並べているだけと言われたらそれまでですが 正規表現にマッチさせた結果 if (result○○○) { //resultの要素数1個の時は、 out.print(result+","+","+","); //kannmaを3つ増やし要素数4に修正 } else if (result○○○) { { //resultの要素数2個の時は、 out.print(result+","+",") ; //kannmaを2つ増やし要素数4に修正 } else if (result○○○) { { //resultの要素数3個の時は、 out.print(result+",") ; // kannmaを1つ増やし要素数4に修正 }} else { out.print(result) } } このような感じで考えていけばいいのでしょうか??
補足
>無理。split()では、どんな文字(文字列)を使って区切るのかを正規表現で表すだけです。 最初にsplit()で作業しようと思ったように 色々な条件で結合しなければいけないという事ですね。 例えば住所桁数maxを20桁としてフラッグをたてて 1 文字列内のスペース数をカウントし配列要素数を決定する 2 スペースをフィールドセパレートとして配列"住所"に入れる。 3 文字列の長さが桁を超えたら改行(分割)する 4 改行(分割)した文字列をカンマで区切る 5 最終的に80桁の文字列を4分割にする。 >あと、この例の場合は末尾の空白を削ると2分割で足りるわけですが、実行後のカンマは必ず3つ要るのですか? 仕様上要ります。 それはテキスト入出力後の条件になるのですが 実際テキストデータを印字する際 ソフトの都合上、住所1・住所2・住所3・住所4と設定して 4項目の印字位置に印字しますので データにより2分割にしてしまうと住所3の項目に別のデータが印字されてしまう もしくは設定エラーとなってしまうのです。 >本当ですか? 以前にも指摘したとおり20文字以下の文字列4つには分割できない入力例はいくらでも作れますから、そのような場合に対応するためのコードをわんさか足さないといけないと思います。 こちらに関しては、まだ例外を記載してないので 提示した以外で考えると嘘になってしまいます。 例えば、1つ例外としてわかっていることは お客様のテストデータではスペースが80桁すべての領域に 文字が埋まっているケースがありスペースなしでもちゃんと分割できるか試されます。 ですのでテスト合格するには別で条件に加える必要がございます。 その他では、今のとこなんパターンかのお客様テストデータを試しましたが 現状はsalsberryさんの構文で文字列は4つに分割されてます。 あとは教えていただいた正規表現" +$"で out.print(result1.replaceAll("[ +$]", ""));のようにしてスペースを スペースなしと置換することが出来たのですが 住所データが5分割のまま変わらないのは、まだ再構成が必要でしょうか??
- racene
- ベストアンサー率70% (21/30)
No.1 です。 正規表現が間違っているのではないでしょうか。 全角空白で分割するのであれば、正規表現は" "で大丈夫ですよ。 試しに手元で String str = new String("札幌市 北区 あああああ 0ハハ 00ハ 0ハ"); // 全角空白で分割 String[] strs = str.split(" "); // 連結 StringBuilder buf = new StringBuilder(); for(int i = 0; i < strs.length; i++) { if(i != 0) buf.append(','); buf.append(strs[i]); } // 結果を出力 System.out.println(buf.toString()); として実行したところ、 札幌市,北区,あああああ,0ハハ,00ハ,0ハ と出力されました。 ただし、20文字の制限については上のプログラムでは特に触れていないので、 もし必要なのであれば分割後の文字列の文字数をチェックしましょう。
補足
アドバイスどうもありがとうございます。 正規表現の知識が乏しいので、正規表現を考えれば 記載して頂いたコードでも 私がイメージしてる分割も出来ると思うのですが・・・ 色々条件がありましてなかなかすんなりとは いきません。
- salsberry
- ベストアンサー率69% (495/711)
1ヶ月以上経つのに、まだ同じことで悩んでいたんですね (QNo.7510971)。 > 正規表現でマッチさせた以降の文字 > 空白を含めた20文字のみ抜き取られて > それ以降は切り取られてます。 質問文に書いてあった正規表現を使ったのであれば当然の結果です。 str.split(正規表現)がどういう機能を持つか理解してから試してますか? もっとも、その正規表現を最初に示したのは僕なので責任の一端は感じます。 String str = "aaa bbbb ccccc dddddd eeeeeee ffffffff"; Pattern pattern2 = Pattern.compile("^.{1,20} "); String result = ""; while (str.length() > 20) { Matcher j = pattern2.matcher(str); if (j.find()) { String s = j.group(); int length = s.length(); result += s.substring(0, length-1) + ","; str = str.substring(length); } else { break; } } result += str; System.out.println(result);
補足
ご回答どうもありがとうございます。 ほとんど構文を記載していただき内容はまだ理解できてませんが 感謝してます。 >str.split(正規表現)がどういう機能を持つか理解してから試してますか? いいえ。理解してないまま試してました。 記載していただきました構文で試させていただいたところ 実行前→ 立川市 上砂○ 0-00-0 ハハハハハハハハハ ハハハ 実行後→ 立川市 上砂○ 0-00-0(14桁), ハハハハハハハハハ ハハハ(スペースを含めた20桁,(スペース20桁),(スペース20桁),(スペース2桁) と文字列はしっかり分割されてますが 文字列によっては5分割になる場合がございます。 あとスペースを詰めることが出来れば すべてが解決すると思われます。 今の私では考えてもすぐ解決できないと思います。 恐縮ですがアドバイスお願いいたします。
- racene
- ベストアンサー率70% (21/30)
正規表現で分割して、カンマ区切りに直したいということでよろしいでしょうか? もしそうであれば、 String str = new String(住所漢字); // 分割 String[] strs = str.split(正規表現); // 連結 StringBuilder buf = new StringBuilder(); for(int i = 0; i < strs.length; i++) { if(i != 0) buf.append(','); buf.append(strs[i]); } // 結果を出力 System.out.println(buf.toString()); でどうでしょうか。
補足
ご回答ありがとうございます。 >正規表現で分割して、カンマ区切りに直したい そうです。 raceneさんの認識で合っているかと思います。 そのままsplitでの構文を使用させて頂いた結果ですが 出力前 札幌市 北区 あああああ 0ハハ 00ハ 0ハ 出力後 ,0ハ と正規表現でマッチさせた以降の文字 空白を含めた20文字のみ抜き取られて それ以降は切り取られてます。
お礼
因みに以前お話しした 桁数20桁としてフラッグをたてる件ですが この方が 別の仕様の際に 要素数ごとに文字の長さを変えやすいので (わかりやすい) 変更しました。
補足
解決したことをご報告いたします。 色々なご提案をいただいて助かりました。 下記のコードに組みなおして 条件を満たしました。 提案いただいてなかったら、解決はしてなかったと思います。 あいまいな質問内容で長い間色々とお世話になり どうもありがとうございました。 String tmp_err ; //エラー用 String YousoMatch ; Integer iKeta ; Integer iKeta_Han = 20; Integer iKeta_Zen = 20; Integer iCnt_GYO =0 ; //行のカウンタ Integer FLG_ERR ; //エラーフラグ といった条件をたてて住所漢字と住所カナ表示のスイッチ切り替え(その他) str=住所漢字.replaceAll(" ",""); if (str.length() == 0){ //(2) tmp= 住所カナ.replaceAll(" +$", "")+" "; iKeta=iKeta_Han; YousoMatch="^.{1," + iKeta + "} "; tmp_err=住所カナ; //エラー用 }else{ tmp= 住所漢字.replaceAll(" +$", "")+" "; iKeta=iKeta_Zen; YousoMatch="^.{1," + iKeta + "} "; tmp_err=住所漢字; //エラー用 }; として Pattern pattern2 = Pattern.compile(YousoMatch); String result = ""; //要素数のカウントを取る //FLG_ERR=0;が4周以上回った時点でオーバーフロー FLG_ERR=0; while (tmp.length() > 0) { iCnt_GYO++; Matcher iHit = pattern2.matcher(tmp); if (iHit.find()) { String s = iHit.group(); int length = s.length(); result += s.substring(0, length-1) + ","; tmp = tmp.substring(length); } else { FLG_ERR=1; break; } }; //要素数が4つ以上の場合オーバーフローで住所の途中で分割 //スペースがない文字列も含む if ((iCnt_GYO >= 4) || ( FLG_ERR==1) ) { result=tmp_err.substring(0,iKeta) + "," +tmp_err.substring(iKeta,iKeta*2) +"," +tmp_err.substring(iKeta*2,iKeta*3) +"," +tmp_err.substring(iKeta*3,iKeta*4); 要素数の判定でカンマをつける } else if (iCnt_GYO == 1){ result= result + "," +","; } else if (iCnt_GYO == 2) { result= result + "," ; } else if (iCnt_GYO == 3) { //処理なし } else if(iCnt_GYO ==4 ){ //処理なし };