質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.55%

正規表現に複数マッチした場合、文字列をsplitしてreturnしたい

解決済

回答 6

投稿

  • 評価
  • クリップ 3
  • VIEW 386

Kimsehwa

score 293

現在、正規表現にマッチしてる文字をリータンするロジックを作ってます。
コードは以下のとおりです。

function regexTest(str){

  const regex = RegExp('^([0-6]?[0-9]):([0-6]?[0-9])');

  if (regex.test(str)) {
    console.log('match')
    return str
  } else {
    console.log('unmatch') 
    return undefined
  }
}

上記コードに機能追加で
条件を複数マッチした場合、マッチした文字から新しくマッチした文字以前までを取り出して
マッチした分配列に入れてreturnしたいです。

期待値は以下のように考えてます

// pattern1
regexTest('0:18 hoge') // -> except return '0:18 hoge'
// pattern2
regexTest('hoge 0:34 hoge') // -> except return 'hoge 0:34 hoge'
// pattern3
regexTest('0:18 hoge 0:34 hoge') // -> except return ['0:18 hoge', '0:34 hoge']

pattern1,pattern2はできてますが、pattern3を具現するにはどうすればよいかがわからずこちらに質問させていただきました。

ヒントになるキーワードでもよいので実装方法をご教示いただけますか?

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • yambejp

    2019/11/01 12:36

    1つだと文字列で、複数だと配列というのはあまりよくないですね

    キャンセル

  • think49

    2019/11/04 08:59

    まだ解決できない状態(回答が意図したものではない)のであれば、意図が正しく伝わる文章/コードになるよう質問を修正して下さい。

    キャンセル

回答 6

checkベストアンサー

+3

仕様がよくわからないのでとりあえず動くもの

function regexTest(str){
  const r1 = RegExp('^(?:([0-5]?[0-9]:[0-5]?[0-9]) .*? ?)+$');
  const r2 = RegExp('[0-5]?[0-9]:[0-5]?[0-9] .*?( |$)','g');
  if(r1.test(str)){
    const r3=str.match(r2);
    console.log('match')
    return r3;
  } else {
    console.log('unmatch') 
    return undefined
  }
}
console.log(regexTest('0:18 hoge'));
console.log(regexTest('hoge 0:34 hoge'));
console.log(regexTest('0:18 hoge 0:34 hoge'));
console.log(regexTest('0:18 hoge 0:34 hoge 0:41 hoge 0:55 hoge'));

調整版

あ、2番めはマッチするですね、であればこうです

function regexTest(str){
  const pattern=".*?[0-5]?[0-9]:[0-5]?[0-9] ?.*?";
  const r1 = RegExp('^(?:'+pattern+' ?)+$');
  const r2 = RegExp(pattern+'( |$)','g');
  if(r1.test(str)){
    const r3=str.match(r2);
    console.log('match')
    return r3;
  } else {
    console.log('unmatch') 
    return undefined
  }
}
console.log(regexTest('hoge hoge'));
console.log(regexTest('0:18 hoge'));
console.log(regexTest('hoge 0:34 hoge'));
console.log(regexTest('0:18 hoge 0:34 hoge'));
console.log(regexTest('0:18 hoge 0:34 hoge 0:41 hoge 0:55 hoge'));

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+3

思ったより汚くなっちまった。

alert(regexTest("0:18 hoge"));
alert(regexTest('hoge 0:34 hoge'))
alert(regexTest('0:18 hoge 0:34 hoge'))

function regexTest(str) {
  // 正規表現定義して
  const regex = RegExp("([0-6]?[0-9]:[0-6]?[0-9])");
  // "XX:XX"を区切り文字として分割して
  const arr = str.split(regex);
  // それを結合して配列にいれる
  const result = [];
  let tmp = ""
  for (let i = 0, l = arr.length, beforeMatch = true; i < l; i++) {
    if (regex.test(arr[i])) {
      if (!beforeMatch) {
        if (tmp) result.push(tmp.trim());
        tmp = "";
      }
      beforeMatch = false;
    }
    tmp += arr[i];
  }
  if (tmp) result.push(tmp);
  return result.length === 1 ? result[0] : result; 
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+3

いくつか気になる点はありますが、

  • 返り値の種類によって条件分岐処理が必要(複雑化する)
  • 「分:秒」の書式に見えて、69:69 を許容する(そもそも、「分:秒」なら 1:60 は 2:00 ですが)

String#match を利用すれば、実装できます。

const sample = string => {
  const result = string.match(/^(?:(?![0-6]?\d:[0-6]?\d)[\s\S])+|[0-6]?\d:[0-6]?\d(?:(?![0-6]?\d:[0-6]?\d)[\s\S])*/g);
  return  result && result.length > 1 ? result : string;
};

console.log(sample('0:18 hoge'));           // "0:18 hoge"
console.log(sample('foo 69:69 bar'));       //  ["foo ", "69:69 bar"]
console.log(sample('0:18 hoge 0:34 hoge')); // ["0:18 hoge ", "0:34 hoge"]

String#match の返り値をそのまま使用すれば、条件分岐処理が不要になり、汎用性が高くなります。

Re: Kimsehwa さん

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+2

regex.test() はマッチしたかどうかの結果を返します。
regex.exec() はパターンにどのようにマッチしたかの配列を返します。
matched のように適当な変数に代入すると、パターンとの関係がつかみやすくなります。

  // regexp, strは略
  let matched;
  if (matched = regex.exec(str)) {
    console.log( "matched :", matched);// 配列
  }

正規表現のパターンについては、今一度、ご自身で調べてみてください。

ヒント:
RegExp オブジェクトの引数

  1. パターン:時刻部分しか定義できていません。(正規表現をググる)
  2. フラグ:3つめの期待値を得るためには?

追記)
正規表現 match() と exec() の違いにあるように、全体を一気に取得できる match() を使いました。

