文字列内の文字の出現数を数えるにはどうすればよいですか?


547

私はひもを持っています

a.b.c.d

「。」の出現回数を数えたい 慣用的な方法で、できればワンライナー。

(以前は、誰もがループを使用せずに答えようとしているのか疑問に思っている場合に備えて、この制約を「ループなし」と表現していました)。


1
宿題?それ以外の場合は、ループを回避するための要件が​​表示されないためです。
PhiLho 2008年

22
慣用的なワンライナーを探すほどループを嫌わない。
バート

2
ループはこのような問題のために作成されました。ループを一般的なユーティリティクラスに記述して、新しく作成した1つのライナーを呼び出します。
che javara

文字列の類似の質問:stackoverflow.com/questions/767759/...
koppor

指摘するだけです-ワンライナーを見つけることに感謝します。楽しくて(真の利点として)覚えやすいことが多いのですが、個別のメソッドとループの方がほぼすべての点で優れていることを指摘したいと思います-読みやすさ、さらにはパフォーマンス。以下の「エレガントな」ソリューションのほとんどは、文字列の再構成やメモリのコピーを伴うため、パフォーマンスがあまりよくありません。一方、文字列をスキャンして出現回数をカウントするだけのループは高速でシンプルです。そのパフォーマンスは一般的に要因ではありませんが、ループ全体の1行を見て、パフォーマンスが向上すると仮定しないでください。
ビルK

回答:


722

これに対する私の「慣用的なワンライナー」は次のとおりです。

int count = StringUtils.countMatches("a.b.c.d", ".");

すでにcommons langになっているのに、なぜ自分で書くのですか?

これに対するSpring Frameworkのワンライナーは次のとおりです。

int occurance = StringUtils.countOccurrencesOf("a.b.c.d", ".");

44
グアバ相当:int count = CharMatcher.is('.').countIn("a.b.c.d");... 重複質問でdogbaneが回答したとおり
Jonik 2013

25
私はこれに反対票を投じませんが、(a)サードパーティのライブラリが必要であり、(b)高価です。
javadba 2014年

これは春のフレームワークでのみ機能し、インポートする必要があります。
Isuru Madusanka

1
誰かがそれを必要とする場合:grepcode.com/file/repo1.maven.org/maven2/commons-lang/...
CV2

19
私が働いたすべての会社で、費用がかかっていたのは、多くの不適切に記述され、保守が不十分な「* Utils」クラスを使用していることです。あなたの仕事の一部は、Apache Commonsで何が利用可能かを知ることです。
AbuNassar 2016年

1016

これはどう。それは下で正規表現を使用しないので、他のソリューションのいくつかよりも速く、ループを使用しません。

int count = line.length() - line.replace(".", "").length();

122
最も簡単な方法。賢い。そして、それはStringUtilsクラスのないAndroidで動作します
Jose_GD

43
これが最良の答えです。それが最良の理由は、別のライブラリをインポートする必要がないためです。
Alex Spencer

27
非常に実用的ですが、地獄のように醜いです。混乱を招くコードにつながるため、お勧めしません。
ダニエルサン

32
醜いコードは、独自の「StringUtils」クラスのメソッドにすることで最小限に抑えることができます。その場合、醜いコードは1か所にあり、他の場所はすべて読みやすくなっています。
RonR 2014年

30
ループ方式はこれよりはるかに高速です。特に、Stringではなくcharをカウントしたい場合(String.replace(char、char)メソッドがないため)。15文字の文字列では、6049 nsと26,739 ns(100ランでの平均)の差が出ます。生の数は大きな違いですが、パーチェテージは賢明です... メモリの割り当てを避けてください-ループを使用してください!
ベン

282

他の回答と、ワンライナーを使用してこれを行うすべての方法を私が知っていることを要約します。

   String testString = "a.b.c.d";

1)Apache Commonsの使用

int apache = StringUtils.countMatches(testString, ".");
System.out.println("apache = " + apache);

2)Spring Frameworkの使用

int spring = org.springframework.util.StringUtils.countOccurrencesOf(testString, ".");
System.out.println("spring = " + spring);

3)置換の使用

int replace = testString.length() - testString.replace(".", "").length();
System.out.println("replace = " + replace);

4)replaceAllを使用する(ケース1)

int replaceAll = testString.replaceAll("[^.]", "").length();
System.out.println("replaceAll = " + replaceAll);

5)replaceAllを使用する(ケース2)

int replaceAllCase2 = testString.length() - testString.replaceAll("\\.", "").length();
System.out.println("replaceAll (second case) = " + replaceAllCase2);

