HHeLiBeXの日記 正道編

日々の記憶の記録とメモ‥

各言語の標準エラー出力

そういえば今まで意識しなかったな、ということで、各言語の標準エラー出力を使ってみたメモ。

環境

手元にあるものということで、環境は以下のものに限定する。

Java

import java.io.IOException;

public class Main {
    public static void main(String[] args) throws IOException {
        byte[] buf = new byte[1024];
        int len;
        while ((len = System.in.read(buf)) > 0) {
            System.err.write(buf, 0, len);
        }
    }
}

System.inが標準入力、System.outが標準出力、System.err標準エラー出力を表す。

ちなみに、System.setIn(InputStream)System.setOut(PrintStream)System.setErr(PrintStream)というのがあるので、例えば標準出力や標準エラー出力をファイルに書き出すように切り替えるとかも可能。

C

#include <stdio.h>

int main(int argc, char** argv) {
    char buf[1024];
    while (fgets(buf, sizeof(buf), stdin)) {
        fputs(buf, stderr);
    }
    return 0;
}

FILE*型だと、stdinが標準入力、stdoutが標準出力、stderr標準エラー出力

ちなみに、int型のファイルディスクリプタ(fd)だと、0が標準入力、1が標準出力、2標準エラー出力に関連付いている。 unistd.hを見ると、以下のように書いてある。

/* Standard file descriptors.  */
#define STDIN_FILENO    0       /* Standard input.  */
#define STDOUT_FILENO   1       /* Standard output.  */
#define STDERR_FILENO   2       /* Standard error output.  */

C++

#include <iostream>

using namespace std;

int main(int argc, char** argv) {
    char buf[1024];
    while (cin.getline(buf, sizeof(buf))) {
        cerr << buf << endl;
    }
    return EXIT_SUCCESS;
}

std::cinが標準入力、std::coutが標準出力、std::cerrstd::clog標準エラー出力にそれぞれ関連付いている。 std::cerrstd::clogの違いは、前者がバッファリングされないのに対して、後者がバッファリングされるというもの。 iostreamを見ると、以下のように書いてある。

namespace std _GLIBCXX_VISIBILITY(default)
{
  // (中略)
  extern istream cin;           /// Linked to standard input
  extern ostream cout;          /// Linked to standard output
  extern ostream cerr;          /// Linked to standard error (unbuffered)
  extern ostream clog;          /// Linked to standard error (buffered)

std::clogの存在は、このヘッダファイルを見て初めて知った。

PHP

<?php

$str = file_get_contents('php://stdin');
file_put_contents('php://stderr', $str);

PHPはどちらかというとWebアプリ開発に使われる言語ということで、特に意識せずにprintとかすると標準出力に書き出されるので、標準入出力を意識することは少ないかもしれない。

'php://stdin'が標準入力、'php://stdout'が標準出力、'php://stderr'標準エラー出力を表すので、これをfile_get_contents / file_put_contentsやfopen等にファイル名と同様に指定してやればよい。

Python 2

import sys

while True:
    line = sys.stdin.readline()
    if line == '':
        break
    sys.stderr.write(line)

sys.stdinが標準入力、sys.stdoutが標準出力、sys.stderr標準エラー出力

Python 3

import sys

while True:
    line = sys.stdin.buffer.readline()
    if line == b'':
        break
    sys.stderr.buffer.write(line)

sys.stdinが標準入力、sys.stdoutが標準出力、sys.stderr標準エラー出力

Ruby

while line = STDIN.gets
    STDERR.puts line
end

STDINが標準入力、STDOUTが標準出力、STDERR標準エラー出力

Perl

while ((my $line = <STDIN>)) {
    print STDERR $line;
}

STDINが標準入力、STDOUTが標準出力、STDERR標準エラー出力

Go

package main

import (
    "bufio"
    "io"
    "os"
)

func main() {
    stdin := bufio.NewReader(os.Stdin)
    stderr := bufio.NewWriter(os.Stderr)
    for {
        ch, err := stdin.ReadByte()
        if err == io.EOF {
            break
        }
        stderr.WriteByte(ch)
    }
    stderr.Flush()
}

os.Stdinが標準入力、os.Stdoutが標準出力、os.Stderr標準エラー出力

bash

#! /bin/bash

cat <&0 >&2

0が標準入力、1が標準出力、2標準エラー出力を表すので、標準エラー出力に出したい場合は、>&2のように書いてリダイレクトしてやればよい。

/dev/stdin/dev/stdout/dev/stderrを使う手もあるので、>> /dev/stderrのようにリダイレクトしてもよい。

Awk

{
    print >> "/dev/stderr";
}

"/dev/stdin"が標準入力、"/dev/stdout"が標準出力、"/dev/stderr"標準エラー出力

まぁ、標準入力や標準出力を明示することはほとんどないけど。

DateTimeクラスのdiffメソッドの罠

突然ですが、以下のコードの出力結果はどうなると思いますか?ちなみに「invert」というのは、結果が負の場合に「1」それ以外の場合に「0」になるプロパティ。

<?php
date_default_timezone_set("Asia/Tokyo");

$dt1 = new DateTime("2017-12-01");
$dt2 = new DateTime("2017-11-30");

$diff1 = $dt1->diff($dt2);
$diff2 = date_diff($dt1, $dt2);

var_dump($diff1->invert, $diff2->invert);

$dt1の方が未来なので、直観的にはint(0)になるだろうと思うところだが・・

ドキュメントは以下だけど・・・

明記されていないが、$dt2 - $dt1の結果になる。すなわち、負になるので、int(1)が出力される。

これは罠過ぎるだろう!!!

なので、DateTimeオブジェクト同士の日時の前後を比較したいだけなら、(PHP 5.2.2以降になるが)比較演算子で比較するようにした方が安全かも。

<?php
date_default_timezone_set("Asia/Tokyo");

$dt1 = new DateTime("2017-12-01");
$dt2 = new DateTime("2017-11-30");

var_dump($dt1 == $dt2);
var_dump($dt1 < $dt2);
var_dump($dt1 <= $dt2);
var_dump($dt1 > $dt2);
var_dump($dt1 >= $dt2);

いやそれリークしますからっ!

以下のようなプログラムを見つけて、思わず机を叩き割ろうかと思いましたよ、えぇ‥

Reader reader = null;
try {
    for (int i = 0; i < files.length; ++i) {
        reader = new BufferedReader(new InputStreamReader(new FileInputStream(files[i])));
        // 以下、いろいろ処理(ただしreaderのclose()以外)
    }
} finally {
    if (reader != null) {
        try { reader.close(); } catch (Exception e) {}
    }
}

いや、これだと、最後に開いたファイルのストリームしか閉じないんですけど‥

バッチプログラムで、長くても数分で終了するプログラムだからまだ影響が少なかったが、Webアプリでこれをやったら確実にアウトですよ、アウト‥

参考

各言語でコマンドライン引数を扱う

そういえばやってなかったなということで、各言語でコマンドライン引数を扱うプログラムを書いてみたメモ。

要件は以下の通り。

