らずらいと姫の挑戦日記(第36回)~文字列~
2017-02-07
文字列は配列を使うと扱いやすい、ということがわかりました。しかし、文字列は一文字ずつ扱うと大変なので「文字列」として扱うための関数が準備されています。今回は、文字列の①コピー②比較③文字列を数字に変換の3つについて学びたいと思います。
コピー
A[n]という配列をB[n]にコピーします。
B[n]=A[n] → ☓ まとめて配列をコピーすることはできません。
B[0]=A[0]・B[1]=A[1] → ○ 1バイトずつコピーすることは出来ますが、大変です。
まとめてコピーする方法
memcpy(B,A,sizeof(B)): AをBにBのサイズ分コピーする
memcpy(B,A,3): AをBに3バイト分コピーする
memset(B,0,sizeof(B)): BにBのサイズ分0(ゼロ)を書く
strcpy(B,”Hello”): 配列Bに”Hello”という文字列をコピーする
ただし、配列Bが”Hello”よりも短い場合にstrcpyを使用すると配列Bのサイズを超えて”HELLO”が書き込まれてしまいます。配列Bの続きのメモリは別の目的で使用されているデータが入っているので、他のデータを壊してしまう可能性があります。
strcpyは、明らかに文字列が配列よりも小さい場合にだけ使用してください。
strncpy(B,”Hello”,sizeof(B)): 配列Bに”Hello”という文字列をコピーする
“Hello”という文字列が配列Bより長い場合、配列Bのサイズまでしかコピーしません。strncpyはより安全にデータをコピーすることが出来ます。
比較用関数
Aが”HELLO”かを知りたい。
Aと”HELLO”を比較する方法は2つあります。
①strcmp(A,”HELLO”): Aを”HELLO”と比較する
②strncmp(A,”HELLO”,sizeof(A)): Aが”HELLO”かをAのサイズ分だけ比較する
いずれもnullコードが出てくるまで比較を続けます。文字化けが発生してしまいnullコードがなくなってしまった場合は、strcmpはnullが見つかるまで比較を続けますが、strncmpは配列のサイズで必ず終了します。
文字を数字に変換する
数値を文字列に変換するのは、strtolを使用するのが便利です。
strtol(文字列、NULL、0)
第一引数: 文字列が格納されている配列を指定します。
第二引数: 文字列の終了ポインタを指定します。NULLを指定しておくと自動認識となります。
第三引数: 文字列の数字の基数を指定します。0にすると自動認識となります。
例えば、
long l = strtok(“100”,NULL,0);
を実行すると、lには10進数の100が保存されます。
続いて、次の関数を実行してみると、lには10進数の16、16進数の0x10が入ります。
long i = strtol(“0x10”,NULL,0)
strtol関数は、10進数/8進数/16進数などの基数を指定しなくても自動で認識してくれるので、文字列を簡単に数値変換してくれます。
その他
そのほかに便利な関数は、strtokです。CSVファイル(カンマ区切り)の文字列を分解して、それぞれを数値に直す時に便利です。
色々と試してみました
今までのことをプログラムにまとめてみましたので、活用してみてくださいね。
プログラム
[c]
#include "hime_mojiretsu_ide.h" // Additional Header
#include "stdlib.h"
char A[32] = "HELLO";
char B[32] = {""};;
void setup() {
// put your setup code here, to run once:
int i;
long l;
char* data[8];
Serial.begin(115200);
Serial.print("A[] = ");
Serial.println(A);
Serial.print("B[] = ");
Serial.println(B);
// 文字列のコピー
A[8] = 1; // ダミーで9バイト目に1を代入しました。
memcpy(B,A,sizeof(B)); // 配列Aを配列Bにコピーします。
Serial.print("B[] = ");
for(i=0;i<16;i++) // 9バイト目に1が入っています
{
Serial.print_long(B[i],DEC);
Serial.print(",");
}
Serial.println("");
memset(B,0,sizeof(B)); // 配列を0にリセットします
Serial.print("B[] = "); // 配列Bが0にクリアされました
for(i=0;i<16;i++)
{
Serial.print_long(B[i],DEC);
Serial.print(",");
}
Serial.println("");
strcpy(B,A); // 配列Aを文字列としてBにコピーします。
Serial.print("B[] = "); // NULLコードまでしかコピーされていません。
for(i=0;i<16;i++)
{
Serial.print_long(B[i],DEC);
Serial.print(",");
}
Serial.println("");
memset(B,0,sizeof(B)); // 配列を0にリセットします
strncpy(B,A,3); // 配列Aを文字列としてBに最大3文字コピーします。
Serial.print("B[] = ");
Serial.println(B);
memset(B,0,sizeof(0));
// 文字列の比較を行います。
if (strcmp(B,A) == 0) Serial.println("B = A"); // B="HE", A="HELLO"で比較を行います
else Serial.println("B != A");
strcpy(B,A);
if (strcmp(B,A) == 0) Serial.println("B = A"); // B="HELLO", A="HELLO"で比較を行いまs。
else Serial.println("B != A");
// 文字列を数値に変換します。
l = strtol("100",NULL,0);
Serial.print("l = ");
Serial.println_long(l,DEC);
l = strtol("0x10",NULL,0);
Serial.print("l = ");
Serial.println_long(l,DEC);
// 最後にCSV形式を数値に変換。
strcpy(A,"100,0×100,0100");
Serial.print("A = ");
Serial.println(A);
data[0] = strtok(A,",\r\n"); // 配列Aをカンマ、または改行マークで区切ります
l = strtol(data[0],NULL,0);
Serial.print("data[0] = ");
Serial.println_long(l,DEC);
for(i=1;i<16;i++) {
data[i] = strtok(NULL,",\r\n"); // 前回の続きから次のカンマ、または改行マークまでを切り出します。
if(data[i] == NULL) break; // 文字列が終わったらおしまい
l = strtol(data[i],NULL,0);
Serial.print("data[");
Serial.print_long(i,DEC);
Serial.print("] = ");
Serial.println_long(l,DEC);
}
}
void loop() {
// put your main code here, to run repeatedly:
}
[/c]