substr()
文字列から最初n
の文字数を取得するために使用できることを知っています。ただし、最後の数文字を削除したい。Python でできるように、-2
またはC の終了位置として使用することは有効ですか?-3
5 に答える
次のように、文字列を終了させたい場所にヌル終了文字を配置するだけです。
int main()
{
char s[] = "I am a string";
int len = strlen(s);
s[len-3] = '\0';
printf("%s\n",s);
}
これにより、次のことが得られます。
「私はストです」
C は Python とは異なります。文字列インデックスは「スマート」ではありません。文字通りに言うとstr[-3]
、「開始の 3 バイト前の文字」を意味します。このメモリへのアクセスは未定義の動作です。
文字列の最後の数文字を別の文字列として取得したい場合は、必要な最初の文字へのポインターを取得するだけで十分です。
char *endstr = str + (strlen(str) - 3); // get last 3 characters of the string
最後の数文字を削除する場合は、末尾から k 番目の文字を null ( \0
) に設定するだけで十分です。
str[strlen(str)-3] = 0; // delete last three characters
substr()
テストコードを含む、関数の可能な実装は次のとおりです。テストコードは境界をプッシュしないことに注意してください—要求された文字列より短いバッファ長またはゼロのバッファ長。
#include <string.h>
extern void substr(char *buffer, size_t buflen, char const *source, int len);
/*
** Given substr(buffer, sizeof(buffer), "string", len), then the output
** in buffer for different values of len is:
** For positive values of len:
** 0 ""
** 1 "s"
** 2 "st"
** ...
** 6 "string"
** 7 "string"
** ...
** For negative values of len:
** -1 "g"
** -2 "ng"
** ...
** -6 "string"
** -7 "string"
** ...
** Subject to buffer being long enough.
** If buffer is too short, the empty string is set (unless buflen is 0,
** in which case, everything is left untouched).
*/
void substr(char *buffer, size_t buflen, char const *source, int len)
{
size_t srclen = strlen(source);
size_t nbytes = 0;
size_t offset = 0;
size_t sublen;
if (buflen == 0) /* Can't write anything anywhere */
return;
if (len > 0)
{
sublen = len;
nbytes = (sublen > srclen) ? srclen : sublen;
offset = 0;
}
else if (len < 0)
{
sublen = -len;
nbytes = (sublen > srclen) ? srclen : sublen;
offset = srclen - nbytes;
}
if (nbytes >= buflen)
nbytes = 0;
if (nbytes > 0)
memmove(buffer, source + offset, nbytes);
buffer[nbytes] = '\0';
}
#ifdef TEST
#include <stdio.h>
struct test_case
{
const char *source;
int length;
const char *result;
};
static struct test_case tests[] =
{
{ "string", 0, "" },
{ "string", +1, "s" },
{ "string", +2, "st" },
{ "string", +3, "str" },
{ "string", +4, "stri" },
{ "string", +5, "strin" },
{ "string", +6, "string" },
{ "string", +7, "string" },
{ "string", -1, "g" },
{ "string", -2, "ng" },
{ "string", -3, "ing" },
{ "string", -4, "ring" },
{ "string", -5, "tring" },
{ "string", -6, "string" },
{ "string", -7, "string" },
};
enum { NUM_TESTS = sizeof(tests) / sizeof(tests[0]) };
int main(void)
{
int pass = 0;
int fail = 0;
for (int i = 0; i < NUM_TESTS; i++)
{
char buffer[20];
substr(buffer, sizeof(buffer), tests[i].source, tests[i].length);
if (strcmp(buffer, tests[i].result) == 0)
{
printf("== PASS == %2d: substr(buffer, %zu, \"%s\", %d) = \"%s\"\n",
i, sizeof(buffer), tests[i].source, tests[i].length, buffer);
pass++;
}
else
{
printf("!! FAIL !! %2d: substr(buffer, %zu, \"%s\", %d) wanted \"%s\" actual \"%s\"\n",
i, sizeof(buffer), tests[i].source, tests[i].length, tests[i].result, buffer);
fail++;
}
}
if (fail == 0)
{
printf("== PASS == %d tests passed\n", NUM_TESTS);
return(0);
}
else
{
printf("!! FAIL !! %d tests out of %d failed\n", fail, NUM_TESTS);
return(1);
}
}
#endif /* TEST */
関数宣言は適切なヘッダーに含める必要があります。この変数sublen
は、コードが次の場所でクリーンにコンパイルされるのに役立ちます。
gcc -O3 -g -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
-Wold-style-definition -Werror -DTEST substr.c -o substr
テスト出力:
== PASS == 0: substr(buffer, 20, "string", 0) = ""
== PASS == 1: substr(buffer, 20, "string", 1) = "s"
== PASS == 2: substr(buffer, 20, "string", 2) = "st"
== PASS == 3: substr(buffer, 20, "string", 3) = "str"
== PASS == 4: substr(buffer, 20, "string", 4) = "stri"
== PASS == 5: substr(buffer, 20, "string", 5) = "strin"
== PASS == 6: substr(buffer, 20, "string", 6) = "string"
== PASS == 7: substr(buffer, 20, "string", 7) = "string"
== PASS == 8: substr(buffer, 20, "string", -1) = "g"
== PASS == 9: substr(buffer, 20, "string", -2) = "ng"
== PASS == 10: substr(buffer, 20, "string", -3) = "ing"
== PASS == 11: substr(buffer, 20, "string", -4) = "ring"
== PASS == 12: substr(buffer, 20, "string", -5) = "tring"
== PASS == 13: substr(buffer, 20, "string", -6) = "string"
== PASS == 14: substr(buffer, 20, "string", -7) = "string"
== PASS == 15 tests passed
別の回答へのコメントで、cool_sopsは次のように質問します。
なぜこれが機能しないのでしょうか:
memcpy(new_string, old_string, strlen(old_string) - 3; &new_string[strlen(old_string) - 3] = '\0'
と仮定するnew_string
と、old_string
両方がchar
ポインタであり、strlen(old_string) > 3
?
を削除し、欠落しているもの&
を挿入し、ポインタが重複しない有効な位置を指し、長さの条件が満たされていると仮定すると、最後の3文字を除くすべてを古い文字列から新しい文字列にコピーできます。テストコードに埋め込むことでテストできます。質問が主に尋ねているように思われる古い文字列の最後の3文字をコピーすることは試みません。)
;
#include <string.h>
#include <stdio.h>
int main(void)
{
char new_string[32] = "XXXXXXXXXXXXXXXX";
char old_string[] = "string";
memcpy(new_string, old_string, strlen(old_string) - 3);
new_string[strlen(old_string) - 3] = '\0';
printf("<<%s>> <<%s>>\n", old_string, new_string);
return(0);
}
出力:
<<string>> <<str>>
ただし、トリッキーな偶然の一致に注意してください。私は6文字の長さのサンプルの古い文字列を選択しました。-3は「長さ-3」も3に等しくなります。最後のN文字を取得するには、次のようなコードが必要です。
#include <assert.h>
#include <string.h>
#include <stdio.h>
int main(void)
{
int N = 3;
char new_string[32] = "XXXXXXXXXXXXXXXX";
char old_string[] = "dandelion";
int sublen = strlen(old_string) - N;
assert(sublen > 0);
memcpy(new_string, old_string + sublen, N);
new_string[N] = '\0';
printf("<<%s>> <<%s>>\n", old_string, new_string);
return(0);
}
出力:
<<dandelion>> <<ion>>
このような小さなプログラムを書くことは良い習慣であり、教育的である可能性があることに注意してください。たくさんのコードを書くことは、コードを上手に書くための1つの方法です。
注意すべき唯一の落とし穴は、「未定義動作」をテストしている場合、単一のコンパイラから応答を取得するだけですが、他のコンパイラは異なる動作のコードを生成する可能性があるということです。このコードは未定義の動作を実行していないため、問題ありません。未定義の動作を特定するのは難しいため、この解説を部分的に無視することはできますが、コンパイラーで、未定義の動作を特定するのに役立つ厳格な警告オプションを使用してコンパイルするようにしてください。
vignettes
;というディレクトリに(ソース管理下で)保持しているサンプルプログラムがあります。これらは、将来再び必要になる可能性があると思われる場合に参照できる手法を示すプログラムの小さな分割画面です。それらは完全です。彼らが働きます; (これらはこれらの特定の例よりも複雑ですが、私はあなたが持っているよりも長い間Cでプログラミングしてきました;)しかし、それらはおもちゃです—便利なおもちゃです。
いいえ、最後の文字を取得するには、このように strlen() を使用する必要があります。
substr(strlen(str)-4,3);
文字列は 0 ベースであるため、これにより最後の 3 が得られることに注意してください。
というわけで、一般的なテクニックは
substr(strlen(str)-n-1,n);
(もちろん文字列は より長くなければなりませんn
)
最後の 3 つを取得する場合は、次を使用します。
substr(0,strlen(str)-4);
または一般的に
substr(0,strlen(str)-n-1);
は標準の C 関数ではないため、C で使用するのは有効ではないことに注意しsubstr
ました。したがって、最後の数文字を削除して部分文字列を見つけるには、次を使用できます。memcpy(new_string, old_string, strlen(old_string) - 3)