  • コマンドライン引数として、3個以上の文字列を与える
  • 標準出力に、以下を順に出力
    • 引数として与えられた文字列を改行区切りで出力
    • 引数の個数を出力
    • 引数の3番目の文字列を出力

環境

手元にあるものということで、環境は以下のものに限定する。

実行例

[各言語でのコマンド実行] a b c d e

出力例

a
b
c
d
e
5
c

Java

public class Main {
    public static void main(String[] args) {
        for (String s : args) {
            System.out.println(s);
        }
        System.out.println(args.length);
        System.out.println(args[2]);
    }
}

C

#include <stdio.h>

int main(int argc, char** argv) {
    for (int i = 1; i < argc; ++i) {
        printf("%s\n", argv[i]);
    }
    printf("%d\n", argc - 1);
    printf("%s\n", argv[3]);

    return 0;
}

C++

#include <iostream>

using namespace std;

int main(int argc, char** argv) {
    for (int i = 1; i < argc; ++i) {
        cout << argv[i] << endl;
    }
    cout << (argc - 1) << endl;
    cout << argv[3] << endl;

    return EXIT_SUCCESS;
}

PHP

<?php

for ($i = 1; $i < count($argv); ++$i) {
    echo $argv[$i] . PHP_EOL;
}
echo (count($argv) - 1) . PHP_EOL;
echo $argv[3] . PHP_EOL;

Python 2

import sys

for i in range(1, len(sys.argv)):
    print sys.argv[i]
print len(sys.argv) - 1
print sys.argv[3]

Python 3

import sys

for i in range(1, len(sys.argv)):
    print(sys.argv[i])
print(len(sys.argv) - 1)
print(sys.argv[3])

Ruby

for a in ARGV
    print a,"\n"
end
print ARGV.length,"\n"
print ARGV[2],"\n"

Perl

for (my $i = 0; $i < @ARGV; ++$i) {
    print $ARGV[$i],"\n";
}
my $argc = @ARGV;
print $argc,"\n";
print $ARGV[2],"\n";

Go

package main

import (
    "fmt"
    "os"
)

func main() {
    for i := 1; i < len(os.Args); i += 1 {
        fmt.Println(os.Args[i])
    }
    fmt.Println(len(os.Args) - 1)
    fmt.Println(os.Args[3]);
}

bash

#! /bin/bash

for c in "$@" ; do
    echo ${c}
done
echo $#
echo $3

まとめ

それぞれの言語ごとの仕様を表にまとめてみる。

言語 変数名 最初の引数の添え字 個数
Java (*1) args 0 args.length
C (*1) argv 1 argc - 1
C++ (*1) argv 1 argc - 1
PHP $argv 1 count($argv) - 1
Python 2/3 sys.argv 1 len(sys.argv) - 1
Ruby ARGV 0 ARGV.length
Perl $ARGV 0 @ARGV (*3)
Go os.Args 1 len(os.Args) - 1
bash $n または ${n} (*2) 1 $#
  • (*1)変数名はmainメソッド/関数の引数の宣言による。
  • (*2)「n」は正の整数。
  • (*3)文脈によっては、「my $argc = @ARGV」などとしてやらないと、個数でなく引数を結合した文字列が得られてしまう。

文字列のマッチング方法によるパフォーマンスの違い

Javaで文字列のパターンマッチをしようと思ったら、以下の3通りに書ける。

String str = "Hello World";
if (str.matches("H.*W")) {
    ...
}
String str = "Hello World";
if (java.util.regex.Pattern.matches("H.*W", str)) {
    ...
}
String str = "Hello World";
java.util.regex.Pattern p = java.util.regex.Pattern.compile("H.*W");
java.util.regex.Matcher m = m.matcher(str);
if (m.matches()) {
    ...
}

まぁ、1番目は内部で2番目の処理を呼び出しているだけだが。

で、3番目のように正規表現コンパイルを事前に行っておく形とそうでない形とでパフォーマンスにどれだけの差が出るのだろうかとふと思ったので計ってみた。

計測に使用したプログラム

import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String str = sc.nextLine();
        String regex = sc.nextLine();

