プログラムがこれらの文字を文字列に正しく入力しているかどうかを確認する必要があるため、これらの文字が解析されていないが実際に表示されている「生の」文字列を確認するにはどうすればよいですか?
5 に答える
入力を (ファイルまたはユーザーから) 読み取る場合、特別なエスケープ コードは解析されません。コンパイラがそれらを特別に扱うのは、ソース内の文字列および文字リテラルのみです。
編集:私が話していることを示すための入力と出力を備えた簡単なサンプルプログラム。
#include <stdio.h>
#include <string.h>
int main(int ac, char *av[])
{
char input[32];
printf("Enter input: ");
fgets(input, sizeof(input), stdin);
/* Remove trailing newline */
if (input[strlen(input) - 1] == '\n')
input[strlen(input) - 1] = '\0';
printf("input is \"%s\"\n", input);
return 0;
}
上記のプログラムの実行例:
入力を入力してください: foo\nbar\thello 入力は「foo\nbar\thello」です
この関数fgets
は、文字列の末尾に実際の改行を残します。ただし、シーケンス\n
と\t
入力は、改行またはタブに (それぞれ) 変換されません。これは、これらの特殊文字シーケンスを処理するのは入力関数または出力関数ではなく、コンパイラであるためです。
ソースの文字列または文字リテラル内にこれらのシーケンスがある場合、コンパイラはそれらを認識し、適切な改行、タブ、または記述したものに変更します。ただし、コンパイラはファイルまたはユーザーから読み取られた入力について何も知らないため、これらのシーケンスは変換されません。
編集 2:文字列にリテラルの特殊文字を表示する方法について疑問がある場合は、次のプログラムを参照してください。
#include <stdio.h>
#include <string.h>
#include <ctype.h>
void print_raw_string(const char *str)
{
while (*str != '\0')
{
if (isprint(*str))
fputc(*str, stdout);
else
{
switch (*str)
{
/* First check for known special sequences */
case '\0':
printf("\\0");
break;
case '\a':
printf("\\a");
break;
case '\b':
printf("\\b");
break;
case '\t':
printf("\\t");
break;
case '\n':
printf("\\n");
break;
case '\v':
printf("\\v");
break;
case '\f':
printf("\\f");
break;
case '\r':
printf("\\r");
break;
default:
/* None of the above, print it out as a hex escape sequence */
printf("\\x%02x", *str);
break;
}
}
str++;
}
}
int main(int ac, char *av[])
{
char input[32];
printf("Enter input: ");
fgets(input, sizeof(input), stdin);
printf("Input is: ");
print_raw_string(input);
printf("\n");
return 0;
}
プログラムの実行時:
入力を入力してください: foo bar 入力: foo\tbar\n
考えられる解決策の 1 つは、文字列の内容のASCIIstdin
値を出力することです。
char str[] = "A\tBC1\n";
int j;
for(j = 0; j<strlen(str); str++)
printf("%#x ", str[j]);
出力:
>>0x41 0x9 0x42 0x43 0x31 0xA
標準入力から:
fgets(str, 5, stdin);
for(j = 0; j<strlen(str); j++)
printf("%#x ", str[j]);
入出力:
>> 1 2 <---- That's a tab between 1 and 2
>> 0x31 0x9 0x32 0xA
生の値の代わりに文字をエスケープして\t
(0x9) と\n
(0xA) を表示することができますが、それらを取得していることを確認したいだけであれば、これは機能するはずです (そしてより高速です)。
文字をエスケープするか、次のような独自の特別なルーチンを作成できます。
void print_raw(const char *ch)
{
char *d = ch;
while(*d){
switch(*d){
case '\n':
printf("\\n");
break;
case '\v':
printf("\\n");
break;
case '\r':
printf("\\r");
break;
case '\a':
printf("\\a");
break;
case '\?':
printf("\\?");
break;
case '\"':
printf("\"");
break;
case '\t':
printf("\\t");
break;
case '\b':
printf("\\b");
break;
case '\f':
printf("\\f");
break;
case '\\':
printf("\\");
break;
case '\'':
printf("\'");
break;
default:
putchar(d);
}
}
文字列を次のような一連の文字として出力するだけです:
str は文字列です:
char *ptr = str;
while(*ptr != 0)
{
print("%02X", *ptr);
ptr++;
}
Printf は \n などで特別なことは何もしません。これらの文字シーケンスは、コンパイラによって解釈されます。これを試して:
$ cat foo.c
#include <stdio.h>
#include <string.h>
int
main(int argc, char **argv)
{
if (argc != 2)
return 1;
printf("%d\n", (int)strlen(argv[1]));
printf("%d\n", (int)strlen("\n"));
printf("[%s]\n", argv[1]);
return 0;
}
$ cc -Wall -O2 -o foo foo.c && ./foo '\n'
2
1
[\n]
$
foo を呼び出すときに \n をエスケープしていることに注意してください。エスケープされていない場合、シェルは \ を (異なる方法で) 解釈するからです。