6)分割を使用する

int split = testString.split("\\.",-1).length-1;
System.out.println("split = " + split);

7)Java8の使用(ケース1)

long java8 = testString.chars().filter(ch -> ch =='.').count();
System.out.println("java8 = " + java8);

8)Java8(ケース2 )を使用すると、ケース1よりもユニコードの方が良い場合があります

long java8Case2 = testString.codePoints().filter(ch -> ch =='.').count();
System.out.println("java8 (second case) = " + java8Case2);

9)StringTokenizerの使用

int stringTokenizer = new StringTokenizer(" " +testString + " ", ".").countTokens()-1;
System.out.println("stringTokenizer = " + stringTokenizer);

コメントから:StringTokenizerには注意してください。abcdの場合は機能しますが、a ... bc ... dまたは... abcdまたはa .... b ...... c ..... dの場合は注意してください。 ...などは動作しません。それだけでカウントされます。キャラクター間で一度だけ

githubの詳細

パフォーマンステストJMHを使用、モード= AverageTime、0.010より良いスコア0.351):

Benchmark              Mode  Cnt  Score    Error  Units
1. countMatches        avgt    5  0.010 ±  0.001  us/op
2. countOccurrencesOf  avgt    5  0.010 ±  0.001  us/op
3. stringTokenizer     avgt    5  0.028 ±  0.002  us/op
4. java8_1             avgt    5  0.077 ±  0.005  us/op
5. java8_2             avgt    5  0.078 ±  0.003  us/op
6. split               avgt    5  0.137 ±  0.009  us/op
7. replaceAll_2        avgt    5  0.302 ±  0.047  us/op
8. replace             avgt    5  0.303 ±  0.034  us/op
9. replaceAll_1        avgt    5  0.351 ±  0.045  us/op

印刷された文字列は上記のものと一致せず、順序が最初に速いため、少なくともルックアップがトリッキーになります。そうでなければいい答えです!
Maarten Bodewes 2017年

ケース2、複数のUTF-16コード単位を必要とするコードポイント向けに一般化:"1🚲2🚲3 has 2".codePoints().filter((c) -> c == "🚲".codePointAt(0)).count()
Tom Blodget

174

遅かれ早かれ、何かがループする必要があります。(非常に単純な)ループを記述するsplit方が、必要以上に強力なものを使用するよりもはるかに簡単です。

必ず別の方法でループをカプセル化してください。

public static int countOccurrences(String haystack, char needle)
{
    int count = 0;
    for (int i=0; i < haystack.length(); i++)
    {
        if (haystack.charAt(i) == needle)
        {
             count++;
        }
    }
    return count;
}

そうすれば、メインコードにループを含める必要はありませんが、ループはどこかにある必要があります。


5
(int i = 0、l = haystack.length(); i <l; i ++)はスタックにやさしい
Chris

12
(私はコメントの「スタック」ビットがどこから来たのかさえわかりません。この回答が実際にスタックに対して厄介なものであるようではありません。)
Jon Skeet

2
それだけでなく、これはおそらくjitが何をするのかを見ずに反最適化であるかもしれません。たとえば、配列forループで上記を実行すると、状況がさらに悪化する可能性があります。
ShuggyCoUk 2009年

4
@sulai:Chrisの懸念は、些細な JIT最適化に直面して、根拠のないIMO です。3年後の今、コメントがあなたの注意を引いた理由はありますか?ただ興味があります。
Jon Skeet

1
おそらく、@ sulaiが私と同じように(Javaにこのための組み込みメソッドがあるかどうか疑問に思っているときに)問題に遭遇し、日付に気づきませんでした。ただし、@ ShuggyCoUkがコメントをいくつか挙げているように、length()呼び出しをループの外に移動するとパフォーマンスが低下する可能性があることに興味があります。
JKillian 2014

63

Mladenに似たアイデアがありましたが、その逆です...

String s = "a.b.c.d";
int charCount = s.replaceAll("[^.]", "").length();
println(charCount);

正しい。ReplaceAll( "。")は、ドットだけでなく任意の文字を置き換えます。ReplaceAll( "\\。")はうまくいきました。ソリューションはより簡単です。
VonC 2008年

jjnguyは実際に、私の「abcd」.split( "\\。")。length-1ソリューションを見て、最初にreplaceAll( "[^。]")を提案していました。しかし、5回ヒットした後、私は自分の回答(および彼のコメント)を削除しました。
VonC 2008年

「... 2つの問題が発生しました」(当然です)とにかく、とで実行されreplaceAll()length()いるループが数十あると思います。まあ、それが見えなければ、それは存在しません; o)
Piskvorは

