4

基本的に任意の文字を含むことができる文字列を区切るための良い/効率的な方法は何でしょうか。たとえば、次のようなn個の文字列を連結する必要があります。

char *str_1 = "foo; for|* 1.234+\"@!`";
char *str_n = "bar; for|* 1.234+%\"@`";

最終的な文字列の場合:

char *str_final = "foo; for|* 1.234+\"@!`bar; for|* 1.234+%\"@`"; // split?

適切に分割するためにどの区切り文字を使用できますか?

連結する文字列が3つ以上ある可能性があることに注意してください。

私は提案を受け付けています。

ありがとう

4

6 に答える 6

3

おそらく、文字列の長さに続いて、すべての文字列の前に特殊文字をエンコードすることができますか?このようにして、次のN文字に含まれる文字について心配する必要はありません。各部分文字列もnullで終了することをお勧めします。

このアプローチの利点の1つは、文字列を非常に高速に解析できることです。

編集:さらに良いアプローチは、エンコードされた長さ+特殊文字の代わりに、以下のコメントでChrisによって提案された最初の2〜4バイトを使用することです。

于 2010-11-04T01:56:42.860 に答える
3

私のコメントはどんどん長くなっているので、ここに完全な答えがあります:

バッファchar *は、文字列の長さを最初のXバイトに格納する必要があります(Pascalのように)。その長さの後に文字列データがあり、好きな文字を含めることができます。その後、次のXバイトで次の文字列の長さがわかります。以下同様に、空の文字列で区切られる最後まで続きます(つまり、最後のXバイトは次の文字列の長さがゼロであると主張し、アプリケーションはこれを信号としてそれ以上の文字列の検索を停止します)。

1つの利点は、文字列データをスキャンする必要がないことです。最初の文字列の先頭から次の文字列を見つけるにはO(1)時間がかかり、リストにある文字列の数を見つけるにはO(n)時間がかかります。それでも非常に高速です(O(n)が受け入れられない場合は、これを回避できますが、今すぐ始める価値はないと思います)。

もう1つの利点は、文字列データに任意の文字を含めることができることです。これは欠点である可能性があります-文字列にNUL文字が含まれている可能性がある場合は、安全に抽出できますが、C文字列関数(strlen()またはstrcat())に渡さないように注意する必要があります。これにより、NUL文字が末尾として表示されます。あなたのデータの(それはそうであるかもしれないし、そうでないかもしれない)。memcpy()ポインタ演算に依存する必要があります。

問題は、Xの値(文字列の長さを格納するために使用するバイト数)です。最も簡単なのは1で、これはすべてのエンディアンと配置の問題をバイパスしますが、文字列を255文字に制限します。これがあなたが生きることができる制限であるならば、素晴らしいです、しかし255は私には少し低いようです。

Xは2バイトまたは4バイトである可能性がありますが、少なくともそのバイト数(stdint.h'suint16_tまたはuint32_t、または多分uint_least16_tまたはuint_least32_t)である(符号なし)データ型があることを確認する必要があります。タイプは、格納したい任意の文字列の長さを格納できることが保証されているためX = sizeof(size_t)、より良い解決策はを作成することです。size_t

アラインメントをX > 1導入し、ネットワークの移植性が問題になる場合はエンディアンを導入します。size_t最初のXバイトを変数として読み取る最も簡単な方法は、char *データをaにキャストしsize_t *、単に逆参照することです。char *ただし、データが適切に配置されていることを保証できない限り、一部のシステムではこれが機能しなくなります。データの整列を保証している場合でもchar *、次の文字列の長さの値が整列されていることを確認するために、ほとんどの文字列の最後で数バイトを浪費する必要があります。

sizeof(size_t)アラインメントを克服する最も簡単な方法は、最初のバイトを手動でsize_t値に変換することです。データをリトルエンディアンとビッグエンディアンのどちらで保存するかを決定する必要があります。ほとんどのコンピューターはネイティブでリトルエンディアンですが、手動変換の場合、これは重要ではありません。1つだけ選択してください。ビッグエンディアンの4バイトに格納された数値65537(2 ^ 16 + 2)は、次のようになり{ 0, 1, 0, 2 }ます。リトルエンディアン、{ 2, 0, 1, 0 }

決定したら(どちらでも構いません)、データの最初のXポイントをunsigned charsにキャストし、次ににキャストsize_tし、適切な指数でビットシフトしてそれらを適切な場所に配置し、それらをすべて一緒に追加します。上記の例では、0に2 ^ 32、1 x 2 ^ 16、0 x 2 ^ 8、2 x 2 ^ 0(または1)を掛けて、0 + 65536 + 0+2または65537を生成します。手動変換を行う場合、ビッグエンディアンとリトルエンディアンの効率の差はゼロになります-私が知る限り、選択は完全に任意であることを(もう一度)指摘したいと思います。

手動変換を行うと、アライメントの問題が回避れ、システム間のエンディアンに関する懸念が完全に回避されるため、リトルエンディアンのコンピューターからビッグエンディアンのコンピューターに転送されるデータも同じように読み取られます。sizeof(size_t) == 4システムからどこにデータが転送されるかについては、まだ潜在的な問題がありsizeof(size_t) == 8ます。これが問題になる場合は、a)捨てsize_tて不変のサイズを選択するか、b)次の値をエンコード(1バイトで十分です)することができます。sizeof(size_t)データの最初のバイトとして送信者に対して、受信者に必要な調整を行わせる。選択a)は簡単かもしれませんが、問題が発生する可能性があります(ネットワーク上のレガシーコンピューターを考慮するには小さすぎるサイズを選択し、段階的に廃止されると、データを保存するためのスペースが不足し始めます)。実行しているシステム(16ビット、32ビット、64ビット、将来的には128ビットでも)に合わせて拡張できるため、b)を選択することをお勧めしますが、そのような作業は必要ない場合があります。 。

