Додати роздільник до рядка на кожні N символів?


77

У мене є рядок, що містить двійкові цифри. Як відокремити рядок після кожних 8 цифр?

Припустимо, що рядок:

string x = "111111110000000011111111000000001111111100000000";

Я хочу додати роздільник типу (кома) після кожного 8 символів.

вихід повинен бути:

"11111111,00000000,11111111,00000000,11111111,00000000,"

Потім я хочу надіслати його до списку <> останні 8 символів, а потім попередні 8 символів (крім,) тощо.

Як я можу це зробити?


1
Ви можете використовувати масив символів або байт.
AliRıza Adıyahşi


чи можу я зробити перше, що потрібно з string.Format ()? якщо чим, як?
Абдур Рахім,

Відповіді:


128
Regex.Replace(myString, ".{8}", "$0,");

Якщо вам потрібен масив із восьмисимвольних рядків, то, мабуть, простіше:

Regex.Split(myString, "(?<=^(.{8})+)");

який розділить рядок лише в тих точках, де перед ним кратно вісім символів.


1
Можливо, варто стверджувати, що це лише двійкові "цифри", а не будь-який символ: "[01]{8}"
GalacticCowboy

4
Ну, я сподіваюся, вони знають, які дані вони сюди вкидають :)
Джої

Ви можете пояснити мені частину "0 доларів"? Я не зовсім впевнений, як цей вираз слід читати / оцінювати.
scottmgerstl

1
В частині заміни $0мається на увазі весь матч ( $1це перша група захоплення тощо). Ви також можете використовувати $&.
Джої

17
Хоча запит і запитував кінцеву кому, якщо розробник не хотів кінцеву кому, він міг змінити шаблон RegEx на ". {8} (?! $)", Який використовує від'ємний результат пошуку, щоб переконатися, що він не відповідає вісім символів у кінці рядка.
Джош Ліон,

43

Спробуйте це:

var s = "111111110000000011111111000000001111111100000000";
var list = Enumerable
    .Range(0, s.Length/8)
    .Select(i => s.Substring(i*8, 8));
var res = string.Join(",", list);

Так справді ... Дякую @dasbinkeblight
Абдур Рахім

1
Вам ToList()між іншим не потрібно , як string.Joinі перевантаження, яке займаєIEnumerable (починаючи з .NET 4).
Джої

1
@Joey Я знаю, але спочатку я неправильно зрозумів питання. Я прочитав частину, де в ОП сказано "Тоді я хочу надіслати її до списку <>", і опублікував відповідь ToList()без string.Joinрядка. Потім я перечитав питання, додав res = ...і зберіг, але забув видалити ToList().
Сергій Калініченко

1
Була одна проблема, після внесення методу розширення. якщо рядок був коротший, ніж інтервал. if (s.Length <інтервал) повернення s; Але інакше працював чудово.
Йогурт Мудрий

Цей метод відсікає довжину рядка. Якби sрядок містив ще 7 символів, вони б не поверталися.
Морт

3

... або стара школа:

public static List<string> splitter(string in, out string csv)
{
     if (in.length % 8 != 0) throw new ArgumentException("in");
     var lst = new List<string>(in/8);

     for (int i=0; i < in.length / 8; i++) lst.Add(in.Substring(i*8,8));

     csv = string.Join(",", lst); //This we want in input order (I believe)
     lst.Reverse(); //As we want list in reverse order (I believe)

     return lst;
}

1
Я називаю це легким для читання - але для кожного своїм: D. Окрім методів Regex тут, це те, що роблять методи Linq за лаштунками - переглядаючи та рубаючи, коли вони йдуть - просто набагато легше для читання. Мені подобається метод Batch вище, це новий для мене :)
Wolf5370

Однак це навіть не компілюється, оскільки lengthне є членом System.String.
Joey

3

Потворно, але менше сміття:

private string InsertStrings(string s, int insertEvery, char insert)
{
    char[] ins = s.ToCharArray();
    int length = s.Length + (s.Length / insertEvery);
    if (ins.Length % insertEvery == 0)
    {
        length--;
    }
    var outs = new char[length];
    long di = 0;
    long si = 0;
    while (si < s.Length - insertEvery)
    {
        Array.Copy(ins, si, outs, di, insertEvery);
        si += insertEvery;
        di += insertEvery;
        outs[di] = insert;
        di ++;
    }
    Array.Copy(ins, si, outs, di, ins.Length - si);
    return new string(outs);
}

Перевантаження рядка:

private string InsertStrings(string s, int insertEvery, string insert)
{
    char[] ins = s.ToCharArray();
    char[] inserts = insert.ToCharArray();
    int insertLength = inserts.Length;
    int length = s.Length + (s.Length / insertEvery) * insert.Length;
    if (ins.Length % insertEvery == 0)
    {
        length -= insert.Length;
    }
    var outs = new char[length];
    long di = 0;
    long si = 0;
    while (si < s.Length - insertEvery)
    {
        Array.Copy(ins, si, outs, di, insertEvery);
        si += insertEvery;
        di += insertEvery;
        Array.Copy(inserts, 0, outs, di, insertLength);
        di += insertLength;
    }
    Array.Copy(ins, si, outs, di, ins.Length - si);
    return new string(outs);
}

3

Існує ще один підхід до регулярних виразів:

var str = "111111110000000011111111000000001111111100000000";
# for .NET 4
var res = String.Join(",",Regex.Matches(str, @"\d{8}").Cast<Match>());

# for .NET 3.5
var res = String.Join(",", Regex.Matches(str, @"\d{8}")
            .OfType<Match>()
            .Select(m => m.Value).ToArray());

