Подтвердить что ты не робот

Что такое более уникальный разделитель, чем запятая для разделения строк?

У меня есть несколько текстовых полей, в которых пользователи могут вводить в них информацию. Это может включать запятые, поэтому я не могу использовать стандартные строки с разделителями-запятыми.

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

Я использую С#, если это имеет значение.

4b9b3361

Ответ 1

| будет следующим в моем списке и часто используется в качестве альтернативы CSV. google "pipe delimited", и вы найдете много примеров.

string[] items = new string[] {"Uno","Dos","Tres"};

string toEncrypt = String.Join("|", items);

items = toEncrypt.Split(new char[] {'|'}, StringSplitOptions.RemoveEmptyEntries);

foreach(string s in items)
  Console.WriteLine(s);

А так как всем нравится критиковать кодировку и не предоставлять код, вот один из способов кодирования текста, чтобы ваш | delim не столкнется.

string[] items = new string[] {"Uno","Dos","Tres"};

for (int i = 0; i < items.Length; i++)
    items[i] = Convert.ToBase64String(Encoding.UTF8.GetBytes(items[i]));

string toEncrypt = String.Join("|", items);

items = toEncrypt.Split(new char[] {'|'}, StringSplitOptions.RemoveEmptyEntries);

foreach (string s in items)
     Console.WriteLine(Encoding.UTF8.GetString(Convert.FromBase64String(s)));

Ответ 2

Я видел unusal символов, используемых в качестве разделителей, даже несовпадающих комбинаций символов, таких как -|::|-, но, хотя они вряд ли произойдут, они все равно могут.

У вас есть в основном два варианта, если вы хотите сделать его водонепроницаемым:

1: используйте символ, который невозможно ввести, например символ '\ 0':

Регистрация:

string combined = string.Join("\0", inputArray);

Split:

string[] result = combined.Split('\0');

2: избегайте строки и используйте экранированный символ в качестве разделителя, например URL-адрес, кодирующий значения и используемые и как разделитель:

Регистрация:

string combined = string.Join("&", inputArray.Select<string,string>(System.Web.HttpUtility.UrlEncode).ToArray());

Split:

string[] result = combined.Split('&').Select<string,string>(System.Web.HttpUtility.UrlDecode).ToArray();

Ответ 3

Я не думаю, что я охотно разделил коллекцию строк, так как я прекратил использовать C. Там просто не нужно это на "современном" языке, и - хотя тривиальным - количество крайних случаев достаточно чтобы раздражать вас до смерти.

Сохраните их в списке <string> или string [] и сериализовать/десериализовать их. Используйте XML, если вы хотите, чтобы пользовательская читаемость или interop - или двоичный serialze, если вы этого не сделаете. Вы можете легко шифровать вывод в любом случае, и нет никакой двусмысленности или создавать свои собственные процедуры эскалации.

В С# он меньше LOC и занимает меньше времени для записи, чем этот ответ. Нет никаких оправданий для развертывания собственного решения.

Ответ 4

Лучшее решение - придерживаться запятых и вводить поддержку для ускорения символа. Какой бы характер вы ни выбрали, в конце концов вам нужно будет ввести, чтобы вы могли поддержать это.

Подумайте о возвратах + двойные кавычки в двойных кавычках.

Не выбирайте такого персонажа, как backtick, потому что некоторые пользователи могут не знать, как его ввести...

Ответ 5