2
正規表現を使用して、カウント用の新しい文字列を作成するのは良い考えではないと思います。文字列のすべての文字をループして数を数える静的メソッドを作成するだけです。
mingfai

1
@mingfai:確かに、元の質問は、1行でループなしで作成することです(1行でループを作成できますが、醜くなります)。答えではなく質問をしてください... :-)
PhiLho

37
String s = "a.b.c.d";
int charCount = s.length() - s.replaceAll("\\.", "").length();

ReplaceAll( "。")はすべての文字を置き換えます。

PhiLhoのソリューションでは、ReplaceAll( "[^。]"、 "")を使用します。これは、[。]が「任意の文字」ではなく「ドット」を表すため、エスケープする必要はありません。


私はこれが好きです。もちろん、ループはまだあります。
典型的なポール、

注意:長さが1
より大きい

30

私の「慣用的なワンライナー」ソリューション:

int count = "a.b.c.d".length() - "a.b.c.d".replace(".", "").length();

StringUtilsを使用するソリューションが受け入れられる理由がわかりません。


4
この投稿には、これに似た古いソリューションがあります。
JCalcines、2014

7
このソリューションは本当に非効率的であるので
アンドラーシュ

これにより、カウントを生成するためだけに追加の文字列が作成されます。StringUtilsがオプションである場合、なぜStringUtilsよりもこれを好むのかはわかりません。オプションでない場合は、ユーティリティクラスで単純なforループを作成するだけです。
2016年

28
String s = "a.b.c.d";
long result = s.chars().filter(ch -> ch == '.').count();

1
+ネイティブソリューションに投票します。
Scadge 2016年

24

短い例は

String text = "a.b.c.d";
int count = text.split("\\.",-1).length-1;

3
これはオーバーヘッドが比較的大きいようです。小さな文字列が大量に作成される可能性があることに注意してください。通常、それは大した問題ではありませんが、注意して使用してください。
Maarten Bodewes 2014

19

ここにループのない解決策があります:

public static int countOccurrences(String haystack, char needle, int i){
    return ((i=haystack.indexOf(needle, i)) == -1)?0:1+countOccurrences(haystack, needle, i+1);}


System.out.println("num of dots is "+countOccurrences("a.b.c.d",'.',0));

まあ、ループがありますが、それは見えません :-)

-よなたん


2
文字列が長すぎない限り、OutOfMemoryErrorが発生します。
Spencer Kormos 2008年

問題は宿題になるほど不自然に聞こえますが、そうであれば、この再帰はおそらくあなたが求められている答えです。
エリクソン08年

これはindexOfを使用します。これはループしますが、良いアイデアです。本当に「ただ再帰的な」ソリューションを1分で投稿...
Jon Skeet

使用可能なスタックスロットよりも多く発生する場合は、スタックオーバーフロー例外が発生します;)
Luca C. 14年

15

この目的のために新しい文字列を割り当てるという考えは好きではありません。また、文字列はその値を格納する後ろにすでにchar配列を持っているため、String.charAt()は事実上無料です。

for(int i=0;i<s.length();num+=(s.charAt(i++)==delim?1:0))

J2SEのみで、収集を必要とする追加の割り当てなしで、1行以下でトリックを実行します。


弦を1回通過するのはこの1つだけなので、この1つを愛する。私はパフォーマンスを気にします。
javadba 2014年

1
charAt文字ではなく16ビットのコードポイントを反復処理します。charJavaのA は文字ではありません。したがって、この答えは、サロゲートがのコードポイントに等しいUnicodeシンボルがあってはならないことを意味しdelimます。ドットに対して正しいかどうかはわかりませんが、一般的には正しくない可能性があります。
14

14

さて、Yonatanのソリューションに触発され、ここにある一つだ純粋に再帰-使用のみのライブラリの方法があるlength()charAt()、どちらも任意のループ操作を行います。

public static int countOccurrences(String haystack, char needle)
{
    return countOccurrences(haystack, needle, 0);
}

private static int countOccurrences(String haystack, char needle, int index)
{
    if (index >= haystack.length())
    {
        return 0;
    }

    int contribution = haystack.charAt(index) == needle ? 1 : 0;
    return contribution + countOccurrences(haystack, needle, index+1);
}

再帰がループとしてカウントされるかどうかは、使用する正確な定義に依存しますが、それはおそらくあなたが得るものと同じくらい近いです。

最近、ほとんどのJVMが末尾再帰を実行するかどうかはわかりませんが、そうでない場合は、当然のことながら、適切に長い文字列に対して、代名詞のスタックオーバーフローが発生します。