        int delta = 100000;
        int max = 2000000;
        for (int i = delta; i <= max; i += delta) {
            System.out.printf("%8d,%8d,%8d,%8d%n",
                i,
                test1(i, str, regex),
                test2(i, str, regex),
                test3(i, str, regex));
        }
    }
    private static long test1(int count, String str, String regex) {
        long S = System.currentTimeMillis();
        for (int i = 0; i < count; ++i) {
            str.matches(regex);
        }
        long G = System.currentTimeMillis();
        return G - S;
    }
    private static long test2(int count, String str, String regex) {
        long S = System.currentTimeMillis();
        for (int i = 0; i < count; ++i) {
            Pattern.matches(regex, str);
        }
        long G = System.currentTimeMillis();
        return G - S;
    }
    private static long test3(int count, String str, String regex) {
        long S = System.currentTimeMillis();
        Pattern p = Pattern.compile(regex);
        for (int i = 0; i < count; ++i) {
            Matcher m = p.matcher(str);
            m.matches();
        }
        long G = System.currentTimeMillis();
        return G - S;
    }
}

入力

正規表現コンパイルにかかる時間に着目しているので、正規表現の複雑さの違いということで2種類の正規表現を試してみた。

  • a: [a-z]+
  • b: [A-Z][a-z]* [A-Z][a-z]*

計測結果

N test1-a test2-a test3-a
100000 191 64 20
200000 172 68 22
300000 79 64 26
400000 83 78 21
500000 100 99 25
600000 120 121 31
700000 138 140 36
800000 154 153 39
900000 176 177 45
1000000 205 197 50
1100000 217 217 54
1200000 235 239 61
1300000 258 259 65
1400000 279 274 71
1500000 306 292 76
1600000 331 326 86
1700000 340 348 89
1800000 364 362 91
1900000 390 383 97
2000000 410 392 100

f:id:hhelibex:20171126155414p:plain

正規表現 b

N test1-b test2-b test3-b
100000 313 92 19
200000 217 130 46
300000 164 161 42
400000 213 217 52
500000 273 271 66
600000 325 326 79
700000 379 389 89
800000 434 431 104
900000 482 490 117
1000000 540 539 131
1100000 584 588 143
1200000 646 641 156
1300000 694 703 173
1400000 745 752 187
1500000 808 816 195
1600000 883 873 211
1700000 932 927 226
1800000 992 984 241
1900000 1032 1020 254
2000000 1083 1088 271

f:id:hhelibex:20171126155536p:plain

まぁ予想通りだが、毎回コンパイルすることになるtest1、test2のケースは、test3に比べて断然遅いことが分かる。 また、正規表現の複雑さが増すと、コンパイルやマッチングに時間が掛かることも読み取れる。

各言語でメッセージダイジェスト

手元にある各言語で、メッセージダイジェストを出力してみたメモ。

要件は以下の通り。

  • 標準入力から、0バイト以上の任意のバイト列が入力される
  • 標準出力に、入力に対する以下のハッシュ文字列を改行区切りで出力する
    • MD5
    • SHA-1
    • SHA-224
    • SHA-256
    • SHA-384
    • SHA-512

環境

手元にあるものということで、環境は以下のものに限定する。

入力

Hello World!

出力

8ddd8be4b179a529afa5f2ffae4b9858
a0b65939670bc2c010f4d5d6a0b3e4e4590fb92b
de9d76f0f6a015ab6629138a42835e7b44571995e4abb291c0817261
03ba204e50d126e4674c005e04d82e84c21366780af1f43bd54a37816b6ab340
07f60df0b95043b3a3717638e7776ab76ebaa4fc705ba659063229cf162980c04a9f7496dcda50de6510d40fde3eba8a
830445e86a0cfafac4e1531002356f384847a11a7456fb8ccb81ab36e37bff28f34fa2c5bfdd347e964c5c5df0fc305de6394368219307b2ceeb0ec84b7c2b31

