web-dev-qa-db-ja.com

区切り文字を使用して文字列を結合する

文字列のリストを結合された区切り文字列に結合する最良の方法は何ですか。デリミタの追加をいつ停止するかについて主に心配しています。私の例ではC#を使用しますが、これは言語にとらわれないようにしたいと思います。

編集:コードを少し単純にするためにStringBuilderを使用していません。

Forループを使用

for(int i=0; i < list.Length; i++)
{
    result += list[i];
    if(i != list.Length - 1)
        result += delimiter;
}

Forループを使用して、前の最初の項目を設定

result = list[0];
for(int i = 1; i < list.Length; i++)
    result += delimiter + list[i];

これらは、リストの長さが事前にわからないIEnumerableでは機能しません。

foreachループの使用

bool first = true;
foreach(string item in list)
{
    if(!first)
        result += delimiter;
    result += item;
    first = false;
}

foreachループのバリエーション

ジョンのソリューションから

StringBuilder builder = new StringBuilder();
string delimiter = "";
foreach (string item in list)
{
    builder.Append(delimiter);
    builder.Append(item);
    delimiter = ",";       
}
return builder.ToString();

イテレーターの使用

再びジョンから

using (IEnumerator<string> iterator = list.GetEnumerator())
{
    if (!iterator.MoveNext())
        return "";
    StringBuilder builder = new StringBuilder(iterator.Current);
    while (iterator.MoveNext())
    {
        builder.Append(delimiter);
        builder.Append(iterator.Current);
    }
    return builder.ToString();
}

他にどんなアルゴリズムがありますか?

23
John Oxley

言語やプラットフォームによって文字列の処理方法が異なり、文字列のリストを結合するためのさまざまなレベルの組み込みサポートが提供されるため、ここでは本当に言語にとらわれない答えを出すことは不可能です。 2つの異なる言語で同一のコードをほとんどとることができます。

C#では、次を使用できます。

StringBuilder builder = new StringBuilder();
string delimiter = "";
foreach (string item in list)
{
    builder.Append(delimiter);
    builder.Append(item);
    delimiter = ",";       
}
return builder.ToString();

これは、最初の項目以外のすべてにカンマをprependします。同様のコードは、Java=でも有効です。

編集:これは代替案です。イアンの後半の回答に少し似ていますが、一般的なIEnumerable<string>に取り組んでいます。

// Change to IEnumerator for the non-generic IEnumerable
using (IEnumerator<string> iterator = list.GetEnumerator())
{
    if (!iterator.MoveNext())
    {
        return "";
    }
    StringBuilder builder = new StringBuilder(iterator.Current);
    while (iterator.MoveNext())
    {
        builder.Append(delimiter);
        builder.Append(iterator.Current);
    }
    return builder.ToString();
}

元の回答からほぼ5年後に編集...

.NET 4では、 string.Join が大幅にオーバーロードされました。自動的にToStringを呼び出すIEnumerable<T>を取得するオーバーロードがあり、IEnumerable<string>にはオーバーロードがあります。したがって、上記のコードはもう必要ありません。NETの場合はとにかく。

36
Jon Skeet

.NETでは、 String.Joinメソッド を使用できます。

string concatenated = String.Join(",", list.ToArray());

。NET Reflector を使用して、それがどのように行われるかを確認できます。

public static unsafe string Join(string separator, string[] value, int startIndex, int count)
{
    if (separator == null)
    {
        separator = Empty;
    }
    if (value == null)
    {
        throw new ArgumentNullException("value");
    }
    if (startIndex < 0)
    {
        throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_StartIndex"));
    }
    if (count < 0)
    {
        throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NegativeCount"));
    }
    if (startIndex > (value.Length - count))
    {
        throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_IndexCountBuffer"));
    }
    if (count == 0)
    {
        return Empty;
    }
    int length = 0;
    int num2 = (startIndex + count) - 1;
    for (int i = startIndex; i <= num2; i++)
    {
        if (value[i] != null)
        {
            length += value[i].Length;
        }
    }
    length += (count - 1) * separator.Length;
    if ((length < 0) || ((length + 1) < 0))
    {
        throw new OutOfMemoryException();
    }
    if (length == 0)
    {
        return Empty;
    }
    string str = FastAllocateString(length);
    fixed (char* chRef = &str.m_firstChar)
    {
        UnSafeCharBuffer buffer = new UnSafeCharBuffer(chRef, length);
        buffer.AppendString(value[startIndex]);
        for (int j = startIndex + 1; j <= num2; j++)
        {
            buffer.AppendString(separator);
            buffer.AppendString(value[j]);
        }
    }
    return str;
}
22
Ian Nelson

一部の言語がこのサポートを1行で提供する場合、言語に依存しないようにする理由はほとんどありません。たとえば、Pythonの

",".join(sequence)

詳細については、 結合ドキュメント を参照してください。

8
Hank Gay

pythonの場合、文字列のリストがあることを確認してください。そうでない場合、 '、'。join(x)は失敗します。2.5+を使用する安全な方法の場合

delimiter = '","'
delimiter.join(str(a) if a else '' for a in list_object)

