テキスト行の正規化
Posted feedbacks - Flatten
Nested Hidden例題の文字ではうまくいきました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #変換前の文字列例
a = "○○○○\n○○○○○○○\n\n○○○○○\n"
#上の文字列例をパディング文字'☆'を指定して変換した文字列
ans = "○○○○☆☆☆\n○○○○○○○\n☆☆☆☆☆☆☆\n○○○○○☆☆\n"
#最大長の行の長さを測る。
size = a.lines.max_by{|x|x.size}.size
#各行にパディング文字を入れて表示。
p b = a.lines.map{|i|i.chomp}.map{|j|[j,("☆" * ((size - j.size) / 2)),"\n"].join}.join
#結果のチェック
p ans == b
|
(pad "○○○○
○○○○○○○
○○○○○
" #\☆)
;=> "○○○○☆☆☆
○○○○○○○
☆☆☆☆☆☆☆
○○○○○☆☆
"
1 2 3 4 5 6 7 8 9 10 | (defun pad (string &optional (padchar #\Space))
(with-output-to-string (out)
(with-input-from-string (in string)
(loop :for line := (read-line in nil) :while line
:maximize (length line) :into maxlen
:collect line :into lines
:finally (dolist (line lines)
(let ((base (make-string maxlen
:initial-element padchar)))
(format out "~A~%" (replace base line))))))))
|
max関数って、keyを指定できたんだ。 これは便利。 出力結果 12345**** 123****** 123456789 12345**** 12*******
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #coding: utf-8
def padding(inputStr, paddingChar = " "):
maxNum = len(max(inputStr.split("\n"), key=(lambda(x):len(x))))
outputStr = ""
for linedata in inputStr.split("\n"):
outputStr += linedata + paddingChar * (maxNum - len(linedata)) + "\n"
return outputStr
if __name__ == '__main__':
s = '''12345
123
123456789
12345
12'''
print padding(s, "*")
|
>>> >>> print padding(u"○○○○\n○○○○○○○\n\n○○○○○\n", u'☆') ○○○○☆☆☆ ○○○○○○○ ☆☆☆☆☆☆☆ ○○○○○☆☆ ☆☆☆☆☆☆☆
出題通りの答えになっていないようですよ。
>元の文字列の最後は改行です。 これを忘れてた。
1 2 3 4 5 6 7 8 9 | #coding: utf-8
def padding(inputStr, paddingChar = " "):
maxNum = len(max(inputStr.split("\n"), key=(lambda(x):len(x))))
outputStr = ""
- for linedata in inputStr.split("\n"):
+ for linedata in inputStr.split("\n")[:-1]:
outputStr += linedata + paddingChar * (maxNum - len(linedata)) + "\n"
return outputStr
|
schemeの勉強で書いてみました
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | (define (padlines s ch)
(let iter ((ls (string-split s "\n"))
(maxlen 0)
(acc 0))
(if (null? ls)
(string-join (map (lambda (f) (f maxlen))
(reverse (cdr acc))) "")
(let ((len (string-length (car ls))))
(iter (cdr ls)
(if (> len maxlen) len maxlen)
(cons (lambda (n)
(format "~A~A\n"
(car ls)
(make-string (- n len) ch)))
acc))))))
|
1 2 3 4 5 | (define (pad lines ch)
(let iter ((ls (string-split lines "\n"))
(maxlen 0)
- (acc 0))
+ (acc '()))
|
大げさなわりに、スマートじゃない気がしてます。
記号は'o'と'*'を使わせてもらいました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | #include <string>
#include <vector>
#include <iostream>
#include <sstream>
#include <algorithm>
#include <iterator>
#include <functional>
struct Padding : public std::unary_function<std::string, std::string>
{
const int length;
const char c;
Padding(int length, int c) : length(length), c(c)
{
}
std::string operator () (const std::string& s)
{
if(s.size() < length)
{
return s + std::string(length - s.size(), c);
}
else
{
return s;
}
}
};
// 改行を含む文字列を受けて、パディングされた文字列をベクタに格納する関数
void padAllLines(const std::string& src, std::vector<std::string>& strings)
{
strings.clear();
std::stringstream ss(src);
std::string s;
int maxLength = 0;
while(std::getline(ss, s).good())
{
maxLength = maxLength < s.size() ? s.size() : maxLength;
strings.push_back(s);
}
std::transform(strings.begin(), strings.end(), strings.begin(), Padding(maxLength, '*'));
}
int main(int, char* [])
{
std::string src("oooo¥nooooooo¥n¥nooooo¥n");
std::vector<std::string> strings;
padAllLines(src, strings);
std::copy(strings.begin(), strings.end(), std::ostream_iterator<std::string>(std::cout, "¥n"));
return 0;
}
|
こういうトラバーサルを減らす方法、見覚えがある。ということで見よう見まねで single traversal に。
see: Tools and libraries to model and manipulate circular programs
1 2 3 4 5 6 7 | pad ss = ss' where (ss', m) = pad' ss m
pad' [] n = ([], 0)
pad' (s:ss) n = ((take n $ s ++ repeat '*'):ss', max m $ length s)
where (ss', m) = pad' ss n
main = mapM putStrLn . pad . lines =<< getContents
|
Cで。とくに面白いことはやってない。 全角・半角混じりでも動くように、という条件がついたら死地。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | #include<stdio.h>
char *padStr(char *str, char padChar)
{
int len;
char *ret;
int line = 0;
int maxlen = 0;
char *p = str;
char *q;
while (*p) {
for (len = 0; *p && *p != '\n'; p++, len++) { }
if (maxlen < len) maxlen = len;
if (*p == '\n') p++;
line++;
}
ret = (char*)calloc(line * (maxlen + 1) + 1);
p = str;
q = ret;
while (*p){
for (len = 0; *p && *p != '\n'; len++) { *q++ = *p++; }
for (; len < maxlen; len++) { *q++ = padChar; }
if (*p == '\n') { *q++ = *p++; }
if (q-ret >= (line * (maxlen + 1) + 1)) puts("******overflow******");
}
return ret;
}
int main()
{
char *data = "oooo\noooooooo\n\nooooo\no\n";
char *pad = padStr(data, 'x');
printf("[%s]\n", pad);
free(pad);
return 0;
}
|
なんか手続き型っぽい。
1 2 3 4 | padding c ss = [x ++ (replicate (maxLength - (length x)) c) | x <- ss]
where maxLength = maximum [length x | x <- ss]
main = getContents >>= return.unlines.padding '*'.lines >>= putStrLn
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | using System;
class Program {
static void Main(string[] args) {
string r = paddingAllLine("○○○○\r\n○○○○○○○\r\n\r\n○○○○○\r\n", '☆');
Console.WriteLine(r);
}
static string paddingAllLine(string str, char chr) {
string r = "";
int maxLenghs = 0;
foreach(string line in str.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)) {
maxLenghs = Math.Max(maxLenghs, line.Length);
}
foreach(string line in str.Split(new string[] { Environment.NewLine }, StringSplitOptions.None)) {
r += line.PadRight(maxLenghs, chr) + Environment.NewLine;
}
r = r.Remove(r.LastIndexOf(Environment.NewLine, r.LastIndexOf(Environment.NewLine)));
return r + Environment.NewLine;
}
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | if(!String.prototype.x){
String.prototype.x = function(n){
var result="";
for(var i=0;i<n;i++){
result += this;
}
return result;
}
}
var str="○○○○\n○○○○○○○\n\n○○○○○\n";
var wk = [];
var re = /(.*)\n/g;
var max = 0;
var matchs;
var len;
var wk_l=0;
while((matchs=re.exec(str))!=null){
// len=matchs[1].length;
len=re.lastIndex - matchs.index;
wk.push({string:matchs[1], length:len});
++wk_l;
if(max<len) max = len;
}
var result="";
for(var i=0;i<wk_l;++i){
result += wk[i].string + "☆".x(max - wk[i].length) + "\n";
}
print(result);//WScript.Echo(result);
|
pad.c: In function ‘padStr’: pad.c:19: warning: implicit declaration of function ‘calloc’ pad.c:19: warning: incompatible implicit declaration of built-in function ‘calloc’ pad.c:19: error: too few arguments to function ‘calloc’ pad.c: In function ‘main’: pad.c:40: warning: implicit declaration of function ‘free’
とりあえずベタに。
1 2 3 4 | import Data.List
padding text ch = let allLines = lines text
in unlines $ map (\str->take (maximum$map length allLines) (str++repeat ch)) allLines
|
Cはもう出ちゃったけど,せっかくなので投稿.
マイナーな売りとしては,最後の行がnで終わっていなくても期待通りに動きます.
マルチバイト文字は,やっぱり未対応.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#define ARRAY_UNIT_SIZE 16
#define handle_error(str) do { \
perror(str); exit(EXIT_FAILURE); } while (0)
struct array_t {
char **value;
size_t size;
size_t size_max;
};
struct array_t *
array_init(void)
{
struct array_t *ret;
if ((ret = calloc(1, sizeof(*ret))) == NULL)
handle_error("calloc");
return ret;
}
void
array_free(struct array_t *ptr)
{
free(ptr);
}
int
array_push(struct array_t *self, char *value)
{
if (self->size >= self->size_max) {
self->size_max += ARRAY_UNIT_SIZE;
if ((self->value = realloc(self->value,
self->size_max * sizeof(self->value)))
== NULL)
handle_error("realloc");
}
self->value[self->size++] = value;
return 0;
}
static int
_do_push(struct array_t *array, char *pos, size_t len)
{
static int max = 0;
if (len > max) max = len;
array_push(array, pos);
return max;
}
char *
strpad(char *orig, char pad)
{
char *cur, *head, *ret = NULL;
struct array_t *array = array_init();
int len_max = 0;
int i;
for (cur = head = orig; *cur != '\0'; cur++) {
if (*cur != '\n') continue;
len_max = _do_push(array, cur, cur - head);
head = cur + 1;
}
if (cur > head) /* last line was not trailed by CR */
len_max = _do_push(array, cur, cur - head);
if ((cur = ret = calloc(1, array->size * (len_max + 1) + 1))
== NULL)
handle_error("calloc");
head = orig;
for (i = 0; i < array->size; i++) {
size_t str_size = array->value[i] - head;
size_t remain_size = len_max - str_size;
char trailer = *array->value[i];
memcpy(cur, head, str_size); cur += str_size;
memset(cur, pad, remain_size); cur += remain_size;
*cur++ = trailer;
head = array->value[i] + 1;
}
array_free(array);
return ret;
}
int
main(int argc, char **argv)
{
char *str = "foo\nbar baz\n\nhoge\n";
char *result = NULL;
result = strpad(str, '*');
assert(strcmp(result,
"foo****\n"
"bar baz\n"
"*******\n"
"hoge***\n") == 0);
printf("Result #1: [%s]\n\n", result);
free(result);
str = "\nhoge\nfoo bar baz";
result = strpad(str, '^');
assert(strcmp(result,
"^^^^^^^^^^^\n"
"hoge^^^^^^^\n"
"foo bar baz") == 0);
printf("Result #2: [%s]\n", result);
free(result);
exit(EXIT_SUCCESS);
}
|
- lines で1回
- length で1回
- take で1回
の3回はトラバースしているように見えます
とりあえず、普通に。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | public class Sample222 {
public static String paddingRight(String input, char padding) {
String[] splited = input.split("\n", 0);
int maxLen = 0;
for (String s: splited) {
maxLen = Math.max(maxLen, s.length());
}
StringBuilder builder = new StringBuilder();
for (String s: splited) {
builder.append(s);
for (int index = 0, len = maxLen - s.length(); index < len; index++) {
builder.append(padding);
}
builder.append("\n");
}
return builder.toString();
}
public static void main(String[] args) {
System.out.println(paddingRight("○○○○\n○○○○○○○\n\n○○○○○\n", '☆'));
}
}
|
確かに、そう言われれば。
lines と length を展開すれば本当に一回に見えるのかな。考えてみます。
GNU coreutilsのwc前提です。
マルチバイト文字の場合はUTF-8ロケール前提です。
1 2 3 4 5 6 7 8 9 10 11 12 13 | #!/bin/bash
function q222() {
local padchar=$1
local text=$2
local maxlen=$(echo -n "$text" | wc -L)
local padstr=$(yes "$padchar" | head -n $maxlen | tr -d '\n')
echo -n "$text" | sed -e "s/\$/$padstr/;s/^\(.\{$maxlen\}\).*/\1/"
}
q222 '☆' $'○○○○\n○○○○○○○\n\n○○○○○\n'
|
トラバースを2回にしてみました。
1 2 3 4 5 6 7 8 9 10 11 12 | test = "hoge\nPOPOPOPOPO\n\nPINGPONG\n"
lineLens [] n=[]
lineLens ('\n':xs) n= n:lineLens xs 0
lineLens (x:xs) n= lineLens xs (n+1)
padding' [] n ch mlen= []
padding' ('\n':xs) n ch mlen= replicate (mlen-n) ch ++ '\n':padding' xs 0 ch mlen
padding' (x:xs) n ch mlen= x:padding' xs (n+1) ch mlen
padding text ch = let maxLen = maximum $ lineLens text 0
in padding' test 0 '*' maxLen
|
Readerモナドを使ってみかけの変数を減らしてみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | import Control.Monad.Reader
test = "hoge\nPOPOPOPOPO\n\nPINGPONG\n"
data Readable = Readable {pChar::Char,mLength::Int}
maxLineLen [] n= n
maxLineLen ('\n':xs) n= max n (maxLineLen xs 0)
maxLineLen (x:xs) n= maxLineLen xs (n+1)
padding' :: [Char] -> Int -> Reader Readable String
padding' [] n = return []
padding' ('\n':xs) n = paddingStr n >>= \padStr->padding' xs 0 >>=return.(padStr ++)
padding' (x:xs) n = padding' xs (n+1) >>= return.(x:)
paddingStr :: Int -> Reader Readable String
paddingStr n = do mlen <- asks mLength
ch <- asks pChar
return (replicate (mlen-n) ch ++ "\n")
padding text ch = let maxLen = maxLineLen text 0
in runReader (padding' test 0) (Readable ch maxLen)
|
これでトラバースは1回になってるんじゃないでしょうか。 書いてて混乱してきたので、検証していただけると幸いです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | test = "hoge\nPOPOPOPOPO\n\nPINGPONG\n"
padding' [] i m ch= (0,[])
padding' ('\n':xs) i m ch= let (cMax,cStr) = padding' xs 0 (max i m) ch
in (max m cMax,replicate ((max cMax m)-i) ch++'\n':cStr)
padding' (x:xs) i m ch= let (cMax,cStr) = padding' xs (i+1) (max i m) ch
in (max m cMax,x:cStr)
padding text ch = snd $ padding' text 0 0 ch
{-
*Main> padding test '-'
"hoge------\nPOPOPOPOPO\n----------\nPINGPONG--\n"
-}
|
1 2 3 4 5 6 | def padRightAll(String s, String p, n = '\n'){
s.split(n).with{ it*.padRight(it*.size().max(), p[0]).join(n) + n }
}
print r = padRightAll('○○○○\n○○○○○○○\n\n○○○○○\n', '☆')
assert r == '○○○○☆☆☆\n○○○○○○○\n☆☆☆☆☆☆☆\n○○○○○☆☆\n'
|
VCEE2008でノーエラーで通ったから油断してた。
ヘッダファイルは大事ですね。
1 2 3 4 5 6 | 1a2
> #include<stdlib.h>
19c20
< ret = (char*)calloc(line * (maxlen + 1) + 1);
---
> ret = (char*)calloc(line * (maxlen + 1) + 1, 1);
|
マルチバイト処理に間違い。wc -Lは文字数ではなくカラム数を返すのに対し、"○"がEast Asian WidthでAmbiguousなだけでした。
以下、修正版です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | #!/bin/bash
function linemax() {
local line max len
while read -r line; do
len=${#line}
((max = max < len ? len : max))
done
echo $max
}
function q222() {
local padchar=$1
local text=$2
local maxlen=$(echo -n "$text" | linemax)
local padstr=$(yes "$padchar" | head -n $maxlen | tr -d '\n')
echo -n "$text" | sed -e "s/\$/$padstr/;s/^\(.\{$maxlen\}\).*/\1/"
}
q222 '☆' $'○○○○\n○○○○○○○\n\n○○○○○\n'
|
トラバースは1回になっていますね。 パディング文字列を replicate で作ってしまってから 連結するのは、ちょっともったいないかもしれませんね。
確認ありがとうございます。 とりあえず、replicateを使わないように書き直してみました。
勉強がてらArrowやらApplicativeやらMonadやら使ってます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | import Control.Arrow
import Control.Monad.Reader
import Control.Applicative
test = "hoge\nPOPOPOPOPO\n\nPINGPONG\n"
padding' :: String->Int->Int->Reader Char (Int,String)
padding' [] i m = return (0,[])
padding' ('\n':xs) i m = do (cMax,cStr) <- padding' xs 0 (max i m)
newStr <- padding'' ((max cMax m) -i) ('\n':cStr)
return (max m cMax,newStr)
padding' (x:xs) i m = (arr(max m)*** arr(x:)) <$> padding' xs (i+1) (max i m)
padding'' :: Int->String->Reader Char String
padding'' 0 str = return str
padding'' n str = ask>>= padding'' (n-1).(:str)
padding text ch = snd $ runReader (padding' text 0 0) ch
|
Squeak Smalltalk で。
1 2 3 4 5 6 7 8 9 10 11 12 | | in out width |
in := '○○○○
○○○○○○○
○○○○○
'.
out := String new writeStream.
width := 0.
in linesDo: [:line | width := width max: line size].
in linesDo: [:line | out nextPutAll: (line forceTo: width paddingWith: $☆); cr].
out contents
|
Haskellのナイーブなコード
- lines で行に分解する
- maximum . map length で各行の長さをカウントして最大長を求める
- map (++repeat c) で各行末にパディング文字の無限列を連結
- 3.で作った各行の先頭から2.で求めた最大長分だけとる
- unlines で1行にもどす
1 2 3 4 5 6 7 8 9 | normLines0 :: String -> String
normLines0 = unlines . norm0 . lines
norm0 :: [String] -> [String]
norm0 = map . take . maximum . map length <*> map (++repeat c)
(<*>) :: (a -> b -> c) -> (a -> b) -> (a -> c) -- S コンビネータ
(f <*> g) x = (f x) (g x)
infixl 4 <*>
|
パディング文字を定義しわすれました。 パディング文字を引数で渡すバージョンを書きましたので、こちらをどうぞ。
1 2 3 4 5 6 7 8 9 10 | normLines0 :: Char -> String -> String
normLines0 c = unlines . norm0 c . lines
norm0 :: Char -> [String] -> [String]
norm0 c = map . take . maximum . map length <*> map (++repeat c)
(<*>) :: (a -> b -> c) -> (a -> b) -> (a -> c)
(f <*> g) x = f x (g x)
infixl 4 <*>
|
ナイーブ版の unlines と lines の間の部分を1トラバースでやる (*+) は2つの文字列を連結するが同時に前の文字列の長さもカウントする
1 2 3 4 5 6 7 8 9 | norm1 :: Char -> [String] -> [String]
norm1 c xs = xs'
where (m,xs') = f xs
f [] = (0,[])
f (y:ys) = let
(i,y') = y *+ replicate (m-i) c
(j,ys') = f ys
in (max i j, y':ys')
xs *+ ys = foldr (\ z (p,zs) -> (p+1,z:zs)) (0,ys) xs
|
unlines も lines もなしにして、全体を1トラバースでする方法 #8241 と(たぶん)同じ方法
1 2 3 4 5 6 7 8 9 10 11 12 | normLines :: Char -> String -> String
normLines c xs = xs'
where (m,xs') = f 0 xs
f _ [] = (0,"")
f i (x:xs) | x == '\n' = let
(j,ys) = f 0 xs
in (max i j, g (m-i) ('\n':ys))
| otherwise = let
(j,ys) = f (i+1) xs
in (j,x:ys)
g 0 xs = xs
g i xs = c:g (i-1) xs
|
マルチバイト文字に対応しました.
Ambiguous widthな一部の記号('○'とか'☆')について,wcwidth(3)が1を返す問題に対処するため,Markus Kuhn氏が作成されたwcwidthの実装を利用しています.
see: wcwidth.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 | #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <wchar.h>
#include <locale.h>
/* wcwidth.c functions */
int mk_wcwidth_cjk(wchar_t);
int mk_wcswidth_cjk(const wchar_t *, size_t);
#define ARRAY_UNIT_SIZE (sizeof(wchar_t) * 16)
#define handle_error(str) do { \
perror(str); exit(EXIT_FAILURE); } while (0)
#define DEF_ARRAY_NEW(prefix, type) \
type * prefix ## _new(void) { \
type *r; \
if ((r = calloc(1, sizeof(*r))) == NULL) \
handle_error("calloc"); \
return r; \
}
#define DEF_ARRAY_FREE(prefix, type) \
void _ ## prefix ## _free(type *p) { \
free(p); \
}
#define DEF_ARRAY_NPUSH(prefix, type) \
int prefix ## _npush(type *p, u_char *value, size_t size) { \
size_t c; \
for (c = 0; c < size; c++) { \
if (p->size >= p->size_max) { \
p->size_max += ARRAY_UNIT_SIZE; \
if ((p->value = realloc(p->value, p->size_max)) \
== NULL) \
handle_error("realloc"); \
} \
((u_char *)(p->value))[p->size++] = value[c]; \
} \
return 0; \
}
#define DEF_ARRAY_FIT(prefix, type) \
type * prefix ## _fit(type *p) { \
if ((p->value = realloc(p->value, p->size)) == NULL) \
handle_error("realloc"); \
return p; \
}
#define DEF_ARRAY_COMMON(prefix, type) \
DEF_ARRAY_NEW(prefix, type) \
DEF_ARRAY_FREE(prefix, type) \
DEF_ARRAY_NPUSH(prefix, type) \
DEF_ARRAY_FIT(prefix, type)
struct wstr_t {
wchar_t *value;
size_t size; /* in bytes */
size_t size_max; /* in bytes */
};
DEF_ARRAY_COMMON(wstr, struct wstr_t);
int
wstr_push(struct wstr_t *p, wchar_t value)
{
return(wstr_npush(p, (u_char *)&value, sizeof(value)));
}
void
wstr_free(struct wstr_t *p)
{
if (p->value != NULL)
free(p->value);
_wstr_free(p);
}
wchar_t *
wstrpad(wchar_t *orig, wchar_t pad)
{
wchar_t *cur, *ret;
size_t wlen = 0, width = 0, width_max = 0;
struct wstr_t *wstr = wstr_new();
int i;
for (cur = orig; *cur != L'\0'; cur++) {
if (*cur == L'\n') {
width_max = (width > width_max) ? width : width_max;
width = 0;
} else {
width += mk_wcwidth_cjk(*cur);
}
}
wlen = cur - orig;
if ((wlen > 0) && (cur[-1] != L'\n'))
width_max = (width > width_max) ? width : width_max;
width = 0;
for (cur = orig; cur - orig < (wlen + 1); cur++) {
if (*cur == L'\n' || (*cur == L'\0' && width > 0)) {
size_t remain_width = width_max - width;
size_t pad_width = mk_wcwidth_cjk(pad);
for (i = 0; i < remain_width / pad_width; i++)
wstr_push(wstr, pad);
width = 0;
} else {
int w = mk_wcwidth_cjk(*cur);
if (w > 0) width += w;
}
wstr_push(wstr, *cur);
}
wstr_fit(wstr);
if ((ret = calloc(1, wstr->size)) == NULL)
handle_error("calloc");
wmemcpy(ret, wstr->value, wstr->size / sizeof(wchar_t));
wstr_free(wstr);
return ret;
}
void
test_wstrpad(char *test, wchar_t *src, wchar_t pad, wchar_t *compare)
{
wchar_t *padded = wstrpad(src, pad);
char *result = (wcscmp(padded, compare) == 0) ? "Succeeded" : "Failed";
printf("%s => %s.\n[%ls]\n\n", test, result, padded);
free(padded);
}
int
main(int argc, char **argv)
{
if (setlocale(LC_ALL, "") == NULL)
handle_error("setlocale");
test_wstrpad("Test case #1",
L"foo\nbar baz\n\nhoge\n", L'*',
L"foo****\n"
L"bar baz\n"
L"*******\n"
L"hoge***\n");
test_wstrpad("Test case #2",
L"\nhoge\nfoo bar baz", L'^',
L"^^^^^^^^^^^\n"
L"hoge^^^^^^^\n"
L"foo bar baz");
test_wstrpad("Test case #3",
L"○○○○\n○○○○○○○\n\n○○○○○\n", L'☆',
L"○○○○☆☆☆\n"
L"○○○○○○○\n"
L"☆☆☆☆☆☆☆\n"
L"○○○○○☆☆\n");
test_wstrpad("Test case #4",
L"○○○○\n○○○○○○○\n\n○○○○○\n", L'*',
L"○○○○******\n"
L"○○○○○○○\n"
L"**************\n"
L"○○○○○****\n");
test_wstrpad("Test case #5",
L"foo\nbar baz\n\nhoge", L'■',
L"foo■■\n"
L"bar baz\n"
L"■■■\n"
L"hoge■");
exit(EXIT_SUCCESS);
}
|
○○○○☆☆☆
○○○○○○○
☆☆☆☆☆☆☆
○○○○○☆☆
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | Array.prototype.each = function(iterator){
for(var i=0, length=this.length; i<length; i++) iterator(this[i],i);
}
String.prototype.width = function(){
var result = this.length;
for(var i=0; i<this.length; i++) {
if(this.charCodeAt(i) >= 128) result++;
if(this.charAt(i).match(/[。-゚]/)) result--;
}
return result;
}
String.prototype.times = function(count){
var result = '';
for(var i=0; i<count; i++) result += this;
return result;
}
//----------------------------
function textJustify(text,padding){
var result = text.split("\n");
var last = result.length - 1;
var maxLength = 0;
result.each(function(v){
maxLength = v.width()>maxLength? v.width(): maxLength;
});
result.each(function(v,i){
if((i!=last)||(v!="")){
var restLength = (maxLength - v.width()) / padding.width();
result[i] = v + padding.times(restLength|0);
}
});
return result.join("\n");
}
//----------------------------
//test
WScript.Echo(textJustify("○○○○\n○○○○○○○\n\n○○○○○\n","☆"));
|
LINQを使って処理を行いました。 変数の数を減らすことに留意しました。 これ以上変数を減らすとパフォーマンス落ちそうなので、この程度で。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | class MyMain
{
public static void Main(string[] args)
{
Console.WriteLine(TextRegex.action("○○○○\n○○○○○○○\n\n○○○○○\n", '☆'));
Console.ReadLine();
}
}
class TextRegex
{
static public string action(string str, char c)
{
List<string> lineList = str.Split(new string[] { Environment.NewLine, "\n" }, StringSplitOptions.RemoveEmptyEntries).ToList();
int maxLen = lineList.Max(s => s.Length);
return string.Join( Environment.NewLine, lineList.Select(s => s.PadRight(maxLen, c)).ToArray() );
}
}
|
1 2 3 4 5 6 | (use srfi-13)
(define (padlines s ch)
(let* ((ss (string-split s #\newline))
(n (apply max (map string-length ss))))
(string-join (map (cut string-pad-right <> n ch) ss) "\n")))
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #!/usr/bin/perl
use strict;
use warnings;
use utf8;
use Encode;
use Getopt::Long;
use List::Util qw/max/;
binmode(STDIN, ':utf8');
binmode(STDOUT, ':utf8');
my $pad_char = '☆';
GetOptions('padding-char=s' => sub { $pad_char = decode 'utf8', $_[1] });
sub pad {
my $pad_char = shift;
my @lines = split /\n/, shift;
my $len = max map length, @lines;
join '', map { $_ . $pad_char x ($len - length($_)) . "\n" } @lines;
}
print pad($pad_char, do { local $/; <> }), "\n";
|
ひねりが無いですが...文字列から文字列へとのことでしたので、awkらしい標準入力→標準出力ではなく、あえて関数にしてみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | BEGIN {
a = "1234\n1234567\n\n12345\n";
pad = "@";
print padding( a, pad );
}
function padding( src, pad ) {
maxlen=0;
cnt = split(src, b, "\n");
for(i=1;i<=cnt;i++) {
if( length(b[i])>maxlen )
maxlen=length(b[i]);
}
output = "";
for (i=1; i<cnt; i++) {
for(;maxlen>length(b[i]);)
b[i] = b[i] pad;
output = output b[i] "\n";
}
return output;
}
|
'*' pad data
oooo***
ooooooo
*******
ooooo**
もしパディング文字が空白固定でよければ
,,.&LF,;._2 data
oooo
ooooooo
ooooo
1 2 3 4 5 6 7 8 | pad=:4 :',,.&LF a{.!.x~&>>./#&>a=.<;._2 y'
data=:0 :0
oooo
oooooooo
ooooo
)
|
なでしこで書いてみました。
1 2 3 4 5 6 7 8 9 10 11 | 「○○○○{\n}○○○○○○○{\n}{\n}○○○○○{\n}」を「☆」でテキスト行正規化して表示
●テキスト行正規化(対象文字列を,パディング文字で)
変数1とは整数。変数2とは文字列
対象文字列を反復。もし(対象の文字数)>変数1ならば、変数1に対象の文字数を代入。
最大文字数に変数1を代入
対象文字列を反復
変数2=変数2&対象
もし(最大文字数-(対象の文字数))>0ならば、(最大文字数-(対象の文字数))回、変数2=変数2&パディング文字
変数2=変数2&改行
それに変数2を代入
|
Haskellのデータ再帰が大活躍ですね…
Arrow.loopを使ったCircular Programmingでやってみました。皆さんのO(n)の解とそれ以外は一緒だと思います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | module Main where
import Control.Arrow
_normLine :: Char -> Int -> (String, Int) -> (String, Int)
_normLine _ i ([], _) = ([], i)
_normLine c i (ch:chs, m) = (repl' cch (ch : str'), max i m')
where
(str', m') = _normLine c i' (chs, m)
(cch, i') = case (ch) of
'\n' -> (max 0 $ m - i, 0)
otherwise -> (0, i + 1)
repl' 0 s = s
repl' j s = c : repl' (j - 1) s
main = putStr $ (loop $ _normLine '*' 0) "oooo\nooooooo\n\nooooo\n"
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | #module
#define ctype MAX(%1,%2) ((%1)*((%1) > (%2)) | (%2)*((%1) <= (%2)))
#define ctype numrg(%1,%2=0,%3=MAX_INT) (((%2) <= (%1)) && ((%1) <= (%3)))
#define ctype IsSJIS1st(%1) (numrg(%1, 0x81, 0x9F) || numrg(%1, 0xE0, 0xFC))
#define ctype IsSJIS2nd(%1) (numrg(%1, 0x40, 0x7E) || numrg(%1, 0x80, 0xFC))
#deffunc MakeTheSameLineLength var code, \
; local buf, local stmp, local index, local maximize, local len, local c
// 最長の行を探す
dim index, 2
repeat
c = peek(code, index(0))
// 終了か
if ( c == 0 ) {
// 最長の座を争う ( index(1) がこの行のバイト数になっている )
maximum = MAX(maximum, index(1))
break
}
if ( IsSJIS1st(c) ) {
index(0) += 2
index(1) += 2
continue
}
// 改行か
if ( c == 0x0D || c == 0x0A ) {
// 最長の座を争う ( index(1) がこの行のバイト数になっている )
maximum = MAX(maximum, index(1))
if ( c == 0x0D && peek(code, index(0) + 1) == 0x0A ) {
index(0) ++ // CR + LF
}
index(1) = 0
}
index(0) ++
index(1) ++
loop
// 変数を確保
sdim stmp, maximum + 64
sdim buf , (maximum + 2) * 80 + 1
// 文字数をそろえる
dim index, 2
repeat
getstr stmp, code, index : index += strsize
len = strlen(stmp)
if ( strsize == 0 ) { break }
// 足りない分半角スペースで埋める
repeat maximum - len, len
poke stmp, cnt, ' '
loop
wpoke stmp, maximum, 0x0A0D
// buf に追加する
memcpy buf, stmp, maximum + 2, index(1)
index(1) += maximum + 2
loop
// code に移し替える
sdim code, index(1) + 1
memcpy code, buf, index(1)
return maximum
#global
*main
sdim text
buf = "○○○○\n○○○○○○○\n\n○○○○○\n"
MakeTheSameLineLength buf
text = "最大文字列長は "+ stat +" でした。\n"
notesel buf
repeat notemax
noteget stmp, cnt
text += strf("%02d行目:", cnt) +"{"+ stmp +"}\n"
loop
noteunsel
objmode 2
mesbox text, ginfo(12), ginfo(13)
stop
|
バッチで。 ファイルやコマンドの実行結果を解析する際、 for文が空行を暗黙 的に読み飛ばしてしまうので、その点を findstrコマンドで補って います。 なお、パディング用の文字に半角空白を指定することはできません。 これはechoコマンドの仕様に依存するためです。 あと、バッチではマルチバイト文字をバイト単位で扱うことができ ないため、シングルバイトとマルチバイトが混在するファイルには 対応できていません。 Windows XPで動作を確認しました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | @echo off
setlocal enabledelayedexpansion
set c=%~2
set f=%~1
set i=0
set l=0
set m=0
if not exist "%f%" (
echo usage: %~n0 FILE CHARACTER >&2
exit /b 1
)
call :length "%c%" l
if %l% neq 1 (
echo usage: %~n0 FILE CHARACTER >&2
exit /b 1
)
for /f "tokens=1,2 delims=:" %%i in ('findstr /n /v /r "^$" %f%') do (
set s[%%i]=%%j
call :length "%%j" l[%%i]
if !l[%%i]! gtr !m! set m=!l[%%i]!
set /a i+=1
)
:: 空行
for /f "delims=:" %%i in ('findstr /n /r "^$" %f%') do (
set s[%%i]=
set l[%%i]=0
set /a i+=1
)
for /l %%i in (1,1,%i%) do (
set /a n=m-!l[%%i]!
for /l %%j in (1,1,!n!) do set s[%%i]=!s[%%i]!%c%
echo !s[%%i]!
)
endlocal & exit /b 0
goto :EOF
:length
setlocal enabledelayedexpansion
set i=0
set t=%~1
if not "%t%" == "" (
:_
set t=!t:~1!
set /a i+=1
if not "!t!" == "" goto _
)
endlocal & set %~2=%i%
goto :EOF
|
scalaがまだの様なので。
1 2 3 4 5 6 7 8 9 | object NormalizeText {
def normalize(s:String,p:String):String = {
val t:List[String] = s.lines.toList
val m:Int = t.map(l => l.size).sort((a,b) => a < b).last
t.map(l => l + p * (m - l.size)).mkString("\n") + "\n"
}
def main(args:Array[String]):Unit =
print(normalize("○○○○\n○○○○○○○\n\n○○○○○\n","△"))
}
|
Haskellの投稿を参考に1トラバースで書いてみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | object NormalizeText {
def normalize(s:String, p:Char):String = {
def _normalize(s:List[Char], l:Int, m:Int):Pair[List[Char],Int] = s match {
case List() => (List(), m)
case '\n'::r => {
val (t, x) = _normalize(r, 0, l.max(m))
(Stream.const(p).take(x - l).toList:::('\n'::t), x)
}
case c::r => {
val (t, x) = _normalize(r, l + 1, m)
(c::t, x)
}
}
_normalize(s.toList, 0, 0)._1.mkString("")
}
def readLines:List[String] = readLine match {
case null => List("")
case l => l::readLines
}
def main(args:Array[String]):Unit = print(normalize(readLines.mkString("\n"), '△'))
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 | def padding(inputStr, paddingChar = " "):
maxNum = max(len(line) for line in inputStr.split("\n"))
return "\n".join(line.ljust(maxNum, paddingChar)
for line in inputStr.split("\n"))
if __name__ == '__main__':
s = '''12345
123
123456789
12345
12'''
print padding(s, "*")
|
言語はViViScript 素直に改行で分割し、最大行長をもとめ、パディング文字を付加しただけ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | $ar="oooo\nooooooo\n\nooooo\n".split('\n'); // 改行で分割
$maxlen = 0;
for($i = 0; $i < $ar.length; ++$i) {
if( $ar[$i].length > $maxlen )
$maxlen = $ar[$i].length; // 最大行長を求める
}
$out = "";
for($i = 0; $i < $ar.length; ++$i) {
$out += $ar[$i];
if( $ar[$i].length < $maxlen ) // パディング文字を付加
$out += "*".repeat($maxlen - $ar[$i].length);
$out += "\n";
}
cout << $out;
|
1 2 3 4 5 6 7 8 9 10 11 | #! /usr/bin/ruby
class String
def normalize chr
array = split("\n")
length = array.map{|l| l.length}.max
array.map{|l| l.ljust length, chr}.join("\n")+("\n")
end
end
p "a\nbb\nccc\ndddd\n".normalize('-')
|
1 2 3 4 5 6 7 8 9 10 | #light
let pad ch (text : string) =
let lines = text.Split([|'\n'|])
let maxLength = lines |> Array.map String.length |> Array.max
let paddedLines = lines.[0..(lines.Length - 2)]
|> Array.map (fun x -> x.PadRight(maxLength, ch))
String.concat "\n" paddedLines ^ "\n"
"○○○○\n○○○○○○○\n\n○○○○○\n" |> pad '☆' |> printfn "%s"
|






nobsun
#8204()
Rating2/4=0.50
[ reply ]