いいえ、末尾再帰はおそらくJava 7にありますが、まだ広くは普及していません。この単純な直接テール再帰は、コンパイル時にループに変換される可能性がありますが、実際にはJava 7がJVMに組み込まれており、さまざまなメソッドを介したチェーンを処理します。
エリクソン2008年

3
加算を実行した結果を返すのではなく、メソッドがそれ自体への呼び出し(実行中の合計パラメーターを含む)を返した場合、末尾再帰が発生する可能性が高くなります。
スティーブンデンヌ

12

スタックを爆破しない非ループバージョンのJon Skeetに触発されました。また、fork-joinフレームワークを使用する場合の出発点としても役立ちます。

public static int countOccurrences(CharSequeunce haystack, char needle) {
    return countOccurrences(haystack, needle, 0, haystack.length);
}

// Alternatively String.substring/subsequence use to be relatively efficient
//   on most Java library implementations, but isn't any more [2013].
private static int countOccurrences(
    CharSequence haystack, char needle, int start, int end
) {
    if (start == end) {
        return 0;
    } else if (start+1 == end) {
        return haystack.charAt(start) == needle ? 1 : 0;
    } else {
        int mid = (end+start)>>>1; // Watch for integer overflow...
        return
            countOccurrences(haystack, needle, start, mid) +
            countOccurrences(haystack, needle, mid, end);
    }
}

(免責事項:テストされていない、コンパイルされていない、賢明ではない。)

おそらくそれを書くための最良の(シングルスレッド、サロゲートペアのサポートなし)方法:

public static int countOccurrences(String haystack, char needle) {
    int count = 0;
    for (char c : haystack.toCharArray()) {
        if (c == needle) {
           ++count;
        }
    }
    return count;
}

11

これの効率についてはわかりませんが、サードパーティのライブラリを導入せずに書ける最短のコードです。

public static int numberOf(String target, String content)
{
    return (content.split(target).length - 1);
}

4
文字列の最後の出現もカウントするには、次のような負の制限引数を指定してsplitを呼び出す必要がありますreturn (content.split(target, -1).length - 1);。デフォルトでは、split()の結果の配列では、文字列の最後の出現は省略されます。Dokuを
vlz

10

ストリームを使用してこれを実現することもできます。明らかに裏でイテレーションがありますが、明示的に記述する必要はありません。

public static long countOccurences(String s, char c){
    return s.chars().filter(ch -> ch == c).count();
}

countOccurences("a.b.c.d", '.'); //3
countOccurences("hello world", 'l'); //3

.codePoints()代わりに.chars()を使用すると、任意のUnicode値(サロゲートペアが必要なものを含む)がサポートされます
Luke Usherwood

10

この問題を解決するためにJava 8でreduceを使用することもできます。

int res = "abdsd3$asda$asasdd$sadas".chars().reduce(0, (a, c) -> a + (c == '$' ? 1 : 0));
System.out.println(res);

出力:

3

8

完全なサンプル:

public class CharacterCounter
{

  public static int countOccurrences(String find, String string)
  {
    int count = 0;
    int indexOf = 0;

    while (indexOf > -1)
    {
      indexOf = string.indexOf(find, indexOf + 1);
      if (indexOf > -1)
        count++;
    }

    return count;
  }
}

コール:

int occurrences = CharacterCounter.countOccurrences("l", "Hello World.");
System.out.println(occurrences); // 3

間違ったコードは、intオカレンスを試行すると機能しません= CharacterCounter.countOccurrences( "1"、 "101"); System.out.println(occurrences); // 1
jayesh 2014

同じロジックで動作するコードの修正をコミットします
MaanooAk 2017

8

答えを得る最も簡単な方法は次のとおりです。

public static void main(String[] args) {
    String string = "a.b.c.d";
    String []splitArray = string.split("\\.",-1);
    System.out.println("No of . chars is : " + (splitArray.length-1));
}

2
このスニペットは、特定の入力「abc」に対して正しいドット数を返しません
dekaru

@dekaruコメントにコメントを貼り付けてください。
Amar Magar

5

Springフレームワークを使用している場合は、「StringUtils」クラスも使用できます。メソッドは「countOccurrencesOf」になります。


5

split()関数は1行のコードで使用できます

int noOccurence=string.split("#",-1).length-1;

Splitは実際には文字列の配列を作成しますが、これは多くの時間を消費します。
Palec、2016年

