文字列に特定の単語が含まれているかどうかを確認するにはどうすればよいですか?

2010年12月06日に質問されました。  ·  閲覧回数 5.4M回  ·  ソース

Charles Yeung picture
2010年12月06日

考えてみましょう:

$a = 'How are you?';

if ($a contains 'are')
    echo 'true';

上記のコードがあるとすると、ステートメントif ($a contains 'are')を書く正しい方法は何ですか?

回答

codaddict picture
2010年12月06日
7189

strpos()関数を使用して、別の文字列内の1つの文字列の出現を見つけることができます。

$a = 'How are you?';

if (strpos($a, 'are') !== false) {
    echo 'true';
}

!== falseは意図的なものであることに注意してください( != false=== trueも目的の結果を返しません)。 strpos()は、干し草の山の文字列で針の文字列が始まるオフセット、または針が見つからない場合はブール値のfalseいずれかを返します。 0は有効なオフセットであり、0は「falsey」であるため、 !strpos($a, 'are')ような単純な構造を使用することはできません。

編集:

PHP 8を使用すると、次のことができます。

if (str_contains('How are you', 'are')) { 
    echo 'true';
}

RFC

str_contains

Breezer picture
2010年12月06日
653

他のユーザーが述べているように、 strposよりも単語の照合に適しているため、正規表現を使用できます。 arestropsチェックは、fare、care、stareなどの文字列に対してもtrueを返します。これらの意図しない一致は、単語の境界を使用することにより、正規表現で簡単に回避できます。

areの単純な一致は、次のようになります。

$a = 'How are you?';

if (preg_match('/\bare\b/', $a)) {
    echo 'true';
}

パフォーマンスの面では、 strposは約3倍高速です。 一度に100万回の比較を行った場合、終了するのにpreg_match 1.5秒かかり、 strpos場合は0.5秒かかりました。

編集:単語ごとだけでなく、文字列の任意の部分を検索するには、次のような正規表現を使用することをお勧めします

$a = 'How are you?';
$search = 'are y';
if(preg_match("/{$search}/i", $a)) {
    echo 'true';
}

正規表現の最後にあるiは、正規表現で大文字と小文字を区別しないように変更します。これを望まない場合は、省略できます。

$ search文字列がサニタイズされていないため、これは非常に問題になる場合があります。つまり、 $searchが追加可能なユーザー入力であるかのように、チェックに合格しない場合があります。いくつかの異なる正規表現のように動作する可能性のある文字列...

また、さまざまな正規表現Regex101の説明をテストおよび表示するための優れたツールがあります。

両方の機能セットを単一の多目的関数(選択可能な大文字と小文字の区別を含む)に組み合わせるには、次のようなものを使用できます。

function FindString($needle,$haystack,$i,$word)
{   // $i should be "" or "i" for case insensitive
    if (strtoupper($word)=="W")
    {   // if $word is "W" then word search instead of string in string search.
        if (preg_match("/\b{$needle}\b/{$i}", $haystack)) 
        {
            return true;
        }
    }
    else
    {
        if(preg_match("/{$needle}/{$i}", $haystack)) 
        {
            return true;
        }
    }
    return false;
    // Put quotes around true and false above to return them as strings instead of as bools/ints.
}

もう1つ覚えておくべきことは、 \bは英語以外の異なる言語では機能しないということです。

これと解決策の説明はここから取られます

\bは、単語の始まりまたは終わりを表します(単語の境界)。 この正規表現は、アップルパイのリンゴと一致しますが、パイナップル、アップルカート、またはベイクアップルのリンゴとは一致しません。

「カフェ」はいかがですか? 正規表現で「カフェ」という単語を抽出するにはどうすればよいですか? 実際には、\ bcafe \ bは機能しません。 どうして? 「cafe」には非ASCII文字が含まれているため:é。 \ bは、समुद्र、감사、месяц、😉などのUnicodeでは単純に使用できません。

Unicode文字を抽出する場合は、単語の境界を表す文字を直接定義する必要があります。

