01. 入力関係とその処理について
引数の扱い方
全引数を表示するサンプルを、いくつかの言語で確認します。
--- tcsh
引数 : $1, $2, ...
全引数は$*
引数の数は$#
echo $#argv
foreach arg ( $* )
echo $arg
end
--- Perl
引数 : $ARGV[0], $ARGV[1], ...
全引数は@ARGV
引数の数は@ARGV
print @ARGV . "\n";
foreach $arg(@ARGV){
print $arg . "\n";
}
--- Ruby
引数 : ARGV[0], ARGV[1], ...
全引数はARGV(リスト)
引数の数はARGV.size or ARGV.length
puts ARGV.size
ARGV.each do |arg|
puts arg
end
--- Python
引数 : sys.argv[1], sys.argv[2], ...
全引数はsys.argv。ただしこのリストの0番にファイル名が入る
引数の数は len(sys.argv) - 1
import sys
print len(sys.argv) - 1
for val,arg in enumerate(sys.argv):
if(val>0):
print arg
--- C#
引数は Main(string[] args) で取得
using System;
class Test {
public static void Main(string[] args){
foreach(string s in args){
Console.WriteLine("{0}", s);
}
}
}
対話式の書き方
画面に「~を入れてください」的なメッセージを表示させて、ユーザーに何を入れてほしいか明示して入力を受け付けるやり方です。例では一桁の数値を求めていますが、エラー処理は入れていません。
--- tcsh
echo "Please input number (1-9) > "
set KEY = $<
echo "---> " $KEY
--- Perl
print "Please input number (1-9) > ";
$KEY = <STDIN>;
chomp($KEY);
print "---> " . $KEY . "\n";
--- Ruby
puts "Please input number (1-9) > "
KEY = STDIN.gets.chomp
puts "---> " + KEY
--- Python
import sys
print "Please input number (1-9) > "
#KEY = raw_input()
KEY = sys.stdin.readline().rstrip()
print "---> " + KEY
*1 raw_input()を使う場合は、改行コードが含まれないので、rstrip()を使う必要がありません
*2 sys.stdin.readline()を使う場合は、import sysが必要で、改行コードを含むので rstrip()で消しています
--- C#
using System;
class Test {
public static void Main(string[] args){
foreach(string s in args){
Console.Write("Please input number (1-9) > ");
string str = Console.ReadLine();
Console.WriteLine("---> {0}", str);
}
}
}
ファイルの読み方
--- tcsh
このコードだけ見ると、catでいいのですが、スクリプトを組むときは、読み込んだ各行に対して何らかの処理を行います。
foreach line ( "`cat $1`" )
echo $line
end
*1 "`cat xxx`" 部分の " を付けない時は、渡されたファイルの内容はスペースで区切られて展開されます
*2 上記コードは、一部のtcsh特別文字(*とか)はコマンドライン上で指定した時と同じ意味で解釈されるため、そのような文字列を含むファイルには適しません。設定ファイルをテキストで用意して、tcshスクリプトで処理するのであれば、foreachの中で事前処理(grepで引っ掛けるとかsedなどで置換するなど)が必要です。もっとうまくできるかもしれませんが、そういうときはtcshなどでやらず、PerlやRubyなどを使う方がコードの見通しもよいです
--- Perl
open(IN, $ARGV[0]);
while(<IN>){
print $_;
}
close(IN);
*1 Perlの暗黙の了解で print $_ の $_ は省略できますが、あえて書いています
*2 毎行の改行コードが邪魔なとき(実処理では邪魔)は、chomp(); を実行した上で $_ を扱います
--- Ruby
f = open(ARGV[0])
f.each do |line|
print line
end
f.close
--- Python
import sys
f = open(sys.argv[1])
for line in f:
print line,
f.close
*1 print文の最後に「,」を入れると、print文による改行コードの付加を抑止できます
--- C#
▼ファイル全部
using System;
using System.IO;
using System.Text;
class Test {
public static void Main(string[] args){
foreach(string s in args){
StreamReader sr = new StreamReader(args[0]);
string text = sr.ReadToEnd();
sr.Close();
Console.Write(text);
}
}
}
▼ファイルを1行ずつ
using System;
using System.IO;
using System.Text;
class Test {
public static void Main(string[] args){
foreach(string s in args){
StreamReader sr = new StreamReader(args[0]);
string line;
while((line = sr.ReadLine()) != null){
System.Console.WriteLine(line);
}
sr.Close();
}
}
}
読み込んだテキストの扱い方
読み込んだテキストが、数値であるのか文字列であるのか、判断が必要なことがあります。「数値しか来ない」などの前提でコードを書いていると、処理内容が少なければ問題検出もしやすいですが、一定以上になるとデバッグに時間を要してしまいます。
【2017/09/17 修正:アルファベットのみのことを string と書いていましたが、違和感があるので alphabet に置き換えました】
--- tcsh
最初の正規表現で、数値だけ構成されているか否かを判定し、次の正規表現で、アルファベットだけで構成されているか否かを判定することで、数値なのか文字列なのか混在なのかを判定しています。grepの頭に¥をつけるのは、他の人がこのコマンドを使う時にgrepをaliasしているかもしれないからです。
set list = ( 1 a 1f )
foreach tmp ( $list )
echo $tmp | \grep '^[0-9]\+$' > /dev/null
if ( $status == 0 ) then
echo "$tmp is number"
else
echo $tmp | \grep '^[a-zA-Z]\+$' > /dev/null
if ( $status == 0 ) then
echo "$tmp is alphabet"
else
echo "$tmp is mixed"
endif
endif
end
実行結果:
1 is number
a is alphabet
1f is mixed
*1 foeach処理時、tmp=1のときはif文が正常に処理されるが、tmp=aのときにErrorとなります
*2 set list = ( "1" a ) のように、数値に"をつけても変わりません
--- Perl
@array = (1,a,"1f");
foreach $tmp(@array){
$check = $tmp;
if($check =~ /^\d+$/){
print $tmp . " is number\n";
}elsif($check =~ /^[a-zA-Z]+$/){
print $tmp . " is alphabet\n";
}else{
print $tmp . " is mixed\n";
}
}
実行結果:
1 is number
a is alphabet
1f is mixed
*1 配列の中の "1" は、""で囲っても囲わなくても数値をして扱われます
*2 配列の中の "a" は、""で囲っても囲わなくても文字列として扱われます
*3 配列の中の "1f" は、数値とアルファベットの混在であるため、""で囲わなければエラーになります
--- Ruby
Rubyは厳密です。注意しないと意図しない結果になります。
array = [1, "a", "1f"]
array.each do |var|
if(var.to_s =~ /^\d+$/)then #①
#if(/^\d+$/.match(var.to_s))then #①は、この書き方でもOK
puts "#{var} is number"
elsif(var =~ /^[a-zA-Z]+$/)then
puts "#{var} is alphabet"
else
puts "#{var} is mixed"
end
end
実行結果:
1 is number
a is alphabet
1f is mixed
*1 array定義にて数値の1を「1」と書いています。この場合Rubyは、1を数値の1として扱います。①のところで、var.to_sとしなかった場合は、実行結果として「1 is mixed」、つまりelse文で処理されてしまいます。正規表現で意図した処理をするためには、数値(Numeric)を念のため.to_sでstring型にしておくことをおすすめします
*2 テキスト情報を取得した時は、string型で扱われます。プログラム内で数値(Numeric)として後段で扱いたいときは、変数格納時に.to_iを書いておきます
--- Python
Pythonは書き慣れていないので、もう少しうまく書けるかもしれませんが…正規表現ライブラリ(Regular Expression : re)を読み込む必要があります。Ruby同様、「1」と書いた数値は「数値」として扱われるため、for文でarrayを展開したあとで、str(item)でstring変換しています。これをしないとErrorになります。※冗長な行があったため削除しました。
import re
array = [1, "a", "1f"]
for item in array:
if(re.match(r'^\d+$', str(item)) is None):
if(re.match(r'^[a-zA-Z]+$', str(item)) is None):
print item, "is mixed"
else:
print item, "is alphabet"
else:
print item, "is number"
実行結果:
1 is number
a is alphabet
1f is mixed
*1 正規表現の処理は、re.match(arg, arg)で行っています
*2 re.matchの第一引数が r'xxx' になっています。この r は、raw string の意味で、あちこちのPython説明サイトで使われています。「r」なしの正規表現 'xxx' でも、上記コードは同じように動作します
--- C#
C#の場合、1つの配列に数値と文字列を混在できませんでしたので、文字配列として定義しています。正規表現の呼び出しに癖がありますが、正規表現自体は上記と同じ文法のようです。
using System;
class Test {
public static void Main(string[] args){
string[] array = new string[] {"1", "a", "1f"};
foreach(string s in array){
if(System.Text.RegularExpressions.Regex.IsMatch(s, @"^\d+$")){
System.Console.WriteLine("{0} is number",s);
}else if(System.Text.RegularExpressions.Regex.IsMatch( s, @"^[a-zA-Z]+$")){
System.Console.WriteLine("{0} is alphabet",s);
}else{
System.Console.WriteLine("{0} is mixed",s);
}
}
}
}
実行結果:
1 is number
a is alphabet
1f is mixed