HHeLiBeXの日記 正道編

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

標準入力から1バイトずつ読み込んで、大文字小文字変換をして標準出力に吐き出すプログラムを書いてみる

唐突に、手元にある各言語で標準入力から1バイトずつ読み込んで、大文字小文字変換をしたうえで標準出力に吐き出すプログラムを書いてみようと思ったメモ。

大文字小文字判定等を行う関数をまじめに使った言語もあれば、正規表現に頼った言語もあったり、果てはASCII文字のコード値に頼ったプログラムになる言語があったりといろいろだが、とりあえず現状の知識ということで気にしない。

(2017/10/18追記)Go言語でbyteからruneに変換すればunicodeパッケージの関数で対応できることが分かったので書き換え。

環境

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

入力ファイル

od -cした結果を載せておく。

  • 001.txt
0000000   H   e   l   l   o       W   o   r   l   d   !  \n
0000015
  • 002.txt
0000000       !   "   #   $   %   &   '   (   )   *   +   ,   -   .   /
0000020   0   1   2   3   4   5   6   7   8   9   :   ;   <   =   >   ?
0000040   @   A   B   C   D   E   F   G   H   I   J   K   L   M   N   O
0000060   P   Q   R   S   T   U   V   W   X   Y   Z   [   \   ]   ^   _
0000100   `   a   b   c   d   e   f   g   h   i   j   k   l   m   n   o
0000120   p   q   r   s   t   u   v   w   x   y   z   {   |   }   ~  \n
0000140

Java

import java.io.IOException;

public class Main {
    public static void main(String[] args) {
        try {
            int ch;
            while ((ch = System.in.read()) != -1) {
                if (Character.isLowerCase(ch)) {
                    ch = Character.toUpperCase(ch);
                } else if (Character.isUpperCase(ch)) {
                    ch = Character.toLowerCase(ch);
                }
                System.out.write(ch);
            }
            System.out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

C

#include <stdio.h>
#include <ctype.h>

int main(int argc, char** argv) {
    int ch;

    while ((ch = getchar()) != -1) {
        if (islower(ch)) {
            ch = toupper(ch);
        } else if (isupper(ch)) {
            ch = tolower(ch);
        }
        putchar(ch);
    }

    return 0;
}

C++

#include <iostream>

using namespace std;

int main(int argc, char** argv) {
    char ch;

    while (cin.get(ch)) {
        if (islower(ch)) {
            ch = toupper(ch);
        } else if (isupper(ch)) {
            ch = tolower(ch);
        }
        cout << ch;
    }

    return EXIT_SUCCESS;
}

cctypeはincludeしなくていいのか?と思ったら、iostreamの中身を追っていくと、cctypeをincludeしているので、自分で明示する必要が無いのだった。

PHP

<?php

$in = fopen('php://stdin', 'r');
$out = fopen('php://stdout', 'w');
while (($ch = fgetc($in)) !== FALSE) {
    if (preg_match('/[a-z]/', $ch)) {
        $ch = strtoupper($ch);
    } else if (preg_match('/[A-Z]/', $ch)) {
        $ch = strtolower($ch);
    }
    fputs($out, $ch);
}

最初、ctype系の関数を使おうと思ったのだが、以下の記事を読んだら嫌な予感がしてきたので、正規表現で判定するようにした。

Python 2

import sys;

while True:
    ch = sys.stdin.read(1)
    if ch == '':
        break
    if ch.islower():
        ch = ch.upper()
    elif ch.isupper():
        ch = ch.lower()
    sys.stdout.write(ch)

Pythonのislower/isupperには以下のような罠が潜んでいるらしいので注意。今回は1文字ずつに切り分けているのでハマることはなかったが。

Python 3

import sys;

while True:
    ch = sys.stdin.buffer.read(1)
    if ch == b'':
        break
    if ch.islower():
        ch = ch.upper()
    elif ch.isupper():
        ch = ch.lower()
    sys.stdout.buffer.write(ch)

Ruby

while ch = STDIN.getc
    ch = ch.swapcase
    STDOUT.putc(ch.chr)
end

大文字小文字判定をするメソッドが見つけられなかったので逃げた例‥

Perl

binmode(STDIN);
while (undef != read(STDIN, $ch, 1)) {
    if ($ch =~ /[a-z]/) {
        $ch = uc($ch);
    } elsif ($ch =~ /[A-Z]/) {
        $ch = lc($ch);
    }
    print $ch;
}

Go

  • 初期版
package main

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

func main() {
    stdin := bufio.NewReader(os.Stdin)
    stdout := bufio.NewWriter(os.Stdout)
    for {
        ch, err := stdin.ReadByte()
        if err == io.EOF {
            break
        }
        if ('a' <= ch && ch <= 'z') {
            ch = ch - ('a' - 'A')
        } else if ('A' <= ch && ch <= 'Z') {
            ch = ch + ('a' - 'A')
        }
        stdout.WriteByte(ch)
    }
    stdout.Flush()
}

byteをbyteのままで大文字小文字判定および変換する手段を見つけられずに、ASCIIコードのコード値に頼ってしまいました‥

  • 2017/10/18書き換え版
package main

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

func main() {
    stdin := bufio.NewReader(os.Stdin)
    stdout := bufio.NewWriter(os.Stdout)
    for {
        ch, err := stdin.ReadByte()
        if err == io.EOF {
            break
        }
        r := rune(ch)
        if (unicode.IsLower(r)) {
            r = unicode.ToUpper(r)
        } else if (unicode.IsUpper(r)) {
            r = unicode.ToLower(r)
        }
        stdout.WriteRune(r)
    }
    stdout.Flush()
}

bash

#! /bin/bash

while IFS= read -r -N 1 ch ; do
    printf "%c" "${ch}" | tr 'a-zA-Z' 'A-Za-z'
done