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