Мені подобається такий підхід, оскільки "шматки зрозумілі", навіть якщо для цього потрібно трохи більше видумки в .NET 3.5

Дякую за доповнення :) - Я постійно забуваю перевіряти сумісність фреймворку.
Олексій

1
Цей код видаляє символи. Сепаратори замінюються на рядок, а рядок буде втрачено.
Mohsen Tavoosi محسن طاوسی

2

Якщо я правильно розумію вашу останню вимогу (мені незрозуміло, чи потрібна вам проміжна рядок, розділена комами, чи ні), ви можете зробити це:

var enumerable = "111111110000000011111111000000001111111100000000".Batch(8).Reverse();

Використовуючи morelinq .


Якби тільки Batchбуло стандартним :( У будь-якому випадку, це рука знати про morelinq.

1

Один із способів використання LINQ:

string data = "111111110000000011111111000000001111111100000000";
const int separateOnLength = 8;

string separated = new string(
    data.Select((x,i) => i > 0 && i % separateOnLength == 0 ? new [] { ',', x } : new [] { x })
        .SelectMany(x => x)
        .ToArray()
    );

Цей роздільник вставки коду справа наліво. Замість зліва направо
Mohsen Tavoosi محسن طاوسی

1

Тут мої два маленькі центи теж. Реалізація за допомогою StringBuilder:

        public static string AddChunkSeparator (string str, int chunk_len, char separator)
        {
            if (str == null || str.Length < chunk_len) {
                return str;
            }
            StringBuilder builder = new StringBuilder();
            for (var index = 0; index < str.Length; index += chunk_len) {
                builder.Append(str, index, chunk_len);
                builder.Append(separator);
            }
            return builder.ToString();
        }

Ви можете назвати це так:

string data = "111111110000000011111111000000001111111100000000";
string output = AddChunkSeparator(data, 8, ',');

Цей метод не вдається, якщо str нульовий або якщо він коротший за chunk_len
Грег,

1

Я зробив це за допомогою Pattern & Matcher наступним чином:

fun addAnyCharacter(input: String, insertion: String, interval: Int): String {
  val pattern = Pattern.compile("(.{$interval})", Pattern.DOTALL)
  val matcher = pattern.matcher(input)
  return matcher.replaceAll("$1$insertion")
}

Де:

inputвказує вхідний рядок. Перевірте розділ результатів.

insertionвказує Вставити рядок між цими символами. Наприклад кома (,), старт (*), хеш (#).

interval вказує, на якому інтервалі потрібно додати символ вставки.

inputвказує вхідний рядок. Перевірте розділ результатів. Перевірити розділ результатів; тут я додав вставку для кожного 4-го символу.

Результати:

I / P: 1234XXXXXXXX5678 O / P: 1234 XXXX XXXX 5678

I / P: 1234567812345678 O / P: 1234 5678 1234 5678

I / P: ABCDEFGHIJKLMNOP O / P: ABCD EFGH IJKL MNOP

Сподіваюся, це допомагає.


0

Це набагато швидше без копіювання масиву (ця версія вставляє простір кожні 3 цифри, але ви можете налаштувати його відповідно до своїх потреб)

public string GetString(double valueField)
{
    char[] ins = valueField.ToString().ToCharArray();
    int length = ins.Length + (ins.Length / 3);
    if (ins.Length % 3 == 0)
    {
        length--;
    }
    char[] outs = new char[length];

    int i = length - 1;
    int j = ins.Length - 1;
    int k = 0;
    do
    {
        if (k == 3)
        {
            outs[i--] = ' ';
            k = 0;
        }
        else
        {
            outs[i--] = ins[j--];
            k++;
        }           
    }
    while (i >= 0);

    return new string(outs);
}

Я цього не розумію. valueField a double? ви перетворюєте вхідний рядок у подвійний, щоб використовувати його у функції, щоб потім перетворити його в рядок і charArray? Не могли б ви трохи прокоментувати код?
Джозе

У мене не було вхідного рядка. У мене просто було подвійне значення, саме тому valueFiledце подвійне. Якщо у вас є рядкове значення, ви можете створити valueFiledрядок і змінити 1-й рядок на char[] ins = valueField.ToCharArray();.
Матеуш Пуваловський

0

Трохи пізно на вечірку, але ось спрощений вираз LINQ для розбиття вхідного рядка xна групи, nрозділені іншим рядком sep:

string sep = ",";
int n = 8;
string result = String.Join(sep, x.InSetsOf(n).Select(g => new String(g.ToArray())));

Короткий огляд того, що тут відбувається:

  • xтрактується як IEnumberable<char>, саме тут InSetsOfвходить метод розширення.
  • InSetsOf(n)групи символів у IEnumerableз IEnumerable- кожного запису в зовнішній угрупованню містить внутрішню групу nсимволів.
  • Усередині Selectметоду кожна група nсимволів перетворюється назад у рядок за допомогою String()конструктора, що приймає масив chars.
  • Результатом Selectтепер є an IEnumerable<string>, який передається String.Joinдля чергування sepрядка, як і будь-який інший приклад.

-1

Я більше ніж запізнююся зі своєю відповіддю, але ви можете використати цю:

    static string PutLineBreak(string str, int split)
    {
        for (int a = 1; a <= str.Length; a++)
        {
            if (a % split == 0)
                str = str.Insert(a, "\n");
        }

        return str;
    }

-1

Для кожного 1 символу ви можете зробити наступне:

string.Join(".", "1234".ToArray()) //result: 1.2.3.4
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.