標準入力から1バイトずつ読み込んで、大文字小文字変換をして標準出力に吐き出すプログラムを書いてみる
唐突に、手元にある各言語で標準入力から1バイトずつ読み込んで、大文字小文字変換をしたうえで標準出力に吐き出すプログラムを書いてみようと思ったメモ。
大文字小文字判定等を行う関数をまじめに使った言語もあれば、正規表現に頼った言語もあったり、果てはASCII文字のコード値に頼ったプログラムになる言語があったりといろいろだが、とりあえず現状の知識ということで気にしない。
(2017/10/18追記)Go言語でbyteからruneに変換すればunicodeパッケージの関数で対応できることが分かったので書き換え。
環境
手元にあるものということで、環境は以下のものに限定する。
- CentOS 7
入力ファイル
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