Я искал способ подсчитать, сколько раз определенный символ появляется после друг друга в строке. Все найденные мной способы просто подсчитывают, сколько раз в строке появляется символ «А».

Example of string:
0xAAABBC0123456789AABBCCDD0123456789ABCDEF

Каждая строка имеет длину 43 символа и начинается с «0x». Каждая строка содержит только следующие символы в случайном порядке: 0-9 и A-F (всего 16 различных символов). Каждый символ может появляться подряд один за другим несколько раз, например: «AAA» или «111».

Меня интересует, сколько раз каждый из максимум 16 символов появляется после друг друга в одной строке, и проверяю это через все мои строки.

До сих пор я только придумал этот скрипт Powershell, который подсчитывает, сколько раз каждый символ появляется в строке:

Get-Content " C:\Temp\strings.txt" | ForEach-Object{
    New-Object PSObject -Property @{
        Strings = $_
        Row = $_.ReadCount
        9 = [regex]::matches($_,"9").count
        D = [regex]::matches($_,"D").count
        B = [regex]::matches($_,"B").count
        C = [regex]::matches($_,"C").count
        7 = [regex]::matches($_,"7").count
        3 = [regex]::matches($_,"3").count
        1 = [regex]::matches($_,"1").count
        8 = [regex]::matches($_,"8").count
        F = [regex]::matches($_,"F").count
        2 = [regex]::matches($_,"2").count
        4 = [regex]::matches($_,"4").count
        E = [regex]::matches($_,"E").count
        6 = [regex]::matches($_,"6").count
        5 = [regex]::matches($_,"5").count
        A = [regex]::matches($_,"A").count
        0 = [regex]::matches($_,"0").count
    }
} | Sort Count -Descending | Export-Csv -Path "C:\Temp\output.csv" –NoTypeInformation

Я бы предпочел сделать это в Powershell, но если есть другой способ сделать это проще, пожалуйста, дайте мне знать.

0
user3316995 3 Сен 2017 в 13:53

5 ответов

Лучший ответ

Вы можете использовать lookbehind и обратную ссылку, чтобы разбить строку на повторяющиеся группы:

$s = '0xAAABBC0123456789AABBCCDD0123456789ABCDEF'
$repeats = $s.Remove(0, 2) -split '(?<=(.))(?!\1|$)'

Теперь мы можем сгруппировать подстроку по первой букве каждого:

$groups = $repeats |Group-Object {$_[0]} -AsHashTable

И наконец захватите самую длинную последовательность каждого персонажа:

'0123456789ABCDEF'.ToCharArray() |%{
    [pscustomobject]@{
        Character = "$_"
        MaxLength = "$($groups[$_] |Sort Length -Descending |Select -First 1)".Length
    }
}

И у вас должен получиться список (для вашего примера), подобный этому:

Character MaxLength
--------- ---------
0                 1
1                 1
2                 1
3                 1
4                 1
5                 1
6                 1
7                 1
8                 1
9                 1
A                 3
B                 2
C                 2
D                 2
E                 1
F                 1
0
Mathias R. Jessen 3 Сен 2017 в 13:56

Создайте HexPair итерируя строковую позицию для позиции (опуская последнюю) и увеличивайте значение в хеш-таблице с HexPair в качестве ключа.

$String = '0xAAABBC0123456789AABBCCDD0123456789ABCDEF'
$Hash=@{}
for ($i=2;$i -le ($string.length-2);$i++){
    $Hash[$($String.Substring($i,2))]+=1
}
$Hash.GetEnumerator()|ForEach-Object{
   [PSCustomObject]@{HexPair = $_.Name
                     Count = $_.Value}
} |Sort Count -Descending

Образец вывода

HexPair Count
------- -----
BC          3
AB          3
AA          3
CD          2
BB          2
9A          2
89          2
78          2
67          2
56          2
45          2
34          2
23          2
12          2
01          2
EF          1
DE          1
DD          1
D0          1
CC          1
C0          1

Альтернативный выход:

$Hash.GetEnumerator()|ForEach-Object{
    [PSCustomObject]@{HexPair = $_.Name
                      Count = $_.Value}
 } |Sort HexPair|group Count |%{"Count {0} {1}" -f $_.Name,($_.Group.HexPair -Join(', '))}|Sort

Count 1 C0, CC, D0, DD, DE, EF
Count 2 01, 12, 23, 34, 45, 56, 67, 78, 89, 9A, BB, CD
Count 3 AA, AB, BC
0
3 Сен 2017 в 13:55

Один из подходов состоит в том, чтобы перебирать исходную строку символ за символом и отслеживать, сколько раз символ был просмотрен. Это легко сделать с помощью хеш-таблицы. Вот так,

# Hashtable initialization. Add keys for 0-9A-F:
# Each char has initial count 0
$ht = @{}
"ABCDEF0123456789".ToCharArray() | % {
    $ht.Add($($_.ToString()), 0)
}

# Test data, the 0x prefix will contain one extra zero
$s = "0xAAABBC0123456789AABBCCDD0123456789ABCDEF"    

# Convert data to char array for iteration
# Increment value in hashtable by using the char as key
$s.ToCharArray() | % { $ht[$_.ToString()]+=1 }

# Check results
PS C:\> $ht

Name                           Value
----                           -----
B                              5
3                              2
5                              2
x                              1
9                              2
2                              2
8                              2
0                              3
1                              2
E                              1
7                              2
F                              1
6                              2
4                              2
D                              3
A                              6
C                              4
0
vonPryz 3 Сен 2017 в 13:43

Результат получился таким образом, несмотря на то, что он дает мне 15 дополнительных строк на строку, я легко могу отфильтровать нежелательный материал в Microsoft Excel.

#Removed all "0x" in textfile before running this script
$strings = Get-Content " C:\Temp\strings_without_0x.txt"
foreach($s in $strings) {
$repeats = $s.Remove(0, 2) -split '(?<=(.))(?!\1|$)'

$groups = $repeats |Group-Object {$_[0]} -AsHashTable

'0123456789ABCDEF'.ToCharArray() |%{
    [pscustomobject]@{
        String = "$s"
        Character = "$_"
        MaxLength = "$($groups[$_] |Sort Length -Descending |Select -First 1)".Length
    }

} | Sort Count -Descending | Export-Csv -Path "C:\Temp\output.csv" -NoTypeInformation -Append}

Спасибо за все отличные ответы!

0
user3316995 3 Сен 2017 в 19:05

Попробуй это.

$out=@()
$string="0xAAABBC0123456789AABBCCDD0123456789ABCDEF"
$out+="Character,Count"
$out+='0123456789ABCDEF'.ToCharArray()|%{"$_," + ($string.split("$_")|Where-object{$_ -eq ""}).count}
ConvertFrom-Csv $out |sort count -Descending 

Это дает следующее:

 Character Count
 --------- -----
 A         3    
 B         2    
 0         1    
 C         1    
 D         1    
 F         1    
 1         0    
 2         0    
 3         0    
 4         0    
 5         0    
 6         0    
 7         0    
 8         0    
 9         0    
 E         0    

Вы можете поместить это в функцию, подобную этой:

function count_dups ($string){
   $out=@() # null array
   $out+="Character,Count" # header
   $out+='0123456789ABCDEF'.ToCharArray()|%{"$_," + ($string.split("$_")|Where-object{$_ -eq ""}).count}
   return ConvertFrom-Csv $out | sort count -Descending
} 

Самая большая часть того, что я делаю здесь, это эта строка.

'0123456789ABCDEF'.ToCharArray()|%{"$_," + (string.split("$_")|Where-object{$_ -eq ""}).count}

Я разделяю строку на массив символов, поступающих из массива символов '0123456789ABCDEF'. Затем я считаю пустые элементы в массиве.

Я только создаю массив $ out, чтобы выходные данные могли быть отформатированы, как в вашем примере.

0
Tim 9 Авг 2018 в 19:50