Java

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class Main {
    public static void main(String[] args) throws NoSuchAlgorithmException {
        MessageDigest[] mdAry = {
            MessageDigest.getInstance("MD5"),
            MessageDigest.getInstance("SHA-1"),
            MessageDigest.getInstance("SHA-224"),
            MessageDigest.getInstance("SHA-256"),
            MessageDigest.getInstance("SHA-384"),
            MessageDigest.getInstance("SHA-512"),
        };
        try (InputStream in = new BufferedInputStream(System.in)) {
            byte[] b = new byte[1024];
            int len;
            while ((len = in.read(b)) > 0) {
                for (MessageDigest md : mdAry) {
                    md.update(b, 0, len);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        for (MessageDigest md : mdAry) {
            byte[] digest = md.digest();
            for (byte b : digest) {
                System.out.printf("%02x", b);
            }
            System.out.println();
        }
    }
}

C

#include <stdio.h>
#include <string.h>
#include <openssl/md5.h>
#include <openssl/sha.h>

int main(int argc, char** argv) {
    MD5_CTX md5_ctx;
    unsigned char md5[MD5_DIGEST_LENGTH];
    SHA_CTX sha1_ctx;
    unsigned char sha1[SHA_DIGEST_LENGTH];
    SHA256_CTX sha224_ctx;
    unsigned char sha224[SHA224_DIGEST_LENGTH];
    SHA256_CTX sha256_ctx;
    unsigned char sha256[SHA256_DIGEST_LENGTH];
    SHA512_CTX sha384_ctx;
    unsigned char sha384[SHA384_DIGEST_LENGTH];
    SHA512_CTX sha512_ctx;
    unsigned char sha512[SHA512_DIGEST_LENGTH];

    if (!MD5_Init(&md5_ctx)) {
        return 1;
    }
    if (!SHA1_Init(&sha1_ctx)) {
        return 1;
    }
    if (!SHA224_Init(&sha224_ctx)) {
        return 1;
    }
    if (!SHA256_Init(&sha256_ctx)) {
        return 1;
    }
    if (!SHA384_Init(&sha384_ctx)) {
        return 1;
    }
    if (!SHA512_Init(&sha512_ctx)) {
        return 1;
    }

    char buf[1024];
    int len;
    while ((len = fread(buf, sizeof(char), sizeof(buf), stdin)) > 0) {
        if (!MD5_Update(&md5_ctx, buf, len)) {
            return 1;
        }
        if (!SHA1_Update(&sha1_ctx, buf, len)) {
            return 1;
        }
        if (!SHA224_Update(&sha224_ctx, buf, len)) {
            return 1;
        }
        if (!SHA256_Update(&sha256_ctx, buf, len)) {
            return 1;
        }
        if (!SHA384_Update(&sha384_ctx, buf, len)) {
            return 1;
        }
        if (!SHA512_Update(&sha512_ctx, buf, len)) {
            return 1;
        }
    }

    if (!MD5_Final(md5, &md5_ctx)) {
        return 1;
    }
    if (!SHA1_Final(sha1, &sha1_ctx)) {
        return 1;
    }
    if (!SHA224_Final(sha224, &sha224_ctx)) {
        return 1;
    }
    if (!SHA256_Final(sha256, &sha256_ctx)) {
        return 1;
    }
    if (!SHA384_Final(sha384, &sha384_ctx)) {
        return 1;
    }
    if (!SHA512_Final(sha512, &sha512_ctx)) {
        return 1;
    }

    for (int i = 0; i < MD5_DIGEST_LENGTH; ++i) {
        fprintf(stdout, "%02x", md5[i]);
    }
    fprintf(stdout, "\n");
    for (int i = 0; i < SHA_DIGEST_LENGTH; ++i) {
        fprintf(stdout, "%02x", sha1[i]);
    }
    fprintf(stdout, "\n");
    for (int i = 0; i < SHA224_DIGEST_LENGTH; ++i) {
        fprintf(stdout, "%02x", sha224[i]);
    }
    fprintf(stdout, "\n");
    for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i) {
        fprintf(stdout, "%02x", sha256[i]);
    }
    fprintf(stdout, "\n");
    for (int i = 0; i < SHA384_DIGEST_LENGTH; ++i) {
        fprintf(stdout, "%02x", sha384[i]);
    }
    fprintf(stdout, "\n");
    for (int i = 0; i < SHA512_DIGEST_LENGTH; ++i) {
        fprintf(stdout, "%02x", sha512[i]);
    }
    fprintf(stdout, "\n");

    return 0;
}

「-lcrypto」を付けてコンパイルする必要がある。

C++

C言語と大して変わらないけど、一応載せておく。

#include <cstdio>
#include <string>
#include <openssl/md5.h>
#include <openssl/sha.h>

int main(int argc, char** argv) {
    MD5_CTX md5_ctx;
    unsigned char md5[MD5_DIGEST_LENGTH];
    SHA_CTX sha1_ctx;
    unsigned char sha1[SHA_DIGEST_LENGTH];
    SHA256_CTX sha224_ctx;
    unsigned char sha224[SHA224_DIGEST_LENGTH];
    SHA256_CTX sha256_ctx;
    unsigned char sha256[SHA256_DIGEST_LENGTH];
    SHA512_CTX sha384_ctx;
    unsigned char sha384[SHA384_DIGEST_LENGTH];
    SHA512_CTX sha512_ctx;
    unsigned char sha512[SHA512_DIGEST_LENGTH];

    if (!MD5_Init(&md5_ctx)) {
        return EXIT_FAILURE;
    }
    if (!SHA1_Init(&sha1_ctx)) {
        return EXIT_FAILURE;
    }
    if (!SHA224_Init(&sha224_ctx)) {
        return EXIT_FAILURE;
    }
    if (!SHA256_Init(&sha256_ctx)) {
        return EXIT_FAILURE;
    }
    if (!SHA384_Init(&sha384_ctx)) {
        return EXIT_FAILURE;
    }
    if (!SHA512_Init(&sha512_ctx)) {
        return EXIT_FAILURE;
    }

    char buf[1024];
    int len;
    while ((len = fread(buf, sizeof(char), sizeof(buf), stdin)) > 0) {
        if (!MD5_Update(&md5_ctx, buf, len)) {
            return EXIT_FAILURE;
        }
        if (!SHA1_Update(&sha1_ctx, buf, len)) {
            return EXIT_FAILURE;
        }
        if (!SHA224_Update(&sha224_ctx, buf, len)) {
            return EXIT_FAILURE;
        }
        if (!SHA256_Update(&sha256_ctx, buf, len)) {
            return EXIT_FAILURE;
        }
        if (!SHA384_Update(&sha384_ctx, buf, len)) {
            return EXIT_FAILURE;
        }
        if (!SHA512_Update(&sha512_ctx, buf, len)) {
            return EXIT_FAILURE;
        }
    }

    if (!MD5_Final(md5, &md5_ctx)) {
        return EXIT_FAILURE;
    }
    if (!SHA1_Final(sha1, &sha1_ctx)) {
        return EXIT_FAILURE;
    }
    if (!SHA224_Final(sha224, &sha224_ctx)) {
        return EXIT_FAILURE;
    }
    if (!SHA256_Final(sha256, &sha256_ctx)) {
        return EXIT_FAILURE;
    }
    if (!SHA384_Final(sha384, &sha384_ctx)) {
        return EXIT_FAILURE;
    }
    if (!SHA512_Final(sha512, &sha512_ctx)) {
        return EXIT_FAILURE;
    }

    for (int i = 0; i < MD5_DIGEST_LENGTH; ++i) {
        fprintf(stdout, "%02x", md5[i]);
    }
    fprintf(stdout, "\n");
    for (int i = 0; i < SHA_DIGEST_LENGTH; ++i) {
        fprintf(stdout, "%02x", sha1[i]);
    }
    fprintf(stdout, "\n");
    for (int i = 0; i < SHA224_DIGEST_LENGTH; ++i) {
        fprintf(stdout, "%02x", sha224[i]);
    }
    fprintf(stdout, "\n");
    for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i) {
        fprintf(stdout, "%02x", sha256[i]);
    }
    fprintf(stdout, "\n");
    for (int i = 0; i < SHA384_DIGEST_LENGTH; ++i) {
        fprintf(stdout, "%02x", sha384[i]);
    }
    fprintf(stdout, "\n");
    for (int i = 0; i < SHA512_DIGEST_LENGTH; ++i) {
        fprintf(stdout, "%02x", sha512[i]);
    }
    fprintf(stdout, "\n");

    return EXIT_SUCCESS;
}

「-lcrypto」を付けてコンパイルする必要がある。

PHP

<?php

$input = file_get_contents('php://stdin');

printf("%s\n", hash('md5', $input));
printf("%s\n", hash('sha1', $input));
printf("%s\n", hash('sha224', $input));
printf("%s\n", hash('sha256', $input));
printf("%s\n", hash('sha384', $input));
printf("%s\n", hash('sha512', $input));

メモリに載りきらないほどのデータが来たら対応できないのが難点。

Python 2

import sys
import hashlib

md5 = hashlib.md5()
sha1 = hashlib.sha1()
sha224 = hashlib.sha224()
sha256 = hashlib.sha256()
sha384 = hashlib.sha384()
sha512 = hashlib.sha512()

while True:
    line = sys.stdin.readline()
    if line == '':
        break
    md5.update(line)
    sha1.update(line)
    sha224.update(line)
    sha256.update(line)
    sha384.update(line)
    sha512.update(line)

print md5.hexdigest()
print sha1.hexdigest()
print sha224.hexdigest()
print sha256.hexdigest()
print sha384.hexdigest()
print sha512.hexdigest()

Python 3

import sys
import hashlib

md5 = hashlib.md5()
sha1 = hashlib.sha1()
sha224 = hashlib.sha224()
sha256 = hashlib.sha256()
sha384 = hashlib.sha384()
sha512 = hashlib.sha512()

while True:
    line = sys.stdin.buffer.readline()
    if line == b'':
        break
    md5.update(line)
    sha1.update(line)
    sha224.update(line)
    sha256.update(line)
    sha384.update(line)
    sha512.update(line)

print(md5.hexdigest())
print(sha1.hexdigest())
print(sha224.hexdigest())
print(sha256.hexdigest())
print(sha384.hexdigest())
print(sha512.hexdigest())

Ruby

require 'openssl'

mdAry = [
    OpenSSL::Digest.new("md5"),
    OpenSSL::Digest.new("sha1"),
    OpenSSL::Digest.new("sha224"),
    OpenSSL::Digest.new("sha256"),
    OpenSSL::Digest.new("sha384"),
    OpenSSL::Digest.new("sha512"),
]

while line = STDIN.gets
    for md in mdAry
        md.update(line)
    end
end

for md in mdAry
    puts md.hexdigest
end

Perl

use Digest::MD5;
use Digest::SHA;

my $md5 = Digest::MD5->new();
my $sha1 = Digest::SHA->new("sha1");
my $sha224 = Digest::SHA->new("sha224");
my $sha256 = Digest::SHA->new("sha256");
my $sha384 = Digest::SHA->new("sha384");
my $sha512 = Digest::SHA->new("sha512");

while (my $line = readline(STDIN)) {
    $md5->add($line);
    $sha1->add($line);
    $sha224->add($line);
    $sha256->add($line);
    $sha384->add($line);
    $sha512->add($line);
}

print $md5->hexdigest(),"\n";
print $sha1->hexdigest(),"\n";
print $sha224->hexdigest(),"\n";
print $sha256->hexdigest(),"\n";
print $sha384->hexdigest(),"\n";
print $sha512->hexdigest(),"\n";

yum install perl-Digest-MD5 perl-Digest-SHA」が必要。

Go

package main

import (
    "fmt"
    "io"
    "os"
    "crypto/md5"
    "crypto/sha1"
    "crypto/sha256"
    "crypto/sha512"
)

func main() {
    aMd5 := md5.New()
    aSha1 := sha1.New()
    aSha224 := sha256.New224()
    aSha256 := sha256.New()
    aSha384 := sha512.New384()
    aSha512 := sha512.New()

    for {
        buf := make([]byte, 1024)
        n, err := os.Stdin.Read(buf)
        if err == io.EOF {
            break
        }
        buf2 := make([]byte, 0);
        buf2 = append(buf2, buf[0:n]...)

        aMd5.Write(buf2)
        aSha1.Write(buf2)
        aSha224.Write(buf2)
        aSha256.Write(buf2)
        aSha384.Write(buf2)
        aSha512.Write(buf2)
    }

    fmt.Printf("%x\n", aMd5.Sum(nil))
    fmt.Printf("%x\n", aSha1.Sum(nil))
    fmt.Printf("%x\n", aSha224.Sum(nil))
    fmt.Printf("%x\n", aSha256.Sum(nil))
    fmt.Printf("%x\n", aSha384.Sum(nil))
    fmt.Printf("%x\n", aSha512.Sum(nil))
}

bash

#! /bin/bash

cat | tee \
    >(sha512sum | cut -d " " -f 1) \
    >(sha384sum | cut -d " " -f 1) \
    >(sha256sum | cut -d " " -f 1) \
    >(sha224sum | cut -d " " -f 1) \
    >(sha1sum | cut -d " " -f 1) \
    >(md5sum | cut -d " " -f 1) \
    >> /dev/null

バイナリデータのテストケースにも対応するためにteeコマンド+コマンド置換を使ったが、期待する順番通りに出力されないことがあるのが難点。

  • man md5sum
  • man sha1sum
  • man sha224sum
  • man sha256sum
  • man sha384sum
  • man sha512sum

各言語でbase64エンコード/デコード

手元にある各言語で、base64エンコード/デコードをしてみたメモ。

要件は以下の通り。

  • 標準入力は以下の通り構成される
    • 1行目はASCII文字(空白文字を含む)からなる文字列が与えられる
    • 2行目はbase64エンコードされた文字列が与えられる
    • 3行目以降はMIME形式に従ってbase64エンコードされた文字列が与えられる
  • 入力として与えられる「base64エンコードされた文字列」をbase64デコードしても、得られるのはASCII文字(空白文字を含む)のみからなる文字列とする
  • 入力される各行の長さは不明とする(つまり上限設定なし)
  • 入力の不正チェックは不要(上記仕様に従った入力が必ず与えられるとしてよい)
  • 標準出力には以下を出力する
    • 入力の1行目をMIME形式に従ってbase64エンコードした結果の文字列(複数行にわたることがある)
    • 入力の1行目を改行無しでbase64エンコードした結果の文字列
    • 入力の2行目をbase64デコードした結果の文字列
    • 入力の3行目以降をbase64デコードした結果の文字列
    • 各出力の末尾には改行コードを入れること

環境

手元にあるものということで、環境は以下のものに限定する。

なお、以下の言語は自前実装しなきゃならなくて辛いので割愛。

入力

od -cした結果で示す。

0000000   a   b   c   d   e   f   g   h   i   j   k   l   m   n   o   p
0000020   q   r   s   t   u   v   w   x   y   z   A   B   C   D   E   F
0000040   G   H   I   J   K   L   M   N   O   P   Q   R   S   T   U   V
0000060   W   X   Y   Z   0   1   2   3   4   5   6   7   8   9  \n   Y
0000100   W   J   j   Z   G   V   m   Z   2   h   p   a   m   t   s   b
0000120   W   5   v   c   H   F   y   c   3   R   1   d   n   d   4   e
0000140   X   p   B   Q   k   N   E   R   U   Z   H   S   E   l   K   S
0000160   0   x   N   T   k   9   Q   U   V   J   T   V   F   V   W   V
0000200   1   h   Z   W   j   A   x   M   j   M   0   N   T   Y   3   O
0000220   D   k   =  \n   Y   W   J   j   Z   G   V   m   Z   2   h   p
0000240   a   m   t   s   b   W   5   v   c   H   F   y   c   3   R   1
0000260   d   n   d   4   e   X   p   B   Q   k   N   E   R   U   Z   H
0000300   S   E   l   K   S   0   x   N   T   k   9   Q   U   V   J   T
0000320   V   F   V   W   V   1   h   Z   W   j   A   x   M   j   M   0
0000340  \r  \n   N   T   Y   3   O   D   k   =  \r  \n
0000354

人間に分かりやすく示すと大体以下のような感じ。

abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0NTY3ODk=
YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0
NTY3ODk=

ただし3行目以降の各行は「CR LF」で終わっていることに注意。

出力

これもod -cした結果で示すと以下のような感じ。

0000000   Y   W   J   j   Z   G   V   m   Z   2   h   p   a   m   t   s
0000020   b   W   5   v   c   H   F   y   c   3   R   1   d   n   d   4
0000040   e   X   p   B   Q   k   N   E   R   U   Z   H   S   E   l   K
0000060   S   0   x   N   T   k   9   Q   U   V   J   T   V   F   V   W
0000100   V   1   h   Z   W   j   A   x   M   j   M   0  \r  \n   N   T
0000120   Y   3   O   D   k   =  \r  \n   Y   W   J   j   Z   G   V   m
0000140   Z   2   h   p   a   m   t   s   b   W   5   v   c   H   F   y
0000160   c   3   R   1   d   n   d   4   e   X   p   B   Q   k   N   E
0000200   R   U   Z   H   S   E   l   K   S   0   x   N   T   k   9   Q
0000220   U   V   J   T   V   F   V   W   V   1   h   Z   W   j   A   x
0000240   M   j   M   0   N   T   Y   3   O   D   k   =  \n   a   b   c
0000260   d   e   f   g   h   i   j   k   l   m   n   o   p   q   r   s
0000300   t   u   v   w   x   y   z   A   B   C   D   E   F   G   H   I
0000320   J   K   L   M   N   O   P   Q   R   S   T   U   V   W   X   Y
0000340   Z   0   1   2   3   4   5   6   7   8   9  \n   a   b   c   d
0000360   e   f   g   h   i   j   k   l   m   n   o   p   q   r   s   t
0000400   u   v   w   x   y   z   A   B   C   D   E   F   G   H   I   J
0000420   K   L   M   N   O   P   Q   R   S   T   U   V   W   X   Y   Z
0000440   0   1   2   3   4   5   6   7   8   9  \n
0000453

人間に分かりやすく示すと大体以下のような感じ。

YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0
NTY3ODk=
YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0NTY3ODk=
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789

ただし、上の例でいうところの1~2行目の末尾は「CR LF」で終わっていることに注意。

Java

import java.io.IOException;
import java.io.PrintStream;
import java.util.Base64;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        // 今回はASCII文字しかない前提なので手抜きでScannerを使っている
        try (Scanner sc = new Scanner(System.in);
            PrintStream out = new PrintStream(System.out)
        ) {
            String line1 = sc.nextLine();

            // MIME format(encode)
            Base64.Encoder mimeEncoder = Base64.getMimeEncoder(/*76, "\n".getBytes()*/);
            out.write(mimeEncoder.encode(line1.getBytes()));
            out.print("\r\n"); // MimeEncoderでは最後の行にCR LFを付加しないので、その分を補う

            // no wrap format(encode)
            Base64.Encoder encoder = Base64.getEncoder();
            out.write(encoder.encode(line1.getBytes()));
            out.println();

            // no wrap format(decode)
            String line21 = sc.nextLine();
            Base64.Decoder decoder = Base64.getDecoder();
            out.write(decoder.decode(line21));
            out.println();

            // MIME format(decode)
            Base64.Decoder mimeDecoder = Base64.getMimeDecoder();
            StringBuilder sb = new StringBuilder();
            while (sc.hasNextLine()) {
                sb.append(sc.nextLine());
                sb.append("\r\n"); // わざとMIME形式を復元してみる
            }
            String line22 = sb.toString();
            out.write(mimeDecoder.decode(line22));
            out.println();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

PHP

<?php

$lines = file('php://stdin');

// MIME format(encode)
$line1 = array_shift($lines);
$line1 = preg_replace("/[\r\n]/", "", $line1);
print chunk_split(base64_encode($line1)/*, 76, "\n"*/);

// no wrap format(encode)
print base64_encode($line1) . PHP_EOL;

// no wrap format(decode)
$line21 = array_shift($lines);
$line21 = preg_replace("/[\r\n]/", "", $line21);
print base64_decode($line21) . PHP_EOL;

// MIME format(decode)
$line22 = implode('', $lines);
$line22 = preg_replace("/[\r\n]/", "", $line22);
print base64_decode($line22) . PHP_EOL;

Python 2

import sys
import base64

line1 = sys.stdin.readline()
line1 = line1[0:len(line1) - 1] # remove LF
res1 = base64.b64encode(line1)

# MIME format(encode)
for i in range(0, len(res1), 76):
    print res1[i:i + 76] + "\r"

# no wrap format(encode)
print res1

# no wrap format(decode)
line21 = sys.stdin.readline()
print base64.b64decode(line21)

# MIME format(decode)
line22 = ''
while True:
    tmp = sys.stdin.readline()
    if tmp == '':
        break
    line22 = line22 + tmp
print base64.b64decode(line22)

Python 3

import sys
import base64

line1 = sys.stdin.buffer.readline()
line1 = line1[0:len(line1) - 1] # remove LF
res1 = base64.b64encode(line1)

# MIME format(encode)
for i in range(0, len(res1), 76):
    print(res1[i:i + 76].decode() + "\r")

# no wrap format(encode)
print(res1.decode())

# no wrap format(decode)
line21 = sys.stdin.buffer.readline()
print(base64.b64decode(line21).decode())

# MIME format(decode)
line22 = b''
while True:
    tmp = sys.stdin.buffer.readline()
    if tmp == b'':
        break
    line22 = line22 + tmp
print(base64.b64decode(line22).decode())

Ruby

require 'base64'

line1 = STDIN.gets
line1 = line1[0, line1.length() - 1]
res1 = Base64.strict_encode64(line1)

# MIME format(encode)
i = 0
while i < res1.length()
    print res1[i, 76],"\r\n"
    i += 76
end

# no wrap format(encode)
print res1,"\n"

# no wrap format(decode)
line21 = STDIN.gets
line21 = line21[0, line21.length() - 1]
print Base64.strict_decode64(line21),"\n"

# MIME format(decode)
line22 = ""
while tmp = STDIN.gets
    line22 = line22 + tmp
end
print Base64.decode64(line22),"\n"

Perl

use MIME::Base64;

my $line1 = readline(STDIN);
chomp($line1);

# MIME format(encode)
print encode_base64($line1, "\r\n");

# no wrap format(encode)
print encode_base64($line1, ""),"\n";

# no wrap format(decode)
my $line21 = readline(STDIN);
chomp($line21);
print decode_base64($line21),"\n";

# MIME format(decode)
my $line22 = "";
while (my $tmp = readline(STDIN)) {
    $line22 = $line22 . $tmp;
}
print decode_base64($line22),"\n";

Go

package main

import (
    "bufio"
    "fmt"
    "io"
    "os"
    "encoding/base64"
)

func ReadLine(reader *bufio.Reader) (bytes []byte, err error) {
    prefix := false
    buf := make([]byte, 0, 1024)
    line := make([]byte, 0, 1)
    for {
        line, prefix, err = reader.ReadLine()
        if err == io.EOF {
            return
        }
        buf = append(buf, line...)
        if prefix {
            continue
        }
        bytes = buf
        return
    }
}

func main() {
    stdin := bufio.NewReader(os.Stdin)

    line1, err := ReadLine(stdin)
    if err == io.EOF {
        panic(err)
    }
    len1 := base64.StdEncoding.EncodedLen(len(line1))
    res1 := make([]byte, len1);
    base64.StdEncoding.Encode(res1, line1)

    // MIME format(encode)
    for i := 0; i < len(res1); i += 76 {
        end := i + 76
        if end > len(res1) {
            end = len(res1)
        }
        fmt.Println(string(res1[i:end]) + "\r")
    }

    // no wrap format(encode)
    fmt.Println(string(res1))

    // no wrap format(decode)
    line21, err := ReadLine(stdin)
    if err == io.EOF {
        panic(err)
    }
    // base64.StdEncoding.DecodedLenが返す値はあくまで最大値なので、
    // パディングがあったりすると、それよりも短くなることがあるので、
    // Decodeしたときの返り値で実際の長さを取得して切り詰めてやる必要がある。
    len21 := base64.StdEncoding.DecodedLen(len(line21))
    res21 := make([]byte, len21)
    resLen21, _ := base64.StdEncoding.Decode(res21, line21)
    fmt.Println(string(res21[0:resLen21]))

    // MIME format(decode)
    line22 := make([]byte, 0)
    for {
        tmp, err := ReadLine(stdin)
        if err == io.EOF {
            break
        }
        line22 = append(line22, tmp...)
    }
    len22 := base64.StdEncoding.DecodedLen(len(line22))
    res22 := make([]byte, len22)
    resLen22, _ := base64.StdEncoding.Decode(res22, line22)
    fmt.Println(string(res22[0:resLen22]))
}

bash

#! /bin/bash

IFS= read -r line1

# MIME format(encode)
#     base64コマンドは"\n"を付加するので、sedで"\r\n"に変えてやる
echo -n "${line1}" | base64 | sed -e 's/$/\r/'

# no wrap format(encode)
echo -n "${line1}" | base64 -w 0
echo

# no wrap format(decode)
IFS= read -r line21
echo -n "${line21}" | base64 -d
echo

# MIME format(decode)
while IFS=$'\r' read -r tmp ; do
    line22="${line22}${tmp}"
done
echo -n "${line22}" | base64 -d
echo
  • man base64