[topic] 末尾の空白文字を取り除く
Posted feedbacks - Nested
Flatten HiddenPythonは文字列にrstrip(右側の空白文字を取り除く)というメソッドがある。 非破壊的。
取り除かれる空白文字はstring.whitespaceで定義されていて、 通常は'\t\n\x0b\x0c\r '。 ASCIIコードで表現すれば[9, 10, 11, 12, 13, 32]。 取り除く文字の種類を変える場合は引数に与える。
1 2 3 | line.rstrip()
line.rstrip('\r\n') # remove \r and \n
|
Rubyは文字列がchomp,chomp!という2つのメソッドを持っている。chompは非破壊的でchomp!は破壊的。返る値は改行の取り除かれた文字列。 取り除かれる文字はASCIIコードで表現すれば[10, 13]のみ。
1 2 | line.chomp
line.chomp!
|
取り除かれる文字はASCIIコードで表現すると[0, 9, 10, 11, 13, 32]。
1 | rtrim(line)
|
JavaScriptには専用のメソッドはなさそうだったので正規表現で置換。取り除かれる文字はASCIIコードで表現すると[9, 10, 11, 12, 13, 32]。
1 | line.replace(/\s+$/, "")
|
取り除かれる文字は isSeparator という問い合わせに対して true を返してくる文字。具体的には ASCII コードで表現すると #(32 13 9 10 12) 。
1 | line withoutTrailingBlanks
|
Javaでは両側の空白文字を削除する String#trim() しかないので、正規表現で実装。 取り除かれる文字は『空白文字: [ tnx0Bfr]』になります。
1 | line.replaceAll("\\s+$", "");
|
『空白文字: [ tnx0Bfr]』の中のバックスラッシュが消えてしまったので、ASCIIコード表記に修正 ASCIIコードの10進表記で 『32, 9, 10, 11, 12, 13』 になります。
Perlがなかったので投稿。 Rubyと同じで 末尾の改行文字を取り除く関数があります。取り除かれる文字は [10, 13] のみ。
1 | chomp($line);
|
Cがなかったので。 正規表現でやると、こんな感じでしょうか。。。
1 2 3 4 5 6 7 8 9 10 11 12 13 | #include <string.h>
#include <regex.h>
void rstrip( char* dest, const char* src ) {
regex_t preg;
regmatch_t pmatch;
regcomp(&preg, "\\ +$", REG_EXTENDED|REG_NEWLINE);
regexec(&preg, src, 1, &pmatch, 0);
regfree(&preg);
strncpy( dest, src, pmatch.rm_so );
}
|
マイナス評価を付けても反応が無かったので、ちゃんと指摘します。
次のような場合|123 |と出力されず、|123|と出力されます。文字列が空白だけの時は...((((;゜Д゜)))
char s[] = "123 ";
rtrim(s + 5);
printf("|%s|\n", s);
僕の勘違いでなければ問題の書き方にも問題があるように思いますね。 トピック全体にもいえることですけど お題を簡素に書きすぎてやるべきことがわかりにくい気がします。
その他の投稿でもchop系とtrim系が混ざっているので 判断つきにくいのではないでしょうか。
このお題は、末尾の空白文字を1文字削除する出よいのですか? それとも連続する空白をすべて削除する?
chop系とtrim系が混ざってる問題は確かにあるんですが、#4192はそれ以前の問題であることをyoheiさんは言っているのでは。(文字列の開始点をチェックしてないので渡されたデータより前にあるメモリにアクセスし、最悪破壊する可能性がある)。
boost を使えば、boost::algorithm に trim が用意されています。trim_right_copy が非破壊で、trim_right が破壊的です。削除される文字は isspace が true になるものと同じです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #include <iostream>
#include "boost/algorithm/string/trim.hpp"
using namespace std;
using namespace boost::algorithm;
int main ( int argc, char *argv[] ){
string str1( argv[1] );
string str2 = trim_right_copy( str1 );
cout << str1 << "#" << endl;
cout << str2 << "#" << endl;
trim_right( str1 );
cout << str1 << "#" << endl;
}
|
Common Lisp には string-right-trim という関数があります。非破壊的です。 取り除く文字は第一引数に文字列、リスト、ベクタのいずれかで指定します。
1 | (string-right-trim '(#\Space #\Tab #\Newline) line)
|
一番わかりやすいのは, ・文字列を反転 ・前から文字を辿って空白である間それを落す ・もう一度反転する これが,trim0 なのですが,この場合与えられた文字列を2回たどることになります. 与えられた文字列をどるのを1回にするには foldr を使って畳み込むという手があります. これが trim1です. Haskellですから当然文字列を破壊することはできません. 空白文字を判定するのは Data.Char モジュールの の isSpace です isSpace で空白と判定される文字コードはghciのプロンプトで以下のようにすると列挙できます。 % ghci -v0 Prelude> :m + Data.Char Text.Printf Prelude Text.Printf Data.Char> mapM_ (printf "0x%02x\n") $ map ord $ filter isSpace [minBound .. maxBound] 0x09 0x0a 0x0b 0x0c 0x0d 0x20 0xa0 0x1680 0x180e 0x2000 0x2001 0x2002 0x2003 0x2004 0x2005 0x2006 0x2007 0x2008 0x2009 0x200a 0x200b 0x202f 0x205f 0x3000
1 2 3 4 5 | import Data.Char
trim0 = reverse . dropWhile isSpace . reverse
trim1 = foldr (\ c cs -> if null cs && isSpace c then "" else c:cs) ""
|
毎回 null cs をチェックするのが癪なのでチェックしないバージョン。 末尾が空白でなければコピーでなく与えられた文字列そのものを返します。 実質は状態遷移してるだけです。 読みづらい。もしかしたら実行効率も落ちてるかも。 でもそんなの関………
1 2 3 4 5 6 7 8 9 10 | import Char (isSpace)
data T a = T [a] (a -> [a] -> T a)
trim2 xs = str where
T str _ = foldr (\c (T cs f) -> f c cs) (T xs lastCheck) xs
lastCheck c cs = if (isSpace c) then (T [] trailCheck) else (T cs pass)
trailCheck c _ = if (isSpace c) then (T [] trailCheck) else (T [c] cons)
pass _ cs = T cs pass
cons c cs = T (c:cs) cons
|
効率なんか関係ねえといいながらもやはり気になって 100000バイトの文字列で 計測してみました。 全部空白文字のとき trim1 と trim2 はほぼ同時。(trim2 がちょい早く終わる) 末尾の空白が少なくなるにつれて trim2 は段々早く終わるようになるが trim1 は変わらず。 trim2 の方が効率よい、よしよしと喜んで、さて問題外の trim0 もついでに 計測してやるかとやってみたところ常に trim0 が時間もメモリも圧倒的に 効率よい・・・ orz reverse も dropWhile も組み込みだから(本当?)なんでしょうか。 凝ったことせずに組み込み関数組み合わせてシンプルなコード書きましょうという教訓でした。 ちなみに trim の結果文字列が完全に評価されたことを確認するのには f xs = foldl' (\b c -> b + ord c) 0 xs を使いました。
see: MSDN - String.TrimEnd メソッド (System)
1 2 3 4 5 6 7 8 9 10 | using System;
static class Program {
static void Main() {
string text = "abcdefg\n \n \n\t\t\t";
Console.WriteLine("[{0}]", text.TrimEnd(null));
// [abcdefg]
Console.WriteLine("[{0}]", text.TrimEnd('\n', '\r', ' ', ' ', '\t', 'g', 'f'));
// [abcde]
}
}
|
Schemeではsrfi-13のstring-trim-rightが使えます。 取り除く文字種はデフォルトではlexerによって空白と見なされる文字(Gauche 0.8.12ではspace, tab, linefeed, vertical tab, form feed, return. いずれUnicode全般の空白文字も含む予定)。 3番目の例のように、オプショナル引数で文字種の指定もできます。
1 2 3 4 5 6 7 8 | gosh> (use srfi-13)
#<undef>
gosh> (string-trim-right "abcd ")
"abcd"
gosh> (string-trim-right "abcd \n")
"abcd"
gosh> (string-trim-right "aBcDeFgh \n" #[a-z\s])
"aBcDeF"
|
見当たらなかったので自作しました。両方非破壊です。
1 2 3 4 5 6 7 8 9 10 11 12 | let rtrim ?(ch=" \t\n\011\012\013") str =
let rec loop i =
if i>=0 && String.contains ch str.[i]
then loop (i-1)
else String.sub str 0 (i+1)
in loop (String.length str - 1);;
(*正規表現版*)
#load "str.cma";;
let rtrim2 str =
let cond = Str.regexp "[ \009-\013]+$" in
Str.global_replace cond "" str;;
|
手頃なオペレータがなかったので自作。取り除く文字は5行目で定義しています。 返り値はもとの文字列の部分文字列(参照)です。 元の文字列実体が保持される、という意味では非破壊です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | %!PS
/RTrim { % (String ) RTrim (String)
dup length 1 sub
<<9 true 10 true 11 true 12 true 13 true 32 true>>
3 1 roll
-1 0 {
2 copy get
3 index exch known {
0 exch getinterval
} {pop exit} ifelse
} for
exch pop
} bind def
(|) print
(ABCD ) RTrim print
(|) =
|







にしお
#4175()
Rating0/0=0.00
与えられた文字列の末尾の空白文字を取り除く方法と、その操作が与えられた文字列を破壊するかどうか。取り除かれる空白文字の種類。
[ reply ]