答え: (?<=[\s,.:;"']|^)UNICODE_WORD(?=[\s,.:;"']|$)

したがって、PHPで回答を使用するには、次の関数を使用できます。

function contains($str, array $arr) {
    // Works in Hebrew and any other unicode characters
    // Thanks https://medium.com/@shiba1014/regex-word-boundaries-with-unicode-207794f6e7ed
    // Thanks https://www.phpliveregex.com/
    if (preg_match('/(?<=[\s,.:;"\']|^)' . $word . '(?=[\s,.:;"\']|$)/', $str)) return true;
}

また、単語の配列を検索する場合は、次を使用できます。

function arrayContainsWord($str, array $arr)
{
    foreach ($arr as $word) {
        // Works in Hebrew and any other unicode characters
        // Thanks https://medium.com/@shiba1014/regex-word-boundaries-with-unicode-207794f6e7ed
        // Thanks https://www.phpliveregex.com/
        if (preg_match('/(?<=[\s,.:;"\']|^)' . $word . '(?=[\s,.:;"\']|$)/', $str)) return true;
    }
    return false;
}

PHP 8.0.0以降、str_containsを使用できるようになり

<?php
    if (str_contains('abc', '')) {
        echo "Checking the existence of the empty string will always 
        return true";
    }
ejunker picture
2011年08月19日
274

これは、このような状況で役立つ小さなユーティリティ関数です。

// returns true if $needle is a substring of $haystack
function contains($needle, $haystack)
{
    return strpos($haystack, $needle) !== false;
}
FtDRbwLXw6 picture
2014年09月03日
150

これらの回答のほとんどは、文字列に部分文字列が含まれているかどうかを示しますが、通常、特定の単語を探している場合は、部分文字列ではなく、それは必要ありません。

違いは何ですか? 部分文字列は、他の単語内に表示できます。

  • 「エリア」の先頭にある「あり」
  • 「うさぎ」の最後にある「あれ」
  • 「運賃」の真ん中にある「ある」

これを軽減する1つの方法は、単語の境界\b )と組み合わせた正規表現を使用することです。

function containsWord($str, $word)
{
    return !!preg_match('#\\b' . preg_quote($word, '#') . '\\b#i', $str);
}

このメソッドには、上記と同じ誤検知はありませんが、独自のエッジケースがいくつかあります。 単語の境界は、単語以外の文字( \W )と一致します。これは、 a-zA-Z0-9 、または_以外の文字になります。

  • 「あなたは何を考えていますか?」の「あり」
  • 「loludunno wutそれらのare4?」の「are」

これよりも正確なものが必要な場合は、英語の構文解析を開始する必要があります。これはかなり大きなワームの可能性があります(とにかく、構文の適切な使用を前提としていますが、常に指定されているとは限りません)。

Jose Vega picture
2010年12月06日
138

文字列に別の文字列が含まれているかどうかを判断するには、PHP関数strpos()を使用できます。

int strpos ( string $haystack , mixed $needle [, int $offset = 0 ] )

<?php

$haystack = 'how are you';
$needle = 'are';

if (strpos($haystack,$needle) !== false) {
    echo "$haystack contains $needle";
}

?>

注意:

検索している針が干し草の山の先頭にある場合、位置0が返されます。機能しない、 ==比較を行う場合は、 ===を実行する必要があります。

==記号は比較であり、左側の変数/式/定数が右側の変数/式/定数と同じ値であるかどうかをテストします。

===記号は、2つの変数/式/定数が等しいかどうかを確認するための比較です。 ANDは同じタイプです。つまり、両方が文字列であるか、両方が整数です。

Haim Evgi picture
2010年12月06日
72

strpos()見てください

<?php
    $mystring = 'abc';
    $findme   = 'a';
    $pos = strpos($mystring, $findme);

    // Note our use of ===. Simply, == would not work as expected
    // because the position of 'a' was the 0th (first) character.
    if ($pos === false) {
        echo "The string '$findme' was not found in the string '$mystring'.";
    }
    else {
        echo "The string '$findme' was found in the string '$mystring',";
        echo " and exists at position $pos.";
    }
?>
glutorange picture
2010年12月06日
64

検索で大文字と小文字を区別しない場合は、 strstr()またはstristr()することもできます。

RafaSashi picture
2014年10月16日
52

SamGoodyとLegoStormtrooprのコメントをご覧ください。

複数の単語の近接性/関連性ランク付けするPHPアルゴリズムを探している場合は、PHPのみで検索結果を生成するためのすばやく簡単な方法があります。

strpos()preg_match()strstr()stristr()などの他のブール検索方法に関する問題

  1. 複数の単語を検索できません
  2. 結果はランク付けされていません

ベクトル空間モデルtf-idf(用語頻度-逆ドキュメント頻度)に基づくPHPメソッド

難しいように聞こえますが、驚くほど簡単です。

文字列内の複数の単語を検索する場合、主要な問題は、それぞれに重みを割り当てる方法です。

文字列全体をどの程度代表しているかに基づいて文字列内の用語に重みを付けることができれば、クエリに最も一致するもので結果を並べ替えることができます。

これはベクトル空間モデルの考え方であり、 SQL全文検索の仕組みからそう遠くはあり

function get_corpus_index($corpus = array(), $separator=' ') {

    $dictionary = array();

    $doc_count = array();

    foreach($corpus as $doc_id => $doc) {

        $terms = explode($separator, $doc);

        $doc_count[$doc_id] = count($terms);

        // tf–idf, short for term frequency–inverse document frequency, 
        // according to wikipedia is a numerical statistic that is intended to reflect 
        // how important a word is to a document in a corpus

        foreach($terms as $term) {

            if(!isset($dictionary[$term])) {

                $dictionary[$term] = array('document_frequency' => 0, 'postings' => array());
            }
            if(!isset($dictionary[$term]['postings'][$doc_id])) {

                $dictionary[$term]['document_frequency']++;

                $dictionary[$term]['postings'][$doc_id] = array('term_frequency' => 0);
            }

            $dictionary[$term]['postings'][$doc_id]['term_frequency']++;
        }

        //from http://phpir.com/simple-search-the-vector-space-model/

    }

    return array('doc_count' => $doc_count, 'dictionary' => $dictionary);
}

function get_similar_documents($query='', $corpus=array(), $separator=' '){

    $similar_documents=array();

    if($query!=''&&!empty($corpus)){

        $words=explode($separator,$query);

        $corpus=get_corpus_index($corpus, $separator);

        $doc_count=count($corpus['doc_count']);

        foreach($words as $word) {

            if(isset($corpus['dictionary'][$word])){

                $entry = $corpus['dictionary'][$word];


                foreach($entry['postings'] as $doc_id => $posting) {

                    //get term frequency–inverse document frequency
                    $score=$posting['term_frequency'] * log($doc_count + 1 / $entry['document_frequency'] + 1, 2);

                    if(isset($similar_documents[$doc_id])){

                        $similar_documents[$doc_id]+=$score;

                    }
                    else{

                        $similar_documents[$doc_id]=$score;

                    }
                }
            }
        }

        // length normalise
        foreach($similar_documents as $doc_id => $score) {

            $similar_documents[$doc_id] = $score/$corpus['doc_count'][$doc_id];

        }

        // sort from  high to low

        arsort($similar_documents);

    }   

    return $similar_documents;
}

ケース1

$query = 'are';

$corpus = array(
    1 => 'How are you?',
);

$match_results=get_similar_documents($query,$corpus);
echo '<pre>';
    print_r($match_results);
echo '</pre>';

結果

Array
(
    [1] => 0.52832083357372
)

ケース2

$query = 'are';

$corpus = array(
    1 => 'how are you today?',
    2 => 'how do you do',
    3 => 'here you are! how are you? Are we done yet?'
);

$match_results=get_similar_documents($query,$corpus);
echo '<pre>';
    print_r($match_results);
echo '</pre>';

結果

Array
(
    [1] => 0.54248125036058
    [3] => 0.21699250014423
)

ケース3

$query = 'we are done';

$corpus = array(
    1 => 'how are you today?',
    2 => 'how do you do',
    3 => 'here you are! how are you? Are we done yet?'
);

$match_results=get_similar_documents($query,$corpus);
echo '<pre>';
    print_r($match_results);
echo '</pre>';

結果

Array
(
    [3] => 0.6813781191217
    [1] => 0.54248125036058
)

行うべき改善点はたくさんありますが、このモデルは、 strpos()preg_match()strstr()などのブール演算子を持たない自然なクエリから良好な結果を得る方法を提供します。 stristr()

NOTABENE

オプションで、単語を検索する前に冗長性を排除します

  • これにより、インデックスサイズが削減され、必要なストレージが少なくなります。

  • より少ないディスクI / O

  • より高速なインデックス作成とその結果としてのより高速な検索。

1.正規化

  • すべてのテキストを小文字に変換する

2.ストップワードの削除

  • 本当の意味を持たない単語をテキストから削除します(「and」、「or」、「the」、「for」など)。

3.辞書の置換

  • 単語を同一または類似の意味を持つ他の単語に置き換えます。 (例:「hungrily」と「hungry」のインスタンスを「hunger」に置き換えます)

  • 単語を本質的な意味にさらに減らすために、さらなるアルゴリズム的手段(スノーボール)が実行され得る。

  • 色名を16進数に置き換えたもの

  • 精度を下げることによる数値の削減は、テキストを正規化する他の方法です。

リソース

Shankar Damodaran picture
2013年10月24日
46

stripos()を使用した大文字と小文字を区別しないマッチングを利用します。

if (stripos($string,$stringToSearch) !== false) {
    echo 'true';
}
Alan Piralla picture
2013年07月09日
41

「falsey」および「truthy」の問題を回避したい場合は、substr_countを使用できます。

if (substr_count($a, 'are') > 0) {
    echo "at least one 'are' is present!";
}

strposより少し遅いですが、比較の問題を回避します。