回答編集履歴

2

qiita

2019/01/14 11:20

投稿

yumetodo
yumetodo

スコア5850

test CHANGED
@@ -212,7 +212,13 @@
212
212
 
213
213
  after_loop:
214
214
 
215
+ {
216
+
217
+ char* const lf = strchr(s, '\n');
218
+
215
- strchr(s, '\n')[0] = '\0';
219
+ if(NULL != lf) lf[0] = '\0';
220
+
221
+ }
216
222
 
217
223
  }
218
224
 
@@ -398,10 +404,18 @@
398
404
 
399
405
  }
400
406
 
401
-
402
-
403
407
  ```
404
408
 
405
409
 
406
410
 
407
- [https://wandbox.org/permlink/b7Ev0PdEj3iu7JJP](https://wandbox.org/permlink/b7Ev0PdEj3iu7JJP)
411
+ [https://wandbox.org/permlink/NuxCyJqbMdnrsPCc](https://wandbox.org/permlink/NuxCyJqbMdnrsPCc)
412
+
413
+
414
+
415
+ 追記:
416
+
417
+
418
+
419
+ Qiitaに記事書きました、上記コードに誤りがあった場合はQiitaのほうのみ修正することにします。
420
+
421
+ [「苦しんで覚えるc言語」のとあるコードをもっと安全に、より良く](https://qiita.com/yumetodo/items/bb2885f8c99e02e3d104)

1

全面改訂

2019/01/14 11:20

投稿

yumetodo
yumetodo

スコア5850

test CHANGED
@@ -11,3 +11,397 @@
11
11
 
12
12
 
13
13
  あと`age`と`sex`が`int`型なのが個人的にはどうかと思うのでenumとかunsignedな何かに書き換えたいt頃です。
14
+
15
+
16
+
17
+ ---
18
+
19
+
20
+
21
+ 暇ができたので全面書き直ししました。他にもいろいろ思うところがあったので。
22
+
23
+
24
+
25
+ ```c
26
+
27
+ #include <stdio.h>
28
+
29
+ #include <stdlib.h>
30
+
31
+ #include <string.h>
32
+
33
+ #include <limits.h>
34
+
35
+ #include <errno.h>
36
+
37
+ #include <stdbool.h>
38
+
39
+ #include <ctype.h>
40
+
41
+ #include <float.h>
42
+
43
+ #include <assert.h>
44
+
45
+ #ifndef static_assert
46
+
47
+ # define NO_STDC_STATIC_ASSERT
48
+
49
+ # define static_assert(...)
50
+
51
+ #endif
52
+
53
+ #if defined(_MSC_VER) || defined(__cplusplus)
54
+
55
+ # define restrict
56
+
57
+ #endif
58
+
59
+ /**
60
+
61
+ * @brief 文字列が文字を持っているか調べます。
62
+
63
+ * @param str 対象文字列へのポインタ
64
+
65
+ * @return false: nullptrか空白文字のみの文字列 true:それ以外
66
+
67
+ */
68
+
69
+ static inline bool str_has_char(const char *str)
70
+
71
+ {
72
+
73
+ if (NULL == str) return false;
74
+
75
+ bool ret = false;
76
+
77
+ for (; !ret && *str != '\0'; str++) ret = (*str != ' ');
78
+
79
+ return ret;
80
+
81
+ }
82
+
83
+ /**
84
+
85
+ * @brief 文字列が文字を持っているか調べます。
86
+
87
+ * @param io 書き換えるbool型変数へのポインタ、呼び出し後はポインタが指す変数にnew_valueが代入される
88
+
89
+ * @param new_value 新しい値
90
+
91
+ * @return ioが指すbool変数がもともと持っていた値
92
+
93
+ */
94
+
95
+ static inline bool exchange_bool(bool* restrict const io, const bool new_value)
96
+
97
+ {
98
+
99
+ const bool tmp = *io;
100
+
101
+ *io = new_value;
102
+
103
+ return tmp;
104
+
105
+ }
106
+
107
+ /**
108
+
109
+ * @brief fgetsで失敗したときにストリームをクリアしてループする関数
110
+
111
+ * @param s ストリームから読み取った文字列を格納するための領域へのポインタ
112
+
113
+ * @param buf_size ストリームから読み取った文字列を格納するための領域の大きさ
114
+
115
+ * @param stream FILE構造体へのポインタかstdin
116
+
117
+ * @param message_on_error エラー時に表示してループする
118
+
119
+ * @return 成功時は0, new line at the end of fileのときは-1
120
+
121
+ */
122
+
123
+ static inline int fgets_wrap(char* restrict const s, size_t buf_size, FILE* restrict const stream, const char* restrict message_on_error)
124
+
125
+ {
126
+
127
+ size_t i = 0;
128
+
129
+ for (bool first_flg = true; i < 100 && NULL == fgets(s, buf_size, stream); ++i) {
130
+
131
+ if (feof(stdin)) return -1;
132
+
133
+ if (!exchange_bool(&first_flg, false)) puts((message_on_error) ? message_on_error : "再入力してください");
134
+
135
+ }
136
+
137
+ if (100u == i) exit(1);//無限ループ防止
138
+
139
+ if (feof(stdin)) return 0;
140
+
141
+ //改行文字が入力を受けた配列にない場合、入力ストリームにごみがある
142
+
143
+ const size_t len = strlen(s);
144
+
145
+ //短すぎる入力
146
+
147
+ if (0 == len || (1 == len && '\n' == s[0])) return 1;
148
+
149
+ //長過ぎる入力
150
+
151
+ if ('\n' != s[len - 1]) {
152
+
153
+ //入力ストリームを掃除
154
+
155
+ while (fgetc(stream) != '\n');
156
+
157
+ return 2;
158
+
159
+ }
160
+
161
+ return 0;
162
+
163
+ }
164
+
165
+
166
+
167
+ /**
168
+
169
+ * @brief 標準入力から文字列の入力を受ける
170
+
171
+ * @param s ストリームから読み取った文字列を格納するための領域へのポインタ
172
+
173
+ * @param buf_size ストリームから読み取った文字列を格納するための領域の大きさ
174
+
175
+ * @param message 入力を受ける前にputsに渡す文字列。表示しない場合はnullptrか空白文字のみで構成された文字列へのポインタを渡す
176
+
177
+ * @param message_on_error エラー時に表示してループする
178
+
179
+ */
180
+
181
+ static inline void input_str(char* restrict const s, size_t buf_size, const char* message, const char* restrict message_on_error)
182
+
183
+ {
184
+
185
+ if (str_has_char(message)) puts(message);
186
+
187
+ size_t i = 0;
188
+
189
+ for (; i < 100u; ++i) {
190
+
191
+ //長過ぎる入力以降の無限ループ防止にerrnoをクリアする
192
+
193
+ errno = 0;
194
+
195
+ switch (fgets_wrap(s, buf_size, stdin, message_on_error)) {
196
+
197
+ case -1: return;//EOF
198
+
199
+ case 1://短すぎる入力
200
+
201
+ case 2://長過ぎる入力
202
+
203
+ continue;
204
+
205
+ default: goto after_loop;
206
+
207
+ }
208
+
209
+ }
210
+
211
+ if (100u == i) exit(1);//無限ループ防止
212
+
213
+ after_loop:
214
+
215
+ strchr(s, '\n')[0] = '\0';
216
+
217
+ }
218
+
219
+
220
+
221
+ /**
222
+
223
+ * @brief 標準入力から入力を受け、unsigned int型に変換する
224
+
225
+ * @details fgetsしてstrtodしている。max, minの条件に合わないかエラー時はループ
226
+
227
+ * @details errnoの値を書き換える
228
+
229
+ * @param message 入力を受ける前にputsに渡す文字列。表示しない場合はnullptrか空白文字のみで構成された文字列へのポインタを渡す
230
+
231
+ * @param message_on_error エラー時に表示してループする
232
+
233
+ * @param max 入力値を制限する。最大値を指定
234
+
235
+ * @param min 入力値を制限する。最小値を指定
236
+
237
+ * @return 入力した数字、EOFのときは0
238
+
239
+ */
240
+
241
+ static inline unsigned int input_uint(const char* message, const char* restrict message_on_error, const unsigned int max, const unsigned int min)
242
+
243
+ {
244
+
245
+ if (str_has_char(message)) puts(message);
246
+
247
+ char s[30];
248
+
249
+ static_assert(sizeof(unsigned int) < 8, "err");
250
+
251
+ unsigned long t = 0;
252
+
253
+ size_t i = 0;
254
+
255
+ for (char* endptr = s; ((0 == t && endptr == s) || 0 != errno || t < min || max < t) && i < 100u; ++i) {
256
+
257
+ //長過ぎる入力以降の無限ループ防止にerrnoをクリアする
258
+
259
+ errno = 0;
260
+
261
+ switch (fgets_wrap(s, sizeof(s), stdin, message_on_error)) {
262
+
263
+ case -1: return 0;//EOF
264
+
265
+ case 1://短すぎる入力
266
+
267
+ case 2://長過ぎる入力
268
+
269
+ endptr = s;//ループ制御フラグとして流用
270
+
271
+ continue;
272
+
273
+ default: break;
274
+
275
+ }
276
+
277
+ t = strtoul(s, &endptr, 10);
278
+
279
+ }
280
+
281
+ if (100 == i) exit(1);//無限ループ防止
282
+
283
+ return ((unsigned int)(t));
284
+
285
+ }
286
+
287
+ #ifdef NO_STDC_STATIC_ASSERT
288
+
289
+ # undef static_assert
290
+
291
+ # undef NO_STDC_STATIC_ASSERT
292
+
293
+ #endif
294
+
295
+
296
+
297
+ #ifndef _countof
298
+
299
+ #define _countof(arr) (sizeof(arr) / sizeof(*arr))
300
+
301
+ #endif
302
+
303
+
304
+
305
+ typedef struct
306
+
307
+ {
308
+
309
+ char name[256];
310
+
311
+ unsigned int age;
312
+
313
+ unsigned int sex;
314
+
315
+ }People;
316
+
317
+
318
+
319
+ void InputPeople(People *data)
320
+
321
+ {
322
+
323
+ do {
324
+
325
+ input_str(data->name, _countof(data->name), "名前を入力してください", "不正な長さの入力です、もう一度入力してください");
326
+
327
+ printf("%sでよろしいですか??(「合ってます」:「1」を入力、「間違いました」:「1以外」を入力)\n", data->name);
328
+
329
+ } while (!feof(stdin) && 1 != input_uint(NULL, NULL, UINT_MAX, 0));
330
+
331
+ if (feof(stdin)) exit(1);
332
+
333
+ do {
334
+
335
+ data->age = input_uint("年齢を入力してください", NULL, UINT_MAX, 0);
336
+
337
+ printf("%dでよろしいですか??(「合ってます」:「1」を入力、「間違いました」:「1以外」を入力)\n", data->age);
338
+
339
+ } while (!feof(stdin) && 1 != input_uint(NULL, NULL, UINT_MAX, 0));
340
+
341
+ if (feof(stdin)) exit(1);
342
+
343
+ do {
344
+
345
+ data->sex = input_uint("性別を入力してください(「男性」:「1」を入力、「女性」:「2」を入力)", NULL, 2, 1);
346
+
347
+ printf("%sでよろしいですか??(「合ってます」:「1」を入力、「間違いました」:「1以外」を入力)\n", (1 == data->sex) ? "男性" : "女性");
348
+
349
+ } while (!feof(stdin) && 1 != input_uint(NULL, NULL, UINT_MAX, 0));
350
+
351
+ if (feof(stdin)) exit(1);
352
+
353
+ putchar('\n');
354
+
355
+ }
356
+
357
+ void ShowPeople(const People* data)
358
+
359
+ {
360
+
361
+ printf(
362
+
363
+ "名前:%s\n"
364
+
365
+ "年齢:%d\n"
366
+
367
+ "性別:%s\n",
368
+
369
+ data->name, data->age, (data->sex == 1) ? "男性" : "女性"
370
+
371
+ );
372
+
373
+ putchar('\n');
374
+
375
+ }
376
+
377
+
378
+
379
+ int main(void)
380
+
381
+ {
382
+
383
+ People data[3];
384
+
385
+ for (size_t i = 0; i < 3; i++) {
386
+
387
+ InputPeople(&data[i]);
388
+
389
+ }
390
+
391
+ for (size_t i = 0; i < 3; i++) {
392
+
393
+ ShowPeople(&data[i]);
394
+
395
+ }
396
+
397
+ return 0;
398
+
399
+ }
400
+
401
+
402
+
403
+ ```
404
+
405
+
406
+
407
+ [https://wandbox.org/permlink/b7Ev0PdEj3iu7JJP](https://wandbox.org/permlink/b7Ev0PdEj3iu7JJP)