2011-04-26 61 views
0

我做一個製表符分隔的文件中的一些轉變,其中一列包含heirarchical標識符是這樣的:awk的SED或正則表達式插入子和變化情況

VI.d5.5 
VII.b2.1 
VII.b2.2 
VII.b2.3 
VII.c1 

我需要改變它看起來像以下,將來自第二點組的第一和第二之間的上套管信:

VI.D.d5.5 
VII.B.b2.1 
VII.B.b2.2 
VII.B.b2.3 
VII.C.c1 

我知道在sed\U國旗,但我不知道如何來只有一次應用它。例如,下面的向上的情況下既插信和原小寫:(不需要)

echo 'VII.b1.1' | sed -e 's/\([a-h]\)/\U\1.\1/' 
VII.B.B1.1 

我歡迎任何殼(SED,AWK,Perl,等等)或者vim解決辦法,讓我在製表符分隔的文件中修改該列。

回答

1

使用\ü嘗試,而不是\ U將下一個字符變爲大寫。但是,如果你想使用帶有\ U那麼你必須停止與\ E或\ L中的大寫不喜歡

's/\([a-h]\)/\U\1\E.\1/'

+0

謝謝 - 我不知道'\ u'。 – 2011-04-26 17:04:52

+0

@Michael:'\ u'和'\ U'是[sed'的非標準擴展](http://pubs.opengroup.org/onlinepubs/000095399/utilities/sed.html#tag_04_126_13_02),雖然它們是標準的在Perl中。嘗試在下面給出的數據集上運行'sed'版本。它行不通。 – tchrist 2011-04-26 17:35:46

+0

@tchrist:我會同意這是一個非標準的擴展,但是OP說\ U是「正在工作」,但不是隻停留在一個字符上,這意味着他不知道\ E或\ u,但更多重要的是,暗示他確實已經爲它安裝了適當的擴展。因此,如果「便攜性」對他來說不是問題,那麼使用\ u或\ U就可以了。 – 2011-04-26 17:51:43

2

你試過\u而不是\U?按照SED信息頁面(info sed):

`\U' 
    Turn the replacement to uppercase until a `\L' or `\E' is found, 

`\u' 
    Turn the next character to uppercase, 
+0

中有沒有這樣的事情[標準的sed](http://pubs.opengroup.org/onlinepubs/000095399/utilities/sed.html#tag_04_126_13_02)。 – tchrist 2011-04-26 17:02:09

+0

我在「標準sed」中找不到'\ U',但是,如果問題的作者試圖使用它,我會假設他們正在使用GNU sed,確實如此有''u' – photoionized 2011-04-26 17:06:41

+0

@photoionized:我去了,發現一個安裝了'ɢɴᴜsed'的Linux系統,但卻無法讓它在我的數據集上正常工作。它在我顯示的第二個數據集中保留了我的一封信的形式圖。 – tchrist 2011-04-26 17:38:31

1
sed -e 's/\([^.]\+\)\.\(.\)/\1.\u\2\.\2/' 

這樣的:

$ sed -e 's/\([^.]\+\)\.\(.\)/\1.\u\2\.\2/' <<<'VI.d5.5' 
VI.D.d5.5 
+0

這是行不通的。 'echo'VI.d5.5'| sed -e's/\([^。] \ + \)\。\(。\)/ \ 1. \ u \ 2 \。\ 2 \'產生''VI.d5.5''。但是,'echo'VI.d5。5'| perl -pe's /([^.]+)\.(.)/$ 1. \ u $ 2 \。$ 2 /''產生''VI.D.d5.5''。 – tchrist 2011-04-26 17:01:03

+0

可能是不同的sed,我的例子是從shell複製的。 – 2011-04-26 18:46:32

2
sed -e 's/\.[a-z]/\U&\E&/' 

Perl的工作也很好:

perl -pe 's/\.[a-z]/uc($&) . $&/e' 
+0

你實際上並不需要在/ */*中使用/// e',因爲它首先從* vi *中借用了casemapping轉義。所以'perl -pe's/\。[a-z]/\ U $&\ E $&/''的工作原理也是一樣的。順便說一句,如果你要像這樣匹配真實的字母,比列舉集合[[az]]更好的方法是使用'\ pL'快捷方式(它是'\ p {Letter}'的別名' ),它匹配任何帶有* Letter *屬性的字符。還有像'\ p {Lower}','\ p {Cased}'這樣的奇特屬性。如果你真的想要它們,甚至可以使用'\ p {Changes_When_Uppercased}'。取決於你真的想說什麼。 – tchrist 2011-04-26 17:39:23

2

你不能這樣做,在standard sed(1),因爲作爲\u\U沒有這樣的事那裏。事實上,在我所有的系統中(但只有一個)它失敗了 - 也是一樣,唉!我在Mac筆記本電腦和Mac桌面上都試過sed版本,然後在我們的Solaris服務器和我們的OpenBSD服務器上嘗試了它。我也在單獨的AIX機器上嘗試過它,當然它在那裏不起作用。 :(

但是,你應該能夠做到這一點可移植性這種方式,那些我測試系統的工作原理:

% cat sample 
VI.d5.5                   
VII.b2.1 
VII.b2.2 
VII.b2.3 
VII.c1 

% perl -wpe 's/([^.]+)\.(.)/$1.\u$2.$2/' /tmp/sample 
VI.D.d5.5 
VII.B.b2.1 
VII.B.b2.2 
VII.B.b2.3 
VII.C.c1 

不僅是更便攜,這是一個容易得多,也

這應該適用於過去20年裏發佈的任何Perl版本,包括perl4。但是,如果你生活在最前沿,所以至少安裝5.10,那麼你可以這樣做:

% perl -M5.10.0 -wpe 's/[^.]+\.\K(?=(.))/\u$1./' /tmp/sample 
VI.D.d5.5 
VII.B.b2.1 
VII.B.b2.2 
VII.B.b2.3 
VII.C.c1 

‑M5.10.0只是爲了確保您確實擁有並加載了5.10功能集。

Unicode怎麼樣?

現在假設您的樣本數據,它有統一:

% cat /tmp/sample.utf8 
Ⅵ.ð5.5 
Ⅷ.ß2.3 
Ⅺ.ç1 

% uniquote /tmp/sample.utf8 
\N{U+2165}.\N{U+F0}5.5 
\N{U+2167}.\N{U+DF}2.3 
\N{U+216A}.\N{U+E7}1 

% uniquote -v /tmp/sample.utf8 
\N{ROMAN NUMERAL SIX}.\N{LATIN SMALL LETTER ETH}5.5 
\N{ROMAN NUMERAL EIGHT}.\N{LATIN SMALL LETTER SHARP S}2.3 
\N{ROMAN NUMERAL ELEVEN}.\N{LATIN SMALL LETTER C WITH CEDILLA}1 

我可以向你保證你不會找到一個版本的sed,做對這些數據正確的事情。它會搞砸。我去了我們犧牲的Linux系統,雖然他們使用的ɢɴᴜsed在你的示例數據上工作,但是它拒絕在我的着名的Unicode數據集中映射其中一個字符,即使我的語言環境都設置正確。但perl版本仍然是正確的。

但是對於perl,只需添加‑CSD命令行選項來告訴perl數據文件和std {in,out,err}全部使用UTF-8,然後運行相同的命令,您將看到真正的東西QᴜɪᴛᴇIɴᴛᴇʀᴇsᴛɪɴɢ

% perl -CSD -wpe 's/([^.]+)\.(.)/$1.\u$2.$2/' /tmp/sample.utf8 
Ⅵ.Ð.ð5.5 
Ⅷ.Ss.ß2.3 
Ⅺ.Ç.ç1 

% perl -CSD -wpe 's/[^.]+\.\K(?=(.))/\u$1./' /tmp/sample.utf8 
Ⅵ.Ð.ð5.5 
Ⅷ.Ss.ß2.3 
Ⅺ.Ç.ç1 

% perl -CSD -wpe 's/[^.]+\.\K(?=(.))/\U$1./' /tmp/sample.utf8 
Ⅵ.Ð.ð5.5 
Ⅷ.SS.ß2.3 
Ⅺ.Ç.ç1 

正如你看到的,有titlecasing\u確實和uppercasing\U確實之間的差異。這是因爲小寫字母「ß」在titlecase中是「Ss」,而大寫字母是「SS」。奇怪而真實!無可否認,這類事情發生在希臘字母上的情況比我們使用的拉丁字母更多,但您仍然希望做到這一點。

這裏說的是all uniquote d,所以你可以看到剛纔我們談論它的代碼點:

% perl -CSD -wpe 's/[^.]+\.\K(?=(.))/\u$1./' /tmp/sample.utf8 | uniquote 
\N{U+2165}.\N{U+D0}.\N{U+F0}5.5 
\N{U+2167}.Ss.\N{U+DF}2.3 
\N{U+216A}.\N{U+C7}.\N{U+E7}1 

% perl -CSD -wpe 's/[^.]+\.\K(?=(.))/\u$1./' /tmp/sample.utf8 | uniquote -v 
\N{ROMAN NUMERAL SIX}.\N{LATIN CAPITAL LETTER ETH}.\N{LATIN SMALL LETTER ETH}5.5 
\N{ROMAN NUMERAL EIGHT}.Ss.\N{LATIN SMALL LETTER SHARP S}2.3 
\N{ROMAN NUMERAL ELEVEN}.\N{LATIN CAPITAL LETTER C WITH CEDILLA}.\N{LATIN SMALL LETTER C WITH CEDILLA}1 

是不是非常酷?

+0

感謝您的詳細分析。對我來說,這是一次性的數據轉換,絕對不包含unicode,但這是有用的信息。 – 2011-04-26 17:44:02

+0

@邁克爾:當然。隨時隨地使用。我只是想給你一些更多的選擇,所以如果你需要更靈活的解決方案,你可以稍後回來看看。我在我的Mac筆記本電腦和臺式機上以及我們的服務器上都嘗試過'sed',但是沒有一個版本是花哨的,儘管它們都是標準的perl。我找到了一個Linux系統,但是發現Linux有一個相當天真的概念Unicode。供應商區域設置只是*非常狡猾*,你知道嗎?永遠不知道他們會對你做什麼。 – tchrist 2011-04-26 17:44:58

+0

+1爲額外信息消化,但有點OT到實際問題。 OT意味着它不遵循GIGO排除故障的規則。你會瘋狂地試圖考慮「如果」的情況超出了OP的職位;) – 2011-04-26 17:55:06

1

這是awk解決方案。沒有雜亂的正則表達式需要。基本思路:分割點,得到第二場的第一個字符。然後使用toupper()函數更改它的大小寫。最後,替換回第二場。

awk -F"." '{ 
    ch = toupper(substr($2,1,1)) 
    $2=ch"."$2 
}1' OFS="." file