あなたは正しい、それは本当の懸念です。別の方法では、プロジェクトにサードパーティのlibを組み込むことを回避します(まだ行われていない場合)。それはあなたが何をしたいか、そしてパフォーマンスの期待が何であるかに依存します。
Benj

3
このlimitオーバーロードされた分割メソッド呼び出しで引数がゼロに設定されているため、このソリューションには後続の空のヒットは含まれません。例:サイズ9()ではなく"1##2#3#####".split("#")、サイズ4([0:"1";1:""; 2:"2"; 3:"3"])の配列のみを生成し[0:"1"; 1:""; 2:"2"; 3:"3"; 4:""; 5:""; 6:""; 7:""; 8:""]ます。
klaar

4
public static int countOccurrences(String container, String content){
    int lastIndex, currIndex = 0, occurrences = 0;
    while(true) {
        lastIndex = container.indexOf(content, currIndex);
        if(lastIndex == -1) {
            break;
        }
        currIndex = lastIndex + content.length();
        occurrences++;
    }
    return occurrences;
}

4
import java.util.Scanner;

class apples {

    public static void main(String args[]) {    
        Scanner bucky = new Scanner(System.in);
        String hello = bucky.nextLine();
        int charCount = hello.length() - hello.replaceAll("e", "").length();
        System.out.println(charCount);
    }
}//      COUNTS NUMBER OF "e" CHAR´s within any string input

3

メソッドはそれを隠すことができますが、ループ(または再帰)なしでカウントする方法はありません。ただし、パフォーマンス上の理由からchar []を使用したい場合。

public static int count( final String s, final char c ) {
  final char[] chars = s.toCharArray();
  int count = 0;
  for(int i=0; i<chars.length; i++) {
    if (chars[i] == c) {
      count++;
    }
  }
  return count;
}

replaceAll(つまりRE)を使用するのは、最良の方法とは思えません。


これが最もエレガントなソリューションだと思います。直接charAtではなくtoCharArrayを使用したのはなぜですか?
Panayotis 2017年

charAtを使用したループは、少なくとも遅くなりました。プラットフォームにも依存する可能性があります。実際に見つける唯一の方法は、違いを測定することです。
tcurdt 2017年

3

まあ、非常によく似たタスクで、このスレッドに出くわしました。プログラミング言語の制限はありませんでした。groovyはjava vmで実行されているため、次のようにしてGroovyを使用して問題を解決できました。

"a.b.c.".count(".")

完了しました。


3

より簡単な解決策は、一致させる文字に基づいて文字列を分割することです。

例えば、

int getOccurences(String characters, String string) { String[] words = string.split(characters); return words.length - 1; }

これは、次の場合に4を返します。 getOccurences("o", "something about a quick brown fox");


ここでの問題は、配列を割り当てる必要があることです。これは非常に低速です。
Palec

2

コードのどこかで、何かがループしなければなりません。これを回避する唯一の方法は、ループを完全に展開することです。

int numDots = 0;
if (s.charAt(0) == '.') {
    numDots++;
}

if (s.charAt(1) == '.') {
    numDots++;
}


if (s.charAt(2) == '.') {
    numDots++;
}

...など、ただし、ソースエディターで手動でループを実行するのは、それを実行するコンピューターではありません。擬似コードを参照してください:

create a project
position = 0
while (not end of string) {
    write check for character at position "position" (see above)
}
write code to output variable "numDots"
compile program
hand in homework
do not think of the loop that your "if"s may have been optimized and compiled to

2

これは少し異なるスタイルの再帰ソリューションです:

public static int countOccurrences(String haystack, char needle)
{
    return countOccurrences(haystack, needle, 0);
}

private static int countOccurrences(String haystack, char needle, int accumulator)
{
    if (haystack.length() == 0) return accumulator;
    return countOccurrences(haystack.substring(1), needle, haystack.charAt(0) == needle ? accumulator + 1 : accumulator);
}

2

なぜ文字で分割してから、結果の配列の長さを取得しないのですか?配列の長さは常にインスタンスの数+ 1になります。


2

次のソースコードは、ユーザーが入力した単語内の特定の文字列の出現回数を示します。

import java.util.Scanner;

public class CountingOccurences {

    public static void main(String[] args) {

        Scanner inp= new Scanner(System.in);
        String str;
        char ch;
        int count=0;

        System.out.println("Enter the string:");
        str=inp.nextLine();

        while(str.length()>0)
        {
            ch=str.charAt(0);
            int i=0;

            while(str.charAt(i)==ch)
            {
                count =count+i;
                i++;
            }

            str.substring(count);
            System.out.println(ch);
            System.out.println(count);
        }

    }
}

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.