</vomit>私が書いたばかりの混乱をすべて整理するのは読者に任せます。

于 2010-11-04T21:45:46.543 に答える
2

文字列が常に有効なUTF-8テキスト(またはASCII)であることがわかっている場合は、有効なUTF-8(またはASCII)に表示できないバイトを区切り文字として使用できます。UTF-8では、バイトC0、C1、F5、F6、F7、F8、F9、FA、FB、FC、FD、FE、およびFFは無効です。ASCIIでは、上位ビットが設定されているバイトは無効です。

于 2010-11-04T02:45:18.130 に答える
2

1つのオプションは、ヌル文字を区切り文字として使用し、ダブルヌルでリストを終了することです。文字列の。次のようになります。

const char* str_final = "foo; for|* 1.234+\"@!`\0bar; for|* 1.234+%\"@`\0";
                                     delimiter ^             delimiter ^ 

Raymond Chenは、ブログ投稿で二重ヌル終了文字列の概要を説明しました。 これは、WindowsAPIのいくつかの関数で使用されます。

于 2010-11-04T01:58:21.240 に答える
2

1つの解決策は、エスケープ文字と区切り文字を選択することです。通常、円記号\はエスケープ文字として使用されますが、文字列リテラルのエスケープ文字であるため、混乱を招く可能性があります。選択は実際には重要ではありません。スラッシュ/をエスケープとして、セミコロン;を区切り文字として使用しましょう。理想的には、文字列で発生する可能性が最も低い2つの文字を選択します。

文字列を連結する場合、最初のステップは、エンコードされていない文字列内の両方の文字を検索し、それらをエスケープされたバージョンに置き換えることです。

str1 = "foo;bar;baz";
str2 = "foo/bar/baz";

になります

estr1 = "foo/;bar/;baz";
estr2 = "foo//bar//baz";

次に、それらは区切り文字と連結されます。

res = "foo/;bar/;baz;foo//bar//baz";

それでおしまい。分割は、先頭のエスケープ文字なしで区切り文字を検索し、単一の文字列のエスケープ文字をエスケープされていないバージョンに置き換えることによって行われます。

strこれは、関数を使用したり、関数とともに出力したりするなど、単一のゼロで終了する文字列を待機する関数を使用して文字列を操作する場合に適していprintfます。独自の関数のみがこれらの文字列で機能することを保証できる場合は、特にゼロ\0で区切る方が効率的です。特に、実際に文字列を分割する必要がないため、ポインタを使用して完全な文字列を使用し、単一の文字列を使用できます。strまたはprintf関数を使用するときの部分的な文字列。

于 2010-11-04T08:46:38.817 に答える
1

2つのアイデア:

1)標準の「エスケープ」アプローチを使用します。これは、Cでchar*リテラルを定義するのと似ています。

2)1'\0'文字を区切り文字として使用し、そのうち2文字を文字列マーカーの終わりとして使用します。

于 2010-11-04T01:57:10.083 に答える