これ、難しいのがなぁ。
まず、C11以降で確かにscanf_s
ってのが定義されている。
一方、それってオプショナルで、事実上、Microsoftの処理系でしか実装されていない(※)。
と言う事はポータブルじゃない、って事になるんだ。
もう一つの問題が「どうやって字数を数えてるのか」。
これって、かなりややこしいんだよ。
基本的に貴方の書いてるコードって「入力がASCIIコード前提」って話になるんだ。っつーかならざるを得ない。
つまり、仮に日本語入力、って前提になると、もう型からして違う、って話になるわけ。
いや、実質上、この辺はコンパイラによって、独自に解決してくれるケースも多いんだけど、その辺実装依存になる、っつーか・・・・・・。
つまり、Microsoftの処理系が「どう解釈するのか」ってのを熟知せなアカン、って話になるんじゃないかなぁ・・・。
多分。
C11以降でUTF-8を扱えるようになってる筈、なんだけど、この辺、甚だ不安定なんだよな。仕様上(苦笑)。
よって、前提としては、(日本と違って世界的には)デファクトスタンダードなC99、あるいはJIS C規格を考慮すると、そもそもwchar_t
を使わな「数えられない」と思う。
そしてその辺がかなりややこしいんだ。
生憎、僕はそんなにCが得意じゃない(どころか嫌いだ・笑)んだけど、調べてみて、ポータブルに書く、って前提だと次のようになるんじゃなかろうか。
詳しい話は識者の人(Cが得意な人)が解説してくれれば嬉しいんだけど(人任せ・笑)・・・。
C
1 # include <stdio.h>
2 # include <stdlib.h>
3 # include <inttypes.h>
4 # include <wchar.h>
5 # include <locale.h>
6
7 # define N 5
8
9 static wchar_t buffer [ 41 ] ;
10
11 typedef struct employee_info
12 {
13 int16_t num ;
14 wchar_t name [ 21 ] ;
15 wchar_t department [ 5 ] ;
16 int16_t phone ;
17 int16_t age ;
18 int32_t salary ;
19 }
20 employee_t ;
21
22 int main ( void )
23 {
24 employee_t p [ N ] = { 0 } ;
25
26 setlocale ( LC_ALL , "" ) ;
27
28 for ( size_t i = 0 ; i < N ; i ++ )
29 {
30 wprintf ( L "%" PRIu64 L "件目\n" , ( uint64_t ) ( i + 1 ) ) ;
31
32 wprintf ( L "従業員番号: " ) ;
33 fgetws ( buffer , 5 , stdin ) ;
34 buffer [ wcscspn ( buffer , L "\n" ) ] = L '\0' ;
35 p [ i ] . num = ( int16_t ) wcstol ( buffer , NULL , 10 ) ;
36
37 wprintf ( L "名前: " ) ;
38 fgetws ( buffer , 21 , stdin ) ;
39 buffer [ wcscspn ( buffer , L "\n" ) ] = L '\0' ;
40 wcscpy ( p [ i ] . name , buffer ) ;
41
42 wprintf ( L "部門: " ) ;
43 fgetws ( buffer , 5 , stdin ) ;
44 buffer [ wcscspn ( buffer , L "\n" ) ] = L '\0' ;
45 wcscpy ( p [ i ] . department , buffer ) ;
46
47 wprintf ( L "年齢: " ) ;
48 fgetws ( buffer , 3 , stdin ) ;
49 buffer [ wcscspn ( buffer , L "\n" ) ] = L '\0' ;
50 p [ i ] . age = ( int16_t ) wcstol ( buffer , NULL , 10 ) ;
51
52 wprintf ( L "内線番号: " ) ;
53 fgetws ( buffer , 5 , stdin ) ;
54 buffer [ wcscspn ( buffer , L "\n" ) ] = L '\0' ;
55 p [ i ] . phone = ( int16_t ) wcstol ( buffer , NULL , 10 ) ;
56
57 wprintf ( L "給与: " ) ;
58 fgetws ( buffer , 10 , stdin ) ;
59 buffer [ wcscspn ( buffer , L "\n" ) ] = L '\0' ;
60 p [ i ] . salary = ( int32_t ) wcstol ( buffer , NULL , 10 ) ;
61 }
62
63 for ( size_t i = 0 ; i < N ; i ++ )
64 {
65 wprintf ( L "%" PRIo64 L "人目: %ls\n" , ( uint64_t ) ( i + 1 ) , p [ i ] . name ) ;
66 wprintf ( L "年齢: %" PRId16 L "\n" , p [ i ] . age ) ;
67 wprintf ( L "従業員番号: %" PRId16 L "\n" , p [ i ] . num ) ;
68 wprintf ( L "部門: %ls\n" , p [ i ] . department ) ;
69 wprintf ( L "内線番号: %" PRId16 L "\n" , p [ i ] . phone ) ;
70 wprintf ( L "給与: %" PRId32 L "\n" , p [ i ] . salary ) ;
71 }
72
73 return EXIT_SUCCESS ;
74
75 }
76
wchar.h
ってヘッダで定義された関数を多用するのが一番信頼性が高いんじゃないか。
んで、読み込みはscanf
とかwscanf
を使うよかfgetws
を使う。バッファbuffer
を定義してそこに文字列として読み込むんだけど、取り込んだ改行文字(L"\n"
)を、ワイド文字 の終端文字、L'\0'
へと変換する。=> L"\n"
の位置検索にwcscspn
を使用
その後、それを数値データに変換する(wcstol
)なり、ワイド文字列とするなり、で構造体のスロットに代入するとかコピー(wcscpy
)する。
かなり面倒くさいんだけど、多分これならどのコンパイラでも動いてくれるんじゃないかな。恐らく。多分。
※: 外野から見ると、Microsoftが提案した機能をCの仕様策定委員会が「敢えて」オプションにしたり、または他のグループが提案したブツもまたオプション化して、Microsoftがそれを実装しなかったり、と、策定委員会がMicrosoft vs. 「その他」に分裂してるように見える。
全く、「なにやっとんねん」と言うカンジだ(笑)。何のための標準化委員会なんだかサッパリ分からん(笑)。
いずれにせよ、「その他」にとってはMicrosoftは「怖い」会社なんだろう。
なお、日本国内では公式ではC言語 = C99なんだけど、長い間Microsoftはキャッチアップして来なかった。現在では最新のC言語仕様はC23と呼ばれるモノだが、Microsoftでは、知ってる限り、現在ではC17に対応はしている。
参考: Visual Studio に C11 および C17 サポートをインストールする
また、C/C++のライブラリ関数に付いては、IBMのページ が良くまとまっている。