我有几个文本框,用户可以在其中输入信息。这可以包括逗号,所以我不能使用标准的逗号分隔字符串。
什么是好的分隔符,表示字符串应根据用户在其着作中通常不使用的字符分隔?我将把这些字段组合成一个字符串字符串并将它们传递给我的加密方法。解密后我需要能够可靠地分离它们。
如果重要的话,我正在使用C#。
答案 0 :(得分:27)
|将列在我的列表中,通常用作CSV的替代品。谷歌“管道划界”,你会发现很多例子。
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)));
答案 1 :(得分:14)
我已经看到异常字符用作分隔符,甚至是-|::|-
之类的异常字符组合,但是尽管它们更不可能发生,但它们仍然可以。
如果你想让它变得不透水,你基本上有两种选择:
1:使用无法输入的字符,例如'\ 0'字符:
加入:
string combined = string.Join("\0", inputArray);
分割:
string[] result = combined.Split('\0');
2:转义字符串并使用转义字符作为分隔符,如url编码值并使用&amp;作为分隔符:
加入:
string combined = string.Join("&", inputArray.Select<string,string>(System.Web.HttpUtility.UrlEncode).ToArray());
分割:
string[] result = combined.Split('&').Select<string,string>(System.Web.HttpUtility.UrlDecode).ToArray();
答案 2 :(得分:8)
我不认为自从我停止使用C语言后,我已经心甘情愿地自行划分了一系列字符串。用“现代”语言不需要它,而且 - 虽然微不足道 - 边缘情况的数量已经足够惹恼你。
将它们存储在List&lt; string&gt;中或string []并序列化/反序列化它们。如果您想要人类可读性或互操作,请使用XML - 如果不需要,则使用二进制序列化。您可以轻松地加密输出,并且没有歧义或创建您自己的转义例程。
在C#中,它的LOC较少,写入的时间比这个答案要少。没有理由推出自己的解决方案。
答案 3 :(得分:7)
最好的解决方案是坚持使用逗号并引入对字符转义的支持。无论您选择哪种角色最终都需要输入,因此您也可以为此提供支持。
在双引号字符串中思考反向词+双引号。
不要选择像反引号这样的字符,因为有些用户可能不知道如何输入...
答案 4 :(得分:3)
任何非标准字符管|,反引号`,波浪号〜,爆炸!或分号;可能会奏效。但是,如果你走这条路,你真的冒险离开可用性。要求他们用反斜杠或其他东西来逃避逗号,请求他们错过一个。
如果无法使用CSV,则应考虑更改用户界面。 (哎呀,你应该远离CSV用户输入!)你说文本框所以我假设你在网络或某种形式的胜利形式或WPF(绝对不是控制台)。所有这些都为您提供了比单个文本框更好的UI控制,并迫使用户顺应您难以实现的UI设计。
更多信息肯定有助于更好地指导答案。
但是,作为使用反斜杠转义逗号的示例。请注意,在使用逗号之前,您无法转义反斜杠。所以@“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"}
答案 5 :(得分:3)
用户是否会在文本框中输入分隔的字符串,还是会输入单独的字符串,然后由代码将其构建为分隔的字符串?
在第一种情况下,重新考虑您的UI可能更好。例如,用户可以一次输入一个字符串到文本框中,然后在每个字符串后单击“添加到列表”按钮。
在第二种情况下,您使用的分隔符并不重要。选择你喜欢的任何角色,只要确保你逃脱该角色的任何其他出现。
修改强>
由于其他答案的几条评论都要求代码,这里有一个创建逗号分隔字符串的方法,使用反斜杠作为转义字符:
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);
}
答案 6 :(得分:2)
我最终想到,每个角色都会被某人使用。用户总能找到一种方法来破坏我们的HL7解析器。
不是单个字符,也可以尝试一个随机的字符串,没有人会使用它。比如“#!@!#”。
答案 7 :(得分:1)
Mark Brackett有正确的答案。我只想补充一点,这个简单问题的答案数量应该会让你不再使用分隔字符串。让这成为“明智的话语”。
答案 8 :(得分:1)
检测未使用的字符,然后使用它。您的最终组合字符串可以从用作分隔符的那一点开始。
示例:您的用户输入“pants”“,;,;,;,;,;”和“| ~~ |” 迭代一组字符,直到找到一个未使用的字符。可以说,“$” 你最后的连接字符串是“$ pants $,;,;,;,;,; $ | ~~ |” 初始字符告诉您的程序将哪个字符用作分隔符。 这样,没有禁止的字符,句号。
答案 9 :(得分:1)
我假设您说的是用户将数据输入到单独的字段中,然后您将它组合在一起。因此用户永远不需要知道或关心分隔符是什么。
不要只是尝试选择一个“没人用过”的角色,因为无论是偶然还是为了试图破坏你的代码,一些用户最终都会使用它。
所以,我要么:
插入反斜杠以转义用户输入中的逗号和反斜杠,然后将字符串与逗号组合。要分开,你可以拆分未转义的逗号(这是状态机的工作),然后转换每个组件。
使用现成的序列化字符串列表的方法。什么是可用的取决于您的环境,我不知道C#/。NET足够建议。在Java中,您可以序列化一个向量或其他任何内容。
使用ASCII-BEL或ASCII-VT等控制字符分隔数据(如果您的字符串永远不会被视为以空字符结尾,则使用ASCII-NUL),并拒绝包含该字符的用户输入。
如果必须允许用户输入他们喜欢的任何字符值,则第一个选项很好。如果您不关心数据膨胀,第二种选择是好的。第三种选择是好的,如果你不介意拒绝那些试图插入有趣数据的smart-alec用户(或那些有不寻常要求的用户)。
答案 10 :(得分:1)
如前所述,您选择的任何字符都有可能出现在输入中,因此您必须处理转义。 XML可能是一种很好的序列化格式,因为我相信.NET具有良好的XML创建和删除支持。这可能比尝试实现自己的字符转义要强大得多,并且将来也会更具扩展性。
答案 11 :(得分:1)
没人说TAB?制表符分隔很好但是在GUI中键入制表符并不容易(它往往会将您移动到下一个屏幕元素)。但是对于计算机生成的文件,TAB是完美的,因为它真的不应该出现在用户生成的文本中。
答案 12 :(得分:1)
为什么不用引号括起每个输入?
这样你最终得到了这个:
"Aaron","Johnson","25","I like cats, and dogs"
不要忘记在输入中转义引号......
答案 13 :(得分:0)
如果您要使用真正的唯一分隔符,建议使用╡
或唯一字符串\u2561
。
答案 14 :(得分:0)
我知道这个回应已经相当晚了,但我在一段时间后遇到了这个问题并且相当好地解决了这个问题(恕我直言)。希望将来,这将有助于其他人寻找类似问题的答案。
虽然我一般会把自己置于类似于Mike Ottum,John Saunders和Mark Brackett的阵营中,但问题的简单事实是,有时我们的开发人员必须做我们不愿意做的事情。我的特殊情况需要提供一个(大部分)人类可读的“id”来在RESTful URI中使用,该RESTful URI是从对象的有机复合键派生的。二进制或XML序列化不是一个真正的选择。所以?我选择尽可能少地重新发明轮子。 System.Text.RegularExpressions.Regex类具有对这些疯狂的正则表达式模式进行操作的escape / unescape方法。有一些可以逃脱的角色可供选择。我决定管道('|')字符。
这是我的实现(为了重用而分类,但你可以为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:请不要尝试查找“用户不会输入的字符”作为连接字符串的分隔符。用户最终会输入它。已经有足够的“神奇数字”代码等待爆炸。并且有许多经过试验和测试的解决方案。
答案 15 :(得分:0)
我也支持选择TAB(\ t)和扩展PIPE(|)符号。
但根据我的经验,最常用的是分号(;)和引用字段以及\和\的转义,这是完美的。只需要一个解析器保持状态。实际的分隔字符变得不重要。< / p>
如果你没有使用逃避,那么计算每行的“字段”并将它们与预期结果进行比较是明智的。由于此类文件的大多数应用程序使用某种固定数量的字段,您可以在条目中捕获错误,如果它不触发,那么一切都很好。
答案 16 :(得分:0)
使用标签(或者可能是\ n) - 如果用户输入该标签会导致文本框退出。
答案 17 :(得分:0)
我更喜欢在可能的情况下使用普通人不可能输入的字符组合作为我的分隔符。例如,我使用了“)^&amp; ^(”并在我的代码中将其设置为const“cDelimiter”;然后将我的所有字段连接起来。通过使用一个小的唯一字符串,我大大减少了可能性用户的引擎盖不小心进入我的分隔符。用户输入一个或一个〜的可能的引擎盖是不可能的,但这并不意味着它不会发生。
答案 18 :(得分:0)
我建议使用“;”
答案 19 :(得分:0)
换行? (即使用多行文本框)
答案 20 :(得分:0)
管道字符(|),也许?如果您的用户群非常狡猾,那么这种方法(要求他们划分文本)可能不是最好的方法;你可以尝试别的东西,例如提供一些动态添加文本框的方法,它接受另一个字符串等。
如果您提供有关您正在做什么以及为谁做的更多信息,则可能有人建议替代方法。
答案 21 :(得分:0)
反击。没有人使用反击。
答案 22 :(得分:-1)
为什么不使用ASCII代码31(单位分隔符),该代码专门用于分隔字符串中的数据元素?
普通人不可能输入该字符,使用程序员工具的人很难输入该字符,因此您不必担心在极端情况下转义。