Присоедините строку, используя разделители - PullRequest
22 голосов
/ 24 февраля 2009

Как лучше всего объединить список строк в комбинированную строку с разделителями. Я в основном обеспокоен тем, когда прекратить добавлять разделитель. Я буду использовать C # для своих примеров, но я бы хотел, чтобы это не зависело от языка.

РЕДАКТИРОВАТЬ: я не использовал StringBuilder, чтобы сделать код немного проще.

Используйте цикл For *

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

Использовать For Loop для установки первого элемента ранее

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;
}

Изменение цикла по каждому элементу

Из решения Джона

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();
}

Какие еще есть алгоритмы?

Ответы [ 24 ]

35 голосов
/ 24 февраля 2009

Здесь невозможно дать действительно независимый от языка ответ, поскольку разные языки и платформы по-разному обрабатывают строки и предоставляют разные уровни встроенной поддержки для объединения списков строк. Вы можете взять в значительной степени идентичный код на двух разных языках, и было бы здорово на одном и ужасно на другом.

В C # вы можете использовать:

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

Это будет предварять запятой на всех, кроме первого элемента. Подобный код был бы хорош и в 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 был перегружен довольно значительно. Есть перегрузка, принимающая IEnumerable<T>, которая автоматически вызывает ToString, и есть перегрузка для IEnumerable<string>. Поэтому вам больше не нужен приведенный выше код ... для .NET, в любом случае.

22 голосов
/ 24 февраля 2009

В .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;
}
8 голосов
/ 24 февраля 2009

Нет особых причин делать это независимым от языка, когда некоторые языки поддерживают его в одной строке, например, Python

",".join(sequence)

Подробнее см. в документации по соединению .

5 голосов
/ 24 февраля 2009

В PHP implode () :

$string = implode($delim, $array);
5 голосов
/ 24 февраля 2009

Для python убедитесь, что у вас есть список строк, иначе ','. Join (x) не удастся. Для безопасного метода используется 2,5 +

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

"str (a) если else ''" подходит для типов None, в противном случае str () в итоге создает None, что нехорошо;)

4 голосов
/ 24 февраля 2009

Я бы выразил это рекурсивно.

  • Проверьте, равно ли число строковых аргументов 1. Если это так, верните его.
  • Иначе рекурсивно, но объедините первые два аргумента с разделителем между ними.

Пример в Common Lisp:

(defun join (delimiter &rest strings)
  (if (null (rest strings))
      (first strings)
      (apply #'join
             delimiter
             (concatenate 'string
                          (first strings)
                          delimiter
                          (second strings))
             (cddr strings))))

Более идиотский способ - использовать reduce, но он расширяется почти до тех же инструкций, что и выше:

(defun join (delimiter &rest strings)
  (reduce (lambda (a b)
            (concatenate 'string a delimiter b))
          strings))
4 голосов
/ 24 февраля 2009

Я всегда добавляю разделитель, а затем при необходимости удаляю его в конце. Таким образом, вы не выполняете оператор 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 голосов
/ 24 ноября 2010
List<string> aaa = new List<string>{ "aaa", "bbb", "ccc" };
string mm = ";";
return aaa.Aggregate((a, b) => a + mm + b);

и вы получите

aaa;bbb;ccc

лямбда довольно удобна

3 голосов
/ 22 февраля 2012

Проблема в том, что компьютерные языки редко имеют строковые логические значения, то есть методы типа string, которые делают что-нибудь полезное. По крайней мере, SQL Server имеет значение [not] null и nullif, что при объединении решает проблему с разделителем, кстати: isnotnull (nullif (columnvalue, ""), "," + columnvalue))

Проблема в том, что в языках есть булевы значения и есть строки, и они никогда не встретятся, кроме как в уродливых формах кодирования, например

concatstring = string1 + "," + string2; если (фубар) concatstring + = string3 concatstring + = string4 и т. д.

Я очень старался избежать всего этого уродства, играя в запятые и объединяясь с объединениями, но у меня все еще остались некоторые из них, включая ошибки SQL Server, когда я пропустил одно из запятых, а переменная пустой.

Jonathan

2 голосов
/ 24 февраля 2009

В C # вы можете просто использовать String.Join (разделитель, string_list)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...