function regexTest(str){
  var regex = RegExp("\\s?([0-6]?[0-9]\\:[0-6]?[0-9])?\\s?([\\w]+)","g")
  , m
  ;
//if ( m = regex.exec(str) ) {
  if ( m = str.match(regex) ) { // matchを使う
    //console.log( "matched:", m );

    // マッチ結果 m には配列が来るので、期待値に合わせて調整  
    if( m.length === 1 ) {
      str = m[0];
    }
    else if( m[0].match(/^[^\d]/) ) {
      str = m.join("")
    }
    else {
      str = m
    }
    return str
  } else {
    console.log('unmatch') 
    return undefined
  }
}
var list = ['0:18 hoge','hoge 0:34 hoge','0:18 hoge 0:34 hoge'];
list.forEach( (src, idx) => {
  console.log( `#${idx} result :`, regexTest( src ) ) 
});


正規表現のマッチパターンの作り方:
次のようにチェックしながらどの文字を使うべきか検討してゆきます。

正規表現リテラル: RegExp()と違い、バックスラッシュ(\)を二重に書かずに済みます

 /\s?([0-6]?[0-9]\:[0-6]?[0-9])?\s?([\w]+)/g  "g"は繰り返し 
     ===========================   =======
     0:18                          hoge      ---> '0:18 hoge'
  ----------------------------------------
                                   hoge      -+-> 'hoge 0:34 hoge'
     0:34                          hoge      -+
  ----------------------------------------
     0:18                          hoge      -+-> '0:18 hoge 0:34 hoge'
     0:34                          hoge      -+

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+1

こんにちは

ご質問のタイトルが、「正規表現に複数マッチした場合、文字列をsplitしてreturnしたい」ということだったので、split を使うコードを回答します。

また、この回答では、入力される文字列を

  • 文字列"hoge" と、"hoge"ではなくかつ空白を含まない文字列(例: "0:18")とが、スペース区切りで交互に出現し、
  • 最後は "hoge" で終わり、
  • 先頭に空白文字は無い。

というものとして捉えました。以下、期待値を得るコードになります。

const FIXEDSTR = 'hoge';

function regexTest(str){

  let ary = str.split(/\s+/);

  if (ary.length > 1) {
    if (ary[0] === FIXEDSTR) {
      ary.shift();
      ary[0] = `${FIXEDSTR} ${ary[0]}`;
    }
    ary = ary.map((s, i) => i%2 ? null : `${s} ${FIXEDSTR}`).filter(s => s);
  }

  return ary.length === 1 ? ary[0] : ary;
}

// pattern1
console.log(regexTest('0:18 hoge')); // => "0:18 hoge"

// pattern2
console.log(regexTest('hoge 0:34 hoge')); // => "hoge 0:34 hoge"

// pattern3
console.log(regexTest('0:18 hoge 0:34 hoge')); // => "[ '0:18 hoge', '0:34 hoge' ]"

上記では、"hoge"ではなくかつ空白を含まない文字列が満たすべき正規表現^([0-6]?[0-9]):([0-6]?[0-9]) は使用していませんが、pattern1からpattern3 の入力に対しては、望まれる結果が出力されます。

とはいえ、 ご質問にある ^([0-6]?[0-9]):([0-6]?[0-9]) を使わない上記のコードでは、望む結果が得られないような、別の(pattern4 以降の)入力パターンとそれに対する期待値が、おそらくあると思われましたので、それらについては、コメントからお知らせ頂くか、質問に追記をお願い致します。

追記

([0-6]?[0-9]):([0-6]?[0-9]) に合致しない文字列、たとえば "70:70" といったものも、解析対象の文字列に入ってくる可能性があり、それを排除する必要があったりするならば、([0-6]?[0-9]):([0-6]?[0-9])にも出番はあると思いますが、ご質問にある pattern1 〜 pattern3 の事例では、hoge以外の全てのトークンは [0-6]?[0-9]:[0-6]?[0-9] を満たしていることが目視で確認できますので、([0-6]?[0-9]):([0-6]?[0-9])は不要になります。このように、正規表現の課題を考える場合、解析対象の文字列ではそもそも何が満たされていることを前提にしてよいのかをはっきりさせることで、正規表現を使ったプログラム側を(当初、想定していたものよりも)簡素にできることがあります。

以上、参考になれば幸いです。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

発想はjjj_aaaさんのと似ています。つまり、

  1. 00:00な文字列で対象を分割(ただし00:00自身もメンバーに含める)。
  2. 分割リストの1番目と2番目、3番目と4番目…を結合してこれを結果とする。
  3. ただし分割リストの0番目は結果リストの最初に連結する。

配列と単純文字列を出力し分ける必然性を感じなかったので、その処理は省略しています。どうしても必要ならlengthをみて変換とかすればいいと思いますが。

function regexTest(str){
        const regex = RegExp("(\\d{1,2}:\\d{1,2})");
        const arr = str.split(regex);
        r = [];
        for(i=1; i<=arr.length-2; i+=2){
            r.push(arr[i]+arr[i+1]);
        }
        r[0] = arr[0]+r[0];
        return(r);
}

console.log(regexTest("0:18 hoge"));
console.log(regexTest('hoge 0:34 hoge'));
console.log(regexTest('0:18 hoge 0:34 hoge')); 

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.55%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる