ユーザーが情報を入力できるテキストボックスがいくつかあります。これにはカンマを含めることができるため、標準のカンマ区切り文字列を使用できません。
ユーザーが文章で通常使用しない文字に基づいて文字列を区切る必要があることを示すための適切な区切り文字は何ですか?これらのフィールドを組み合わせて文字列文字列にし、使用している暗号化メソッドに渡します。それらを復号化した後、それらを確実に分離できる必要があります。
重要な場合はC#を使用しています。
|私のリストの次のリストになり、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);
そして、誰もがエンコーディングについて批評家になり、コードを提供しないのが好きなので、ここにテキストをエンコードする1つの方法があります。 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つのオプションがあります。
1:「\ 0」文字のように、入力できない文字を使用します。
参加:
string combined = string.Join("\0", inputArray);
スプリット:
string[] result = combined.Split('\0');
2:文字列をエスケープし、エスケープされた文字を区切り文字として使用します。たとえば、値をエンコードするurlのように、&を区切り文字として使用します。
参加:
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();
Cの使用をやめたので、文字列のコレクションを進んで自己区切りしたとは思いません。「現代」言語では必要ありません。些細なことですが、Edgeケースの数はあなたを悩ませるのに十分です。死ぬまで。
それらをList <string>またはstring []に格納し、シリアル化/逆シリアル化します。人間が読みやすくしたり相互運用したりする場合はXMLを使用し、そうでない場合はバイナリシリアル化します。どちらの方法でも出力を簡単に暗号化でき、あいまいさや独自のエスケープルーチンを作成する必要はありません。
C#では、この回答よりもLOCが少なく、書き込みにかかる時間が短くなります。独自のソリューションを展開する言い訳はありません。
最善の解決策は、コンマに固執し、文字エスケープのサポートを導入することです。どの文字を選択しても、最終的には入力する必要があるため、これをサポートすることもできます。
二重引用符で囲まれた文字列内のバックスラッシュ+二重引用符を考えてください。
一部のユーザーは入力方法がわからない可能性があるため、バッククォートのような文字を選択しないでください...
非標準の文字パイプ|、バッククォート `、チルダ〜、バング!、またはセミコロンのいずれか;おそらくうまくいくでしょう。しかし、あなたがこのルートに行くなら、あなたは 本当に 使いやすさから離れて冒険します。バックスラッシュまたは何かでコンマをエスケープするように依頼することは、コンマを見逃すことを懇願しています。
CSVが不可能な場合は、UIの変更を検討する必要があります。 (とにかく、ユーザー入力のためにCSVから離れるべきです!)テキストボックスと言うので、Webまたはある種のWinフォームまたは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"}
ユーザーは区切り文字列をテキストボックスに入力しますか、それとも個々の文字列を入力し、コードによって区切り文字列に組み込まれますか?
最初のケースでは、代わりにUIを再考する方がよい場合があります。たとえば、ユーザーは一度に1つの文字列をテキストボックスに入力し、各文字列の後に[リストに追加]ボタンをクリックできます。
2番目のケースでは、使用する区切り文字は実際には重要ではありません。好きな文字を選択してください。その文字の他の出現をエスケープするようにしてください。
[〜#〜]編集[〜#〜]
他の回答に関するいくつかのコメントがコードを要求しているため、エスケープ文字としてバックスラッシュを使用して、コンマ区切りの文字列を作成する方法を次に示します。
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);
}
最終的には、すべてのキャラクターが誰かによって使用されるようになると思います。ユーザーは常にHL7パーサーを壊す方法を見つけます。
単一の文字の代わりに、誰も使用しないほどランダムな文字列を試してみてください。何かのようなもの "#!@!#"。
すでに述べたように、選択した文字は入力に表示される可能性があるため、エスケープを処理する必要があります。 .NETには優れたXMLの作成と削除のサポートがあると私は信じているので、XMLは使用するのに適したシリアル化形式かもしれません。これは、独自の文字エスケープを実装しようとするよりもはるかに堅牢である可能性が高く、将来的にはさらに拡張可能になります。
誰もTABを言わなかった?タブ区切りは素晴らしいですが、GUIにタブを入力するのは簡単ではありません(次の画面要素に移動する傾向があります)。ただし、コンピューターで生成されたファイルの場合、TABは、ユーザーが生成したテキストには表示されないため、完璧です。
各入力を引用符で囲んでみませんか?
そうすれば、これで終わります:
"Aaron","Johnson","25","I like cats, and dogs"
入力の引用符をエスケープすることを忘れないでください...
マークブラケットは正解です。この単純な質問に対する非常に多くの回答が、区切られた文字列の使用を妨げるはずであることを付け加えておきます。これを「賢者への言葉」としましょう。
使用されていない文字を検出し、それを使用します。最終的に結合される文字列は、区切り文字として使用されるそのポイントからの文字で開始できます。
例:ユーザーは「パンツ」「、;、;、;、;、;」と入力しますと「| ~~ |」使用されていない文字が見つかるまで、文字のセットを繰り返し処理します。たとえば、「$」のようになります。最後の連結文字列は「$ pants $、;、;、;、;、; $ | ~~ |」です。最初の文字は、区切り文字として使用する文字をプログラムに指示します。このように、禁止されている文字、ピリオドはありません。
あなたの言うことから、ユーザーが別々のフィールドにデータを入力し、それを結合していると思います。したがって、ユーザーは区切り文字が何であるかを知ったり気にしたりする必要はありません。
「誰も使用したことがない」文字を選択しようとしないでください。偶然またはコードを解読しようとするために、最終的に一部のユーザーが使用するためです。
だから、私はどちらかだろう:
バックスラッシュを挿入してユーザー入力のコンマとバックスラッシュをエスケープし、文字列をコンマと組み合わせます。分離するには、エスケープされていないコンマ(ステートマシンのジョブ)で分割してから、各コンポーネントをエスケープ解除します。
文字列のリストをシリアル化する既成の手段を使用します。利用できるものは環境によって異なりますが、C#/。NETについてアドバイスするのに十分な知識がありません。 Javaでは、ベクトルなどをシリアル化できます。
データをASCII-BELやASCII-VT(または文字列がヌル終了として扱われない場合はASCII-NUL)などの制御文字で区切り、その文字を含むユーザー入力を拒否します。
最初のオプションは、ユーザーが好きな文字値を入力できるようにする必要がある場合に適しています。 2番目のオプションは、データの肥大化を気にしない場合に適しています。 3番目のオプションは、面白いデータを挿入しようとするスマートアレックユーザー(または異常な要件を持つユーザー)を拒否してもかまわない場合に適しています。
タブ(または\ n)を使用します-ユーザーが入力すると、テキストボックスが終了します。
本当にniqueセパレーターを使用したい場合は、╡
または一意の文字列\u2561
をお勧めします。
また、TAB(\ t)の選択をサポートし、PIPE(|)記号を拡張します。
しかし、私の経験で最も使用されているのは、引用符で囲まれたフィールドと\および\ "のエスケープを含むセミコロン(;)です。これは、状態を保持するパーサーが必要です。実際の区切り文字は重要ではなくなります。
エスケープを使用しない場合は、行ごとの「フィールド」をカウントして、期待される結果と比較することをお勧めします。この種のファイルのほとんどのアプリケーションは、ある種の固定数のフィールドを使用するため、エントリのエラーをキャッチして、トリガーされない場合はすべてが良好な状態になります。
この応答がかなり遅いことは知っていますが、しばらく前にこの問題を経験し、かなりうまく対処しました(IMHO)。うまくいけば、将来的には、これは他の誰かが同様の質問への答えを探しているのに役立つでしょう。
私は通常、Mike Ottum、John Saunders、Mark Brackettと同様のキャンプに身を置きますが、問題の単純な事実は、開発者がやりたくないことをしなければならない場合があるということです。私の特定のケースでは、オブジェクトの有機複合キーから派生したRESTful URIで使用する(ほとんど)人間が読める「ID」を提供する必要がありました。バイナリまたはXMLシリアル化は実際にはオプションではありませんでした。そう?私はできるだけ少ない車輪を再発明することを選びました。 System.Text.RegularExpressions.Regexクラスには、これらのクレイジーな正規表現パターンを操作するエスケープ/エスケープ解除メソッドがあります。選択できるエスケープ可能な文字がいくつかあります。パイプ( '|')の文字に落ち着きました。
これが私の実装です(再利用のために分類されていますが、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:連結された文字列の区切り文字として使用するために、「ユーザーが入力することのない文字」を見つけようとしないでください。ユーザーは最終的にそれを入力します。爆発するのを待っている「マジックナンバー」コードはすでに十分にあります。そして、問題に対する多様な試行錯誤された解決策があります。
訛り。誰もバッククォートを使用しません。
パイプ文字(|)、おそらく?ユーザーベースがリモートでITに恥ずかしがり屋である場合、このアプローチ(テキストを区切るように求める)は最適なアプローチではない可能性があります。あなたは何か他のものを試すことができます、例えば別の文字列などを受け入れるテキストボックスをその場で動的に追加するいくつかの手段を提供します。
あなたが何をしているのか、そして誰のために行っているのかについてもう少し情報を提供すれば、誰かが別のアプローチを提案する可能性があります。
改行? (つまり、複数行のテキストボックスを使用します)
「;」の使用をお勧めします
可能であれば、通常の人が入力する可能性が低い文字の組み合わせを区切り文字として使用することを好みます。たとえば、 ")^&^("を使用して、コードでconst "cDelimiter"として設定し、すべてのフィールドをそれと連結しました。小さな一意の文字列を使用することで、可能性のあるフードを大幅に削減します。ユーザーが誤って区切り文字を入力した可能性があります。|または〜を入力したユーザーの可能性は確かにありそうにありませんが、それが起こらないという意味ではありません。