HHeLiBeXの日記 正道編

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

各言語でUTF-8バイト列を文字列置換および文字列分割してみる

各言語でUTF-8のバイト列を読み込み、文字列置換と文字列分割をしてみたメモ。

要件は以下の通り。

  • 標準入力から、文字列が1行だけ入力される。
  • 標準出力に、以下の2つを改行区切りで出力する。
    • 文字列の各文字をすべて'.'で置き換えた文字列
    • 入力文字列の各文字を改行で区切ったもの
  • つまり、10文字の文字列が入力されたら、出力は11行になる

環境

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

Java

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;

public class Main {
    public static void main(String[] args) {
        try (BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
            PrintWriter out = new PrintWriter(System.out)
        ) {
            String s = in.readLine();

            // 文字列置換
            out.println(s.replaceAll(".", "."));

            // 文字列分割
            for (int i = 0; i < s.length(); ++i) {
                char ch1 = s.charAt(i);
                out.print(ch1);
                if (Character.isSurrogate(ch1)) {
                    ++i;
                    char ch2 = s.charAt(i);
                    out.print(ch2);
                }
                out.println();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

C

#include <stdio.h>
#include <string.h>
#include <locale.h>
#include <stdlib.h>
#include <regex.h>

int main(int argc, char** argv) {
    setlocale(LC_ALL, "ja_JP.UTF-8");

    char str[1024];

    fgets(str, sizeof(str), stdin);
    while (str[strlen(str) - 1] == '\n' || str[strlen(str) - 1] == '\r') {
        str[strlen(str) - 1] = '\0';
    }

    regex_t rb;
    if (regcomp(&rb, ".", REG_EXTENDED | REG_NEWLINE)) {
        perror("regcomp");
        return 1;
    }

    const char* p;
    regmatch_t rm;
    int err;
    int idx;

    // 文字列置換
    p = str;
    idx = 0;
    do {
        err = regexec(&rb, p + idx, 1, &rm, 0);
        if (!err) {
            if (rm.rm_so > 0) {
                char buf[1024];
                memset(buf, '\0', sizeof(buf));
                strncpy(buf, p + idx, rm.rm_so);
                fprintf(stdout, "%s", buf);
            }
            fprintf(stdout, ".");
            idx += rm.rm_eo;
        }
    } while (!err);
    fprintf(stdout, "%s\n", p + idx);

    // 文字列分割
    p = str;
    idx = 0;
    do {
        err = regexec(&rb, p + idx, 1, &rm, 0);
        if (!err) {
            char buf[1024];
            memset(buf, '\0', sizeof(buf));
            strncpy(buf, p + idx + rm.rm_so, rm.rm_eo - rm.rm_so);
            fprintf(stdout, "%s\n", buf);
            idx += rm.rm_eo;
        }
    } while (!err);

    regfree(&rb);

    return 0;
}

C++

#include <iostream>
#include <locale>
#include <string>

using namespace std;

int main(int argc, char** argv) {
    setlocale(LC_ALL, "ja_JP.UTF-8");
    wcout.imbue(locale("japanese"));

    wstring str;
    getline(wcin, str);

    // 文字列置換(ごまかし)
    //   regex_matchが完全マッチにしか対応してなくて使えないので。
    for (int i = 0; i < str.length(); ++i) {
        wcout << L".";
    }
    wcout << endl;

    // 文字列分割(ごまかし)
    //   regex_matchが完全マッチにしか対応してなくて使えないので。
    for (int i = 0; i < str.length(); ++i) {
        wcout << str[i] << endl;
    }

    return EXIT_SUCCESS;
}

(2017/12/05追記)

yum install boost-devel」してBoostのライブラリを使うようにしたらまともに動いてくれたので、そのソースコードを追記。コンパイル時に「-lboost_regex」が必要。

#include <iostream>
#include <locale>
#include <string>
#include <boost/regex.hpp>

using namespace std;

int main(int argc, char** argv) {
    setlocale(LC_ALL, "ja_JP.UTF-8");
    wcout.imbue(locale("japanese"));

    wstring str;
    getline(wcin, str);

    boost::wregex re(L".");

    // 文字列置換
    wcout << boost::regex_replace(str, re, L".") << endl;

    // 文字列分割
    boost::wsmatch sm;
    wstring::const_iterator start = str.begin();
    wstring::const_iterator end = str.end();
    int offset = 0;
    while (boost::regex_search(start + offset, end, sm, re)) {
        size_t idx = 0;
        for (int i = 0; i < sm.length(idx); ++i) {
            wcout << str[sm.position(idx) + offset + i];
        }
        wcout << endl;
        offset += sm.position(idx) + sm.length(idx);
    }

    return EXIT_SUCCESS;
}

PHP

<?php

$str = file_get_contents('php://stdin');
$str = trim($str);

mb_regex_encoding('UTF-8');

// 文字列置換
//   mb_xxx系ではereg版しかない
//   パターンの書き方がpreg系の関数と違うことに注意・・
echo mb_ereg_replace('.', '.', $str) . PHP_EOL;

// 文字列分割
//   mb_xxx系ではereg版しかない
//   パターンの書き方がpreg系の関数と違うことに注意・・
$tmp = $str;
do {
    mb_ereg_search_init($tmp, '.');
    $range = mb_ereg_search_pos();
    if ($range !== false) {
        echo substr($tmp, $range[0], $range[1]) . PHP_EOL;
        $tmp = substr($tmp, $range[1]);
    }
} while ($tmp !== false && $range !== false);

Python 2

# -*- coding: UTF-8 -*-
import sys
import re

s = sys.stdin.readline()
ustr = unicode(s, 'UTF-8')
ustr = ustr.replace('\n', '')
ustr = ustr.replace('\r', '')

# 文字列置換
print re.sub(r'.', '.', ustr)

# 文字列分割
for i in range(0, len(ustr)):
    print ustr[i].encode('UTF-8')

Python 3

# -*- coding: UTF-8 -*-
import sys
import re

b = sys.stdin.buffer.readline()
s = str(b, 'UTF-8')
s = s.replace('\n', '')
s = s.replace('\r', '')

# 文字列置換
print(re.sub(r'.', '.', s))

# 文字列分割
for i in range(0, len(s)):
    print(s[i])

Ruby

str = STDIN.gets
str.chomp!()

# 文字列置換
print str.gsub(/./, '.'),"\n"

# 文字列分割
for i in 0...str.size()
    print str[i],"\n"
end

Perl

use Encode;

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

# 文字列置換
my $ustr = decode('UTF-8', $str);
my $tmp = $ustr;
$tmp =~ s/././g;
print $tmp,"\n";

# 文字列分割
my $tmp = $ustr;
for (my $i = 0; $i < length($ustr); ++$i) {
    print encode('UTF-8', substr($tmp, $i, 1)),"\n";
}

Go

package main

import (
    "fmt"
    "os"
    "io"
    "bufio"
    "regexp"
)

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

func main() {
    stdin := bufio.NewReader(os.Stdin)
    s, _ := ReadLine(stdin)

    ss := regexp.MustCompile(`.`).ReplaceAllString(s, ".")
    fmt.Println(ss)

    runes := []rune(s)
    for i := 0; i < len(runes); i += 1 {
        fmt.Println(string(runes[i]))
    }
}

bash

#! /bin/bash

IFS= read s

echo "${s}" | sed -e 's/././g'

echo -n "${s}" | sed -e 's/\(.\)/\1\n/g'