質問
こちらも参照 C トークナイザー
これは私が書いた C 用の簡単な substr() です (はい、変数の初期化を関数の先頭などに移動する必要がありますが、アイデアはわかります)
私はこれまで、strncpy() を 1 行で呼び出す単純な substr() の「スマートな」実装を数多く見てきました。
これらはすべて間違っています (strncpy は null 終了を保証しないため、呼び出しで正しい部分文字列が生成されない可能性があります)。
ここにもっと良いものがあるでしょうか?
虫を追い出せ!
char* substr(const char* text, int nStartingPos, int nRun)
{
char* emptyString = strdup(""); /* C'mon! This cannot fail */
if(text == NULL) return emptyString;
int textLen = strlen(text);
--nStartingPos;
if((nStartingPos < 0) || (nRun <= 0) || (textLen == 0) || (textLen < nStartingPos)) return emptyString;
char* returnString = (char *)calloc((1 + nRun), sizeof(char));
if(returnString == NULL) return emptyString;
strncat(returnString, (nStartingPos + text), nRun);
/* We do not need emptyString anymore from this point onwards */
free(emptyString);
emptyString = NULL;
return returnString;
}
int main()
{
const char *text = "-2--4--6-7-8-9-10-11-";
char *p = substr(text, -1, 2);
printf("[*]'%s' (\")\n", ((p == NULL) ? "<NULL>" : p));
free(p);
p = substr(text, 1, 2);
printf("[*]'%s' (-2)\n", ((p == NULL) ? "<NULL>" : p));
free(p);
p = substr(text, 3, 2);
printf("[*]'%s' (--)\n", ((p == NULL) ? "<NULL>" : p));
free(p);
p = substr(text, 16, 2);
printf("[*]'%s' (10)\n", ((p == NULL) ? "<NULL>" : p));
free(p);
p = substr(text, 16, 20);
printf("[*]'%s' (10-11-)\n", ((p == NULL) ? "<NULL>" : p));
free(p);
p = substr(text, 100, 2);
printf("[*]'%s' (\")\n", ((p == NULL) ? "<NULL>" : p));
free(p);
p = substr(text, 1, 0);
printf("[*]'%s' (\")\n", ((p == NULL) ? "<NULL>" : p));
free(p);
return 0;
}
出力:
[*]'' (")
[*]'-2' (-2)
[*]'--' (--)
[*]'10' (10)
[*]'10-11-' (10-11-)
[*]'' (")
[*]'' (")
解決
NULL
ed空の文字列ではなく有効でない場合は、私は帰りのmalloc()
を言うでしょう。あなたは関数がif(p)
はなくif(*p == 0)
に失敗したかではないかどうかをテストすることができます。
また、私はemptyString
は一つだけ条件付きでfree()
dされているので、あなたの関数がメモリをリークだと思います。あなたは右free()
前に、すなわち、無条件にそれをreturn
を確認する必要があります。
strncpy()
上のあなたのコメントに関しては(真である)文字列をNUL終端ではないあなたは、むしろcalloc()
よりも文字列を割り当てるためにmalloc()
を使用する場合は、あなたがコピーよりも多くの1つのバイトを割り当てた場合、これは、問題になることはありませんcalloc()
が自動的に0に(この場合、端部を含む)のすべての値を設定するからである。
私はあなたより多くのノートを与えるだろうが、私はキャメルケースコードを読み取る嫌い。ではないことに何か問題があること。
EDIT:あなたのアップデートに関してます:
C規格では、1にかかわらず、お使いのシステムのようにsizeof(char)
を定義することに注意してください。あなたがバイトで9ビットを使用しているコンピュータを使用している場合(神禁じる)、sizeof(char)
はまだsizeof(char)
を言っに何か問題があることを1でないことになるだろう - それは明らかに自分の意思を示し、calloc()
の呼び出しやmalloc()
で対称性を提供します他のタイプのために。しかし、(sizeof(int)
sは、16ビットおよび32ビットとこれら目新しい64ビットコンピュータ上で異なるサイズ可能)、実際に便利int
です。より多くのあなたが知っています。
私はまた、他のほとんどのCコードとその一貫性をあらためて表明したいというよりも、NULL
エラーに""
を返すことです。私はあなたが彼らにNULLを渡すと、おそらく悪いことを行います(strcmp()
のような)多くの機能を知っている - これが予想されます。しかし、C標準ライブラリ(および他の多くのC API)のアプローチ取る「赤ちゃんにそれは、NULL
をチェックする機能の責任は呼び出し側の責任ではないのですが、彼/彼女は、if(s)は、彼はしていません。」あなたはそれを他の方法をしたい場合は、それはクールだが、それはCインタフェース設計の強力なトレンドの一つに対して起こっています。
また、私はむしろstrncpy()
よりmemcpy()
(またはstrncat()
)を使用します。 strncat()
(およびstrcat()
)を使用すると、あなたの意図をあいまいに - それはあなたのコードを見て誰かになります(calloc()
た後、最後の始まりですので、あなたが)文字列の末尾に追加したいと思うとき、あなたは何をしたいのか文字列を設定されています。 strncat()
は、あなたが文字列に追加しているようstrcpy()
(または別のコピー・ルーチン)が、それはより多くのあなたの意図が何であるかのように見せることだろうが、それは、見えるのです。次の3行はすべて、このコンテキストで同じことを行う - あなたが考える方1ピックが素敵に見えます:
strncat(returnString, text + nStartingPos, nRun);
strncpy(returnString, text + nStartingPos, nRun);
memcpy(returnString, text + nStartingPos, nRun);
Plusは、strncpy()
とmemcpy()
はおそらく/ビットstrncat()
よりも効率的に高速(おしっこ少し)となります。
text + nStartingPos
を置くだろうが、あなたは、あなた次第ですに入れたいものは何でもオーダー - nStartingPos + text
はchar *
と同じです。 +
が,
よりも優先順位が高いことからも、彼らの周りの括弧は、不要な(しかし、素敵な)されます。
EDIT 2:3行のコードが同じことをしないが、この文脈では、彼らはすべて同じ結果になります。その上で私をキャッチしてくれてありがとう。
他のヒント
単純な操作であるはずの関数が非常に複雑に見えます。いくつかの問題があります (これらすべてがバグというわけではありません)。
strdup()
, 、およびその他のメモリ割り当て関数、 できる 失敗する場合は、考えられるすべての問題を考慮する必要があります。- 必要な場合にのみリソース (この場合はメモリ) を割り当てます。
- エラーと有効なスティングを区別できるはずです。現時点では、どうかはわかりません。
malloc()
の失敗substr ("xxx",1,1)
または働いているsubstr ("xxx",1,0)
空の文字列を生成します。 - その必要はありません
calloc()
いずれにせよ上書きされるメモリ。 - すべての無効なパラメータはエラーを引き起こすか、有効なパラメータに強制される必要があります (API はそれを文書化する必要があります)。
- ローカルの emptyString を解放した後に NULL に設定する必要はありません。関数が返されると失われます。
- ユーザーする必要はありません
strncat()
- あなた すべき コピーを行う前に、使用可能なサイズとメモリを把握しておくと、(おそらく) より高速に使用できるようになります。memcpy()
. - 文字列オフセットに基数 0 ではなく基数 1 を使用しているのは、C の粒度に反します。
次のセグメントは、私がやろうとしていることです (負の値の Python のイディオムは文字列の末尾から数えることを好みますが、終了位置ではなく長さを維持しています)。
char *substr (const char *inpStr, int startPos, int strLen) {
/* Cannot do anything with NULL. */
if (inpStr == NULL) return NULL;
/* All negative positions to go from end, and cannot
start before start of string, force to start. */
if (startPos < 0)
startPos = strlen (inpStr) + startPos;
if (startPos < 0)
startPos = 0;
/* Force negative lengths to zero and cannot
start after end of string, force to end. */
if (strLen < 0)
strLen = 0;
if (startPos >strlen (inpStr))
startPos = strlen (inpStr);
/* Adjust length if source string too short. */
if (strLen > strlen (&inpStr[startPos]))
strLen = strlen (&inpStr[startPos]);
/* Get long enough string from heap, return NULL if no go. */
if ((buff = malloc (strLen + 1)) == NULL)
return NULL;
/* Transfer string section and return it. */
memcpy (buff, &(inpStr[startPos]), strLen);
buff[strLen] = '\0';
return buff;
}
char* emptyString = strdup(""); /* C'mon! This cannot fail? */
あなたはnullをチェックする必要があります。それはまだヌル文字の1つのバイトを割り当てなければならないことに注意してください。
のstrdupは失敗する可能性があります。しかし別の問題を持っている - それは、標準C関数ではありません。 malloc関数を使用する方が良いでしょう。
また、長さに最初から部分文字列を返すためにMEMMOVE機能を使用することができます。 改善/ paxdiabloの溶液から別の溶液を添加します:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
char *splitstr(char *idata, int start, int slen) {
char ret[150];
if(slen == NULL) {
slen=strlen(idata)-start;
}
memmove (ret,idata+start,slen);
return ret;
}
/*
Usage:
char ostr[]="Hello World!";
char *ores=splitstr(ostr, 0, 5);
Outputs:
Hello
*/
は、それがお役に立てば幸いです。 TCC C CompilierでWindows 7のホームプレミアムでテストされます。