質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

3回答

1678閲覧

文字列を構造体に代入して分解したい

dbfreak

総合スコア20

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

0クリップ

投稿2020/10/25 11:20

編集2020/10/25 14:31

入力データが文字列の固定長データで与えられる場合に、構造体に代入
することで分解できる(?)と聞いたので試してみたのですがうまくいきません。
良いやり方をご教示ください。

固定長データは、年次(4byte)、クラス(4byte)..のようにいくつかの項目を持っており、
これを予め用意しておいた構造体に代入して、アロー演算子でそれぞれの項目のデータを参照できるようにしたいです。

書いてみたコードはこちらです。

C

1#include <stdio.h> 2#include <stdlib.h> 3#include <string.h> 4 5typedef struct{ 6 char year[4]; 7 char class[4]; 8}student; 9 10void strToStudent(student *seito, char *instr){ 11 seito = (student*)instr; 12 printf("year = %s%n" ,seito->year); //1234と表示させたい 13 printf("year = %s%n" ,seito->class); //5678と表示させたい 14} 15 16int main(void){ 17 student *data = NULL; 18 char *str = "12345678" //入力データ。本当は外部モジュールから引き渡される 19 strToStudent(data,str); 20 return 0; 21}
実行結果 year = 12345678 //null文字が無いので1234で止まらず最後までアクセスしてしまう class = 5678

追記
目的としては、
いくつかの項目が連結されている文字列データを手間をかけずにパースする方法を探しています。
おススメの手法があれば、合わせて教えていただけると助かります。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

pepperleaf

2020/10/25 11:28

> 構造体に代入することで分解できる 初めて聞きましたが、Cでできるのでしょうか? 文字列でなく、文字(1byte)という事では?
dodox86

2020/10/25 12:06

分解できると言うよりそのようにとらえることも出来る、と言う程度のことだと思います。各レコードが固定長のデータを扱う時にそのような扱いをすることがあります。
guest

回答3

0

ベストアンサー

既にいくつか回答と指摘をいただいていますが、固定長の文字列を構造体に当てはめて取り扱うのは、それぞれの意味がちゃんと分かってからでないと難しいです。特に構造体はメンバー変数間の境界のパディングやパックの問題もあり、適切な対応をしないと読み取り位置を誤ります。

それらが分かった上で行う限り、例えば以下のようなコードでsscanfの書式指定文字列を工夫することで、\0の付与なくそれぞれの値を正しく取り出すことができることもあります。どうにも理解できないようであれば、実直にそれぞれの文字列部分を別の領域にコピーし、\0を付与して処理しましょう。

C

1#include <stdio.h> 2#include <stdlib.h> 3#include <string.h> 4#include <assert.h> 5 6/* 構造体でアクセスする場合は、構造体メンバーのパディングを意識する必要がある。 7 * C処理系ごとにパックする機能 #pragmaなどがあるので、適時利用する。 8 * gccでは#pragma pack()が使える。 9 */ 10#pragma pack(1) 11struct DATA { 12 char year[4]; /* 年 */ 13 char class[4]; /* クラス */ 14 char s3[3]; /* 3バイトの文字列 */ 15 char s4[4]; /* 4バイトの文字列 */ 16 char v5[5]; /* 数値 */ 17}; 18 19void parse(const char *instr){ 20 21 int year; 22 int class; 23 char s3[16]; 24 char s4[16]; 25 int v5; 26 27 printf("#1 instr=[%s]\n", instr); 28 29 /* 桁数(構造体メンバーのバイト数)を意識してsscanfの書式指定文字列をセット */ 30 int num = sscanf(instr, "%4d%4d%3s%4s%5d", &year, &class, s3, s4, &v5); 31 32 /* 5個の値が正しく取れているか検証 */ 33 assert(num == 5); 34 printf("#2 num=%d, year=%d, class=%d, s3=[%s], s4=[%s], v5=%d\n", 35 num, year, class, s3, s4, v5); 36 37 /* DATA構造体でアクセスしてみる */ 38 const struct DATA *pst = (const struct DATA *)instr; 39 40 /* 構造体メンバーs3のアドレスを指定して取り出す。*/ 41 num = sscanf(pst->s3, "%3s", s3); 42 43 /* 1個の値が正しく取れているか検証 */ 44 assert(num == 1); 45 printf("#3 num=%d, s3=[%s]\n", num, s3); 46} 47 48int main(void){ 49 /* year class s3 s4 v5 */ 50 char str[] = "2020" "0012" "ABC" "DEFG" "89012"; 51 52 parse(str); 53 54 return 0; 55}

実行例です。

console

1$ gcc -Wall t2.c 2$ ./a.out 3#1 instr=[20200012ABCDEFG89012] 4#2 num=5, year=2020, class=12, s3=[ABC], s4=[DEFG], v5=89012 5#3 num=1, s3=[ABC] 6$

投稿2020/10/25 13:39

編集2020/10/25 13:41
dodox86

総合スコア9183

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

dodox86

2020/10/25 13:55

構造体を使う際、破壊(書き込み)可能なものであれば、構造体メンバーそれぞれの末尾に一時的に'\0'を挿入して使ったりすることもあります。char配列のunionにすれば若干扱いやすくなったりしますが、特にお勧めするものではありません。回答に示したコードのsscanfの書式指定文字列を見ても分かるように、「手軽」な方法は特に無いと思って良いです。
dbfreak

2020/10/25 14:52

ご丁寧にありがとうございました。 雑談レベルの話で聞いたので、どうやってやるのか知りたかったのですが楽な方法は無さそうですね。 #pragma pack(n)・・・nの整数倍になるようにメモリ領域をパディングする。 と理解しました。 そのような考慮も必要なのですね、、愚直に処理するとします。 ご指導ありがとうございました。
guest

0

何故、

入力データが文字列の固定長データで与えられる場合に、構造体に代入することで分解できる

ということが言えるのか、よく考えて、その理由を理解しましょう。
分からなければ構造体を復習しましょう。

理解できれば、「どういうデータ構造の時にこれが使えて、どういうデータ構造の時にこれが使えないか」も分かると思います。

あと、質問文のコードはコンパイルエラーになります。コピペするファイルを間違えたのでは?

投稿2020/10/25 13:11

otn

総合スコア84421

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

C言語での文字列、ってのは、文字の配列で、終端に'\0'をつけたもの、です。
printfなどでは(他も同様)文字の並びを出力していき、'\0'に出会うと終端と判断してそこで出力を終了します。

その構造体で文字列を入れて、どういう動作になるのか考えてみればわかるとおもいますよ

#他にもいろいろツッコミどころはありますが

投稿2020/10/25 11:43

編集2020/10/25 11:45
y_waiwai

総合スコア87719

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問