"str(a)if a else ''"はNone型に適しています。そうでない場合、str()は、Niceではない 'None'を作成します;)

5
Christian Witts

PHPの implode()

$string = implode($delim, $array);
5
Jeremy L

これを再帰的に表現します。

  • 文字列引数の数が1かどうかを確認します。1の場合は、それを返します。
  • それ以外の場合は再帰しますが、最初の2つの引数をそれらの間の区切り文字と組み合わせます。

一般的なLISPの例:

(defun join(delimiter&rest strings)
(if(null(rest strings))
(first strings)
(apply# 'join 
 delimiter 
(concatenate 'string 
(最初の文字列)
 delimiter 
(2番目の文字列))
(cddr文字列))))

より慣用的な方法はreduceを使用することですが、これは上記とほぼ同じ命令に拡張されます。

(defun join(delimiter&rest strings)
(reduce(lambda(ab)
(concatenate 'string a delimiter b))
 strings))
4
Svante

デリミタは常に追加し、必要に応じて最後に削除します。この方法では、作業を1回だけ行うことに関心がある場合、ループのすべての反復でifステートメントを実行しません。

StringBuilder sb = new StringBuilder();

foreach(string item in list){
    sb.Append(item);
    sb.Append(delimeter);
}

if (list.Count > 0) {
    sb.Remove(sb.Length - delimter.Length, delimeter.Length)
}
4
List<string> aaa = new List<string>{ "aaa", "bbb", "ccc" };
string mm = ";";
return aaa.Aggregate((a, b) => a + mm + b);

そしてあなたは得る

aaa;bbb;ccc

ラムダはかなり便利です

4
eponie

問題は、コンピューター言語に文字列ブール値、つまり文字列型のメソッドをほとんど使用しないことです。 SQL Serverには少なくともis [not] nullとnullifがあり、これらを組み合わせると、区切り記号の問題が次のように解決されます。isnotnull(nullif(columnvalue、 "")、 "、" + columnvalue))

問題は、言語にはブール値と文字列があり、醜いコーディング形式を除いてトウェインが出会うことはないということです。

concatstring = string1 + "、" + string2; if(fubar)concatstring + = string3 concatstring + = string4など

私はこの醜さをすべて回避するために力を尽くし、コンマゲームをプレイし、結合と連結しましたが、コンマの1つを逃し、変数が空である場合のSQL Serverエラーを含め、まだいくつか残っています。

ジョナサン

3

C#では String.Join(separator、string_list) を使用できます

2
vartec

Java 8では、次のように使用できます:

List<String> list = Arrays.asList(new String[] { "a", "b", "c" });
System.out.println(String.join(",", list)); //Output: a,b,c

プレフィックスとサフィックスを付けるには

StringJoiner joiner = new StringJoiner(",", "{", "}");
list.forEach(x -> joiner.add(x));
System.out.println(joiner.toString()); //Output: {a,b,c}

Java 8の前に、ジョンの答えのように行うことができます

StringBuilder sb = new StringBuilder(prefix);
boolean and = false;
for (E e : iterable) {        
    if (and) {
        sb.append(delimiter);
    }
    sb.append(e);
    and = true;
}
sb.append(suffix);
1
gladiator

この言語に依存しないタグを付けたので、

これはあなたがそれをPythonで行う方法です

# delimiter can be multichar like "| trlalala |"
delimiter = ";"
# sequence can be any list, or iterator/generator that returns list of strings
result = delimiter.join(sequence)
#result will NOT have ending delimiter 

編集:何人かの人が答えに打ちのめされたようです。申し訳ありません

1
Luka Marinko

これはC#の実用的なソリューションであり、Javaではイテレータのそれぞれに対して同様に使用できます。

        string result = string.Empty; 

        // use stringbuilder at some stage.
        foreach (string item in list)
            result += "," + item ;

        result = result.Substring(1);
        // output:  "item,item,item"

.NETを使用している場合は、拡張メソッドを使用してlist.ToString( "、")詳細を確認できます- 配列、リスト、辞書、汎用IEnumerableの区切り記号で区切られたToString

// contains extension methods, it must be a static class.
public static class ExtensionMethod
{
    // apply this extension to any generic IEnumerable object.
    public static string ToString<T>(this IEnumerable<T> source,
      string separator)
    {
        if (source == null)
           throw new ArgumentException("source can not be null.");

        if (string.IsNullOrEmpty(separator))
           throw new ArgumentException("separator can not be null or empty.");

        // A LINQ query to call ToString on each elements
        // and constructs a string array.
        string[] array =
         (from s in source
          select s.ToString()
          ).ToArray();

        // utilise builtin string.Join to concate elements with
        // customizable separator.
        return string.Join(separator, array);
    }
}

編集:パフォーマンス上の理由から、連結コードをこのスレッド内で言及されている文字列ビルダーソリューションに置き換えます。

1
Ray Lu

Pythonの回答は3回のように見えますが、Rubyはありませんか?!?!?

コードの最初の部分は新しい配列を宣言しています。次に、.join()メソッドを呼び出して区切り文字を渡すだけで、途中に区切り文字がある文字列が返されます。 joinメソッドは、連結する前に各項目の.to_sメソッドを呼び出すと思います。

["ID", "Description", "Active"].join(",")
>> "ID, Description, Active"

これは、メタプログラミングとデータベースの相互作用を組み合わせるときに非常に役立ちます。

c#にこの構文砂糖に似たものがあるかどうか誰かが知っていますか?

1
taelor

私はそのようなことをするための最良の方法を間引きします(私は疑似コードを使用するので、それを本当に言語にとらわれないようにします):

_function concat(<array> list, <boolean> strict):
  for i in list:
    if the length of i is zero and strict is false:
      continue;
    if i is not the first element:
      result = result + separator;
    result = result + i;
  return result;
_

concat()の2番目の引数、strictは、最終的に空の文字列を連結で考慮する必要があるかどうかを知るためのフラグです。

私は最後のセパレーターを追加することを考慮していませんでした。一方、strictがfalseの場合、区切り文字がカンマである場合、結果の文字列には「A、B , F」などが含まれない可能性がありますが、代わりに「A、B、F」として表示されます。

1
Federico Zancan

Javaの場合、この question または question で非常に完全な回答が得られます。

それはApache CommonsでStringUtils.joinを使用することです

String result = StringUtils.join(list, ", ");
0
Ethan Heilman

Java(ジョンのソリューションから):

    StringBuilder sb = new StringBuilder();
    String delimiter = "";
    for (String item : items) {
        sb.append(delimiter).append(item);
        delimeter = ", ";
    }
    return sb.toString();
0
ceklock
string result = "";
foreach(string item in list)
{
    result += delimiter + item;
}
result = result.Substring(1);

編集:もちろん、これまたはアルゴリズムのいずれかを使用して文字列を連結することはできません。 C#/。NETでは、おそらくStringBuilderを使用します。

StringBuilder sb = new StringBuilder();
foreach(string item in list)
{
    sb.Append(delimiter);
    sb.Append(item);
}
string result = sb.ToString(1, sb.Length-1);

そして、このソリューションのバリエーション:

StringBuilder sb = new StringBuilder(list[0]);
for (int i=1; i<list.Count; i++)
{
    sb.Append(delimiter);
    sb.Append(list[i]);
}
string result = sb.ToString();

どちらのソリューションにもエラーチェックは含まれていません。

0
M4N

これがpythonが問題を解決する方法です:

','.join(list_of_strings)

些細な場合でも「アルゴリズム」の必要性を理解できなかった

0
SilentGhost

文字列が空でない場合に限り、区切り文字を追加する独自のメソッドAppendTostring(string、delimiter)を記述できます。次に、追加するタイミングと追加しないタイミングを気にせずに、ループでそのメソッドを呼び出すだけです。

編集:もちろん、可能な場合はメソッドで何らかの種類のStringBufferを使用する方がよいでしょう。

0
tehvan

.NETでは、可能であればString.joinメソッドを使用します。これにより、セパレータと文字列配列を指定できます。リストはToArrayを使用して配列に変換できますが、それによるパフォーマンスへの影響はわかりません。

あなたが言及する3つのアルゴリズムは私が使用するものです(ifステートメントが含まれていないので2番目が好きですが、長さがわからない場合はコードが重複しないため3番目を使用します)。 2番目はリストが空でない場合にのみ機能するため、別のifステートメントが必要になる場合があります。

4番目のバリアントは、連結されているすべての要素の前にセパレーターを置き、結果から最初のセパレーターを削除することです。

ループ内で文字列を連結する場合、自明ではないケースでは、stringbuilderを使用すると、繰り返される文字列連結を大幅に上回ります。

0
Renze de Waal

Groovyには、String Object.join(String)メソッドもあります。

0
user159313

から http://dogsblog.softwarehouse.co.zw/post/2009/02/11/IEnumerable-to-Comma-Separated-List-(and-more).aspx

開発時にペットが嫌いなのは、カンマ区切りのIDのリストを作成する場合です。これは、SO単純ですが、常に醜いコードです。..一般的な解決策は、ループしてそれぞれの後にカンマを置くことです。次に、最後の文字を削除するか、リストの最初または最後にあるかどうかを確認するifステートメントを作成します。以下は、IEnumberable、つまりリスト、配列などで使用できるソリューションです。これは最も効率的な方法でもあります。文字列を編集したり、ifを使用したりするよりも良い割り当てに依存しているので、そうすることを考えることができます。

public static class StringExtensions
{
    public static string Splice<T>(IEnumerable<T> args, string delimiter)
    {
        StringBuilder sb = new StringBuilder();
        string d = "";
        foreach (T t in args)
        {
            sb.Append(d);
            sb.Append(t.ToString());
            d = delimiter;
        }
        return sb.ToString();
    }
}

これで、IEnumerableなどで使用できます。

StringExtensions.Splice(billingTransactions.Select(t => t.id)、 "、")

私たちに31、32、35を与える

0
Matthew Hood

Clojureでは、clojure.contrib.str-utils/str-joinを使用できます。

(str-join ", " list)

しかし、実際のアルゴリズムの場合:

(reduce (fn [res cur] (str res ", " cur)) list)
0
wtetzner