Любая нестандартная символьная труба |, backtick`, тильда ~, bang! или semi-colon; вероятно, будет работать. Однако, если вы идете по этому маршруту, вы действительно отказываетесь от юзабилити. Попросив их избежать запятых с обратной косой чертой или что-то просит их пропустить один.

Если CSV невозможен, вам следует рассмотреть возможность изменения пользовательского интерфейса. (Heck, вы должны держаться подальше от CSV в любом случае для ввода пользователем!) Вы говорите текстовое поле, поэтому я предполагаю, что вы находитесь в сети или каких-то форм выигрыша или WPF (определенно не консоль). Все это дает вам лучший контроль пользовательского интерфейса, чем одно текстовое поле и заставляет пользователей соответствовать вашему сложному дизайну интерфейса.

Дополнительная информация, безусловно, поможет лучше ответить на вопросы.

Однако, как пример выхода из запятой с обратной косой чертой. Обратите внимание, что вы не можете избежать обратной косой черты перед запятой. Итак, @ "uno, dos, tr \\, es" закончится с { "uno", "dos", "tr\es" }.

string data = @"uno, dos, tr\,es";
string[] items = data.Split(','); // {"uno", " dos", @"tr\", "es"}
List<string> realitems = new List<string>();
for (int i=items.Length-1; i >= 0; i--)
{
    string item = items[i];
    if (item.Length == 0) { realitems.Insert(0, ""); continue; }

    if (realitems.Count == 0) { realitems.Insert(0, item); }
    else
    {
        if (item[item.Length - 1] == '\\') { realitems[0] = item + "," + realitems[0]; }
        else { realitems.Insert(0, item); }
    }
}

// Should end up with {"uno", " dos", "tr,es"}

Ответ 6

Будет ли пользователь вводить строки с разделителями в текстовые поля или они будут вводить отдельные строки, которые затем будут встроены в строки с разделителями по вашему коду?

В первом случае лучше пересмотреть свой интерфейс. например, Пользователь может ввести одну строку за раз в текстовое поле и нажать кнопку "Добавить в список" после каждого из них.

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

ИЗМЕНИТЬ

Так как несколько комментариев к другим ответам запрашивают код, вот способ создания строки с разделителями-запятыми, используя обратную косую черту в качестве escape-символа:

public static string CreateDelimitedString(IEnumerable<string> items)
{
    StringBuilder sb = new StringBuilder();

    foreach (string item in items)
    {
        sb.Append(item.Replace("\\", "\\\\").Replace(",", "\\,"));
        sb.Append(",");
    }

    return (sb.Length > 0) ? sb.ToString(0, sb.Length - 1) : string.Empty;
}

И здесь метод для преобразования этой строки с разделителями-запятыми обратно в коллекцию отдельных строк:

public static IEnumerable<string> GetItemsFromDelimitedString(string s)
{
    bool escaped = false;
    StringBuilder sb = new StringBuilder();

    foreach (char c in s)
    {
        if ((c == '\\') && !escaped)
        {
            escaped = true;
        }
        else if ((c == ',') && !escaped)
        {
            yield return sb.ToString();
            sb.Remove(0, sb.Length);
        }
        else
        {
            sb.Append(c);
            escaped = false;
        }
    }

    yield return sb.ToString();
}

И вот пример использования:

string[] test =
    {
        "no commas or backslashes",
        "just one, comma",
        @"a comma, and a\ backslash",
        @"lots, of\ commas,\ and\, backslashes",
        @"even\\ more,, commas\\ and,, backslashes"
    };

    string delimited = CreateDelimitedString(test);
    Console.WriteLine(delimited);

    foreach (string item in GetItemsFromDelimitedString(delimited))
    {
        Console.WriteLine(item);
    }

Ответ 7

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

Вместо одного символа, возможно, попробуйте строку, которая была бы случайной, чтобы никто ее не использовал. Что-то вроде "#! @! #".

Ответ 8

Обнаружьте символ, который не используется, а затем используйте его. Ваша итоговая объединенная строка может начинаться с символа, который должен быть из этой точки, используемой в качестве разделителя.

Например: ваши пользователи вводят "штаны", ";", ";"; и "| ~~ |" Вы повторяете набор символов, пока не найдете тот, который не используется. Может быть, скажем, "$" Тогда ваша конечная конкатенированная строка: "$ pants $,;,;,;,;,; $| ~~ |" Начальный символ указывает вашей программе, какой символ следует использовать в качестве разделителя. Таким образом, нет запрещенных символов, период.

Ответ 9

Я предполагаю, что вы говорите, что пользователь вводит данные в отдельные поля, а затем вы их объединяете. Таким образом, пользователю никогда не нужно знать и не учитывать, что такое разделитель.

Не пытайтесь выбрать персонажа, который "никто никогда не использует", потому что либо случайно, либо для того, чтобы попытаться сломать ваш код, некоторые пользователи в конечном итоге его используют.

Итак, я хотел бы:

  • Вставьте обратную косую черту для удаления запятых и обратных косых черт в пользовательском вводе, а затем объедините строки с запятыми. Чтобы разделить, вы разбиваете на неэкранированные запятые (это задание для конечного автомата), затем отменяете каждый компонент.

  • Используйте готовые средства для сериализации списка строк. Что зависит от вашей среды, я не знаю, С#/.NET достаточно хорошо, чтобы советовать. В Java вы можете просто сериализовать вектор или что-то еще.

  • Отделите данные с помощью управляющего символа, такого как ASCII-BEL или ASCII-VT (или ASCII-NUL, если ваши строки никогда не рассматриваются как nul-terminated) и отклоняют ввод пользователя, который содержит этот символ.

Первый вариант хорош, если пользователю разрешено вводить любые значения char, которые им нравятся. Второй вариант хорош, если вы не заботитесь о раздувании данных. Третий вариант хорош, если вы не возражаете против отказа пользователям smart-alec (или тем, у кого необычные требования), которые пытаются вставить смешные данные.

Ответ 10

Как уже отмечалось, любой символ, который вы выберете, имеет шанс появиться во входе, поэтому вам придется обрабатывать экранирование. XML может быть хорошим форматом сериализации для использования, поскольку я считаю, что .NET имеет хорошую поддержку создания и удаления XML. Это, вероятно, будет намного более надежным, чем попытка реализовать свое собственное экранирование персонажа, а также будет более расширяемой в будущем.

Ответ 11

Никто не сказал ТАБ? Разграничение табуляции велико, но вводить вкладки в графические интерфейсы нелегко (это приводит к перемещению вас к следующему элементу экрана). Но для файлов, созданных с помощью компьютера TAB, идеально, поскольку он никогда не должен появляться в тексте, создаваемом пользователем.

Ответ 12

Почему бы вам просто не обернуть каждый вход в кавычки?

Таким образом, вы получите следующее:

"Aaron","Johnson","25","I like cats, and dogs"

Не забудьте избежать котировок на входе...

Ответ 13

У Марка Брэкетта есть правильный ответ. Я только добавлю, что очень большое количество ответов на этот простой вопрос должно помешать вам использовать строки с разделителями. Пусть это будет "слово мудрым".

Ответ 14

Отступ. Никто не использует обратный ход.

Ответ 15

Возможно, символ трубы (|)? Если ваша пользовательская база удаленно IT-стеснительна, тогда такой подход (просить их разграничить их текст) может быть не самым лучшим; вы можете попробовать что-то еще, например. предоставить некоторые средства для динамического добавления текстового окна "на лету", которое принимает другую строку и т.д.

Если вы предоставите немного больше информации о том, что вы делаете, и для кого, возможно, кто-то может предложить альтернативный подход.

Ответ 16

Newline? (т.е. использовать многострочное текстовое поле)

Ответ 17

Я бы предложил использовать ";"

Ответ 18

Я предпочитаю использовать комбинацию символов, которые, по возможности, вряд ли будут введены обычным человеком в качестве моего разделителя. Например, я использовал ") ^ & ^ (" и настроил его как const "cDelimiter" в моем коде, а затем связал все мои поля с этим. Используя небольшую уникальную строку, я значительно уменьшаю вероятность капот пользователя, случайно входящий в мой разделитель. Вероятный капюшон пользователя, вводящего или или, предположительно, маловероятен, но это не значит, что этого не произойдет.

Ответ 19

Используйте вкладку (или, возможно, \n) - которая, если она будет введена пользователем, приведет к выходу текстового поля.

Ответ 20

Я также поддерживаю выбор TAB (\ t) и некоторую длину символа PIPE (|).

Но наиболее используемым в моем опыте является точка с запятой (;) вместе с цитируемыми полями и escape-последовательности для\и\", которые просто идеальны. Просто нужен парсер, сохраняющий состояние. Фактическое разграничение char становится несущественным.

Если вы не используете escape, разумно подсчитать "поля" в строке и сравнить их с ожидаемыми результатами. Поскольку большинство приложений такого типа используют какое-то фиксированное количество полей, вы можете уловить ошибки в записи и получить все, что хорошо, если оно не срабатывает.

Ответ 21

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

В то время как я обычно ставил себя в лагерях, подобных Майку Оттаму, Джону Сондерсу и Марку Брэкетту, простой факт заключается в том, что иногда мы, разработчики, должны делать то, чего мы бы предпочли. В моем конкретном случае было необходимо предоставить (в основном) человекообразный "id" для использования в URI RESTful, который был получен из органического составного ключа объекта. Двоичная или XML-сериализация не была действительно вариантом. Так? Я решил изобретать как можно меньше колеса. Класс System.Text.RegularExpressions.Regex имеет методы escape/unescape, которые работают с этими сумасшедшими шаблонами регулярных выражений. Там есть несколько исчезающих символов. Я поселился на канале ( "|" ).

Вот моя реализация (class-ified для повторного использования, но вы можете вырезать хорошие биты для 7-строчного "встроенного" решения, если это нравится, когда вы любите кататься):

using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;

namespace RPlus.DTO
{
    /// <summary>
    /// Provide safe string un/concatenating
    /// </summary>
    static class Glob
    {
        // a Regex Split param that basically says:
        // Split on the pipe char unless the preceeding char is a backslash
        private const string _splitterer = @"(?<!\\)\|";
        // no explanation needed (hopefully)
        private const char _delimiter = '|';

        /// <summary>
        /// Produce a properly escaped concatenation
        /// from some number of strings
        /// </summary>
        /// <param name="items">strings to escape/concate</param>
        /// <returns>an escaped concatenation of items</returns>
        public static string To(IEnumerable<string> items)
        {
            var escapedItems = new List<string>();
            foreach (var s in items) escapedItems.Add(Regex.Escape(s));
            return string.Join(_delimiter.ToString(), escapedItems);
        }

        /// <summary>
        /// Unconcatenate/unescape a string into its original strings
        /// </summary>
        /// <param name="globbedValue">
        /// A value returned from Glob.To()
        /// </param>
        /// <returns>
        /// The orignal strings used to construct the globbedValue
        /// </returns>
        public static List<string> From(string globbedValue)
        {
            return From(globbedValue, default(int?));
        }

        /// <summary>
        /// Unconcatenate/unescape a string into its original strings
        /// </summary>
        /// <param name="globbedValue">
        /// A value returned from Glob.To()
        /// </param>
        /// <param name="expectedTokens">
        /// The number of string tokens that 
        /// should be found in the concatenation
        /// </param>
        /// <returns>
        /// The orignal strings used to construct the globbedValue
        /// </returns>
        public static List<string> From(string value, int? expectedTokens)
        {
            var nugs = Regex.Split(value, _splitterer);
            if (expectedTokens.HasValue && nugs.Length != expectedTokens.Value)
                throw new ArgumentException("Unexpected number of tokens");
            var unescapedItems = new List<string>();
            foreach (var s in nugs) unescapedItems.Add(Regex.Unescape(s));
            return unescapedItems;
        }

    }
}

И здесь несколько примеров использования:

var glob = Glob.To(new string[] { "Foo|Bar", "Bar|Baz", "Baz|Qux" });
var orig = Glob.From(glob);

CAVEAT: Пожалуйста, не пытайтесь найти "символ, который пользователь никогда не вводит" для использования в качестве разделителя конкатенированных строк. В конечном итоге пользователи введут его. Там уже достаточно кода "магического номера", ожидающего взорваться. И есть множество проверенных и проверенных решений проблемы.

Ответ 22

Если вы хотите использовать действительно уникальный разделитель, я бы порекомендовал или уникальную строку \u2561.

Ответ 23

Почему бы не использовать код 31 ASCII (разделитель единиц), который был специально зарезервирован для разделения элементов данных в строках?

Обычный человек не может ввести этот символ, кто-то с инструментами программирования может случайно ввести его, и поэтому вам не нужно беспокоиться о побеге в крайних случаях.