從字串中提取每第 n 個字符

從字串中提取每第 n 個字符

我正在嘗試找出解決方案問題。到目前為止,我解決這個問題的方法如下。

  • 將所有字元附加在一起以使其成為一個長字串。
  • 完成上述步驟後,刪除所有空格或製表符空格,這樣我們就只剩下一個大字串了。

我能夠使用以下命令建立上述步驟。

column -s '\t' inputfile | tr -d '[:space:]'

所以對於這樣的輸入文件,

1   0   0   0   0   0

0   1   1   1   0   0

應用上述命令後,我的值如下:

100000011100

現在,在這個大字串中,我嘗試應用以下方法。

提取每 6字元(如原始 OP 所需),並將其附加到數組元素直到字串末尾。

所以基本上,透過上述步驟,我嘗試將數組元素創建為,

101和第 7字元)、01(第 2第 8字元)、01(第 3第 9字元)、01(第 4第 10字元)、00(第 5第 11字元)、006和 12 個字元)第一個字元)。

所以我的問題是,如何提取每 n 個字符,以便將它們添加到數組中以進一步進行? (在本例中,n=6)。

答案1

兩條線

這是bash一個產生bash數組的純解:

s="100000011100"
array=($(
    for ((i=0; i<${#s}-6; i++))
    do
        echo "${s:$i:1}${s:$((i+6)):1}"
    done
    ))
echo "${array[@]}"

這會產生與問題中所示相同的輸出:

10 01 01 01 00 00

這裡的關鍵要素是 bash 的使用子字串擴充。 Bash 允許從變數中提取子字串,例如parametervia ${parameter:offset:length}。在我們的例子中,偏移量由循環變數決定i,長度始終為1

任意數量線路的通用解決方案

例如,假設我們的原始字串有 18 個字符,我們要提取 i 從 0 到 5 的第 i 個、第 i+6 個和第 i+12 個字符。

s="100000011100234567"
array=($(
    for ((i=0; i<6; i++))
    do
        new=${s:$i:1}
        for ((j=i+6; j<${#s}; j=j+6))
        do 
            new="$new${s:$j:1}"
        done
        echo "$new"
    done
    ))

echo "${array[@]}"

這會產生輸出:

102 013 014 015 006 007

相同的程式碼可以擴展到任意數量的 6 字元行。例如,如果s有三行(18 個字元):

s="100000011100234567abcdef"

然後,輸出變成:

102a 013b 014c 015d 006e 007f

答案2

使用perl

$ echo 100000011100 | perl -nle '
    for ($i = 0; $i < length()/2; $i++) {
        print substr($_,$i,1), substr($_,$i+6,1);
    }
'
10
01
01
01
00
00

它適用於兩條線。如果您想處理任意行,您應該直接處理行,而不是建立大字串。透過此輸入:

1   0   0   0   0   0                                                           
0   1   1   1   0   0                                                           
0   0   0   0   0   0

嘗試:

$ perl -anle '
    for ($i = 0; $i <= $#F; $i++) {
      push @{$h{$i}}, $F[$i];
    }
    END {
        print @{$h{$_}} for keys %h;
    }
' file
000
010
000
100
010
010

答案3

作為 shell 解決方案,getopts可能是最簡單的。問題在於getopts,它是 POSIX 指定的,可以完全按照您的要求進行操作 - 在 shell 循環中處理位元組流。我知道這聽起來很奇怪,因為如果你在我學到這一點之前像我一樣,你可能會想,好吧,天啊,我以為它應該處理命令列開關。這是事實,但第一件事也是。考慮:

-thisisonelongstringconsistingofseparatecommandlineswitches

是的,getopts必須處理這個問題。它必須在循環中逐個字符地拆分該字符,然後將 shell 變量$OPTARG或您通過名稱指定的另一個變量中的每個字符返回給您,這一切都取決於您調用它時獲得的具體程度。更重要的是,它必須傳回 shell 變數中的錯誤儲存其進度當它在 shell 變數中執行時,$OPTIND它可以從停止處繼續如果你能以某種方式解決它。它必須在不呼叫任何子 shell 的情況下完成整個工作。

假設我們有:

arg=$(seq -s '' 1000); set --
while getopts :0123456789 v -"${arg}"
do [ "$((i=$i+1<6?$i+1:0))" -gt 0 ] ||
set "$@" "$v"
done

嗯......我想知道它是否有效?

echo "$((${#arg}/6))" "$#"
482 482

那很好...

eval '
printf %.1s\\n "${arg#'"$(printf %0$((124*6-1))d | tr 0 \?)"'}" "${124}"'
4
4

因此,如您所見,該getopts命令為字串中的每六個位元組完全設定了數組。它不一定是這樣的數字 - 甚至也不一定是 shell 安全字元 - 而且您甚至不需要像我上面那樣指定目標字元01234565789。我已經在很多 shell 中反覆測試過,它們都可以正常工作。有一些怪癖 -bash如果第一個字符是空白字符,則會丟棄它 -dash接受:冒號作為指定參數,即使它是唯一 POSIX 明確禁止的。但這都不重要,因為即使它回傳錯誤,getopts仍然會存入當前 opt char 的值$OPTARG(由分配給您指定的 opt 變數的 ? 表示)否則明確取消設置,$OPTARG除非您聲明選項應該有參數。空白是件好事-它只會丟棄一個領導space,這非常好,因為在處理未知值時,您可以執行以下操作:

getopts : o -" $unknown_value"

....開始循環,而不會有任何第一個字元實際上位於您接受的 args 字串中的危險 - 這將導致立即getopts將整個內容$OPTARG插入為參數。

這是另一個例子:

OPTIND=1
while getopts : o -" $(dd if=/dev/urandom bs=16 count=1 2>/dev/null)"                         
do printf '\\%04o' "'$OPTARG"; done  

\0040\0150\0071\0365\0320\0070\0161\0064\0274\0115\0012\0215\0222\0271\0146\0057\0166

$OPTIND=1在第一行設定是因為我剛剛使用過getopts,並且在您重置它之前,它期望下一次調用從它停止的地方繼續 -"${arg2}"換句話說,它想要。但我不想付出,而且我現在正在做另一件事,所以我透過重置來讓它知道$OPTIND什麼時候可以開始。

在這個中我使用了zsh- 它不會對前導空格提出異議 - 因此第一個字元是八進制 40 - 空格字元。不過,我通常不會getopts以這種方式使用 - 我通常用它來避免對每個位元組執行 a 操作write(),並將其輸出(來自變數)分配給另一個 shell 變量,就像我上面所做的那樣set。然後,當我準備好時,我可以獲取整個字串,而當我這樣做時,通常會刪除第一個位元組。

答案4

sed我首先想到的是。

$ echo 1234567890abcdefghijklmnopqrstuvwxyz | sed 's/.\{5\}\(.\)/\1/g'
6bhntz

匹配 5 個字符,捕獲第 6 個字符,然後將其全部替換為捕獲的字符。

然而,如果字串的長度不是 6 的精確倍數,則會出現問題:

$ echo 1234567890abcdefghijklmnopqrstuvwxy | sed 's/.\{5\}\(.\)/\1/g' 
6bhntuvwxy

但我們可以透過sed稍微改變一下來解決這個問題:

$ echo 1234567890abcdefghijklmnopqrstuvwxy | sed 's/.\{1,5\}\(.\{0,1\}\)/\1/g'
6bhnt

由於正規表示式的貪婪本質,可變長度匹配將盡可能匹配,如果沒有剩餘內容可供捕獲,則不會捕獲,並且字元將被刪除。

相關內容