Перехват совпадений с регулярным выражением в массиве с powershell - PullRequest
0 голосов
/ 02 июня 2018

У нас есть большой файл .vcf, который мы экспортировали с компьютера пользователя Mac.

В результате процесса экспорта контактов был получен один файл .vcf, который объединил все контакты в один файл.Я использовал notepad ++, чтобы заменить все экземпляры «BEGIN:» на «\ nBEGIN:», чтобы я мог спать сегодня вечером.

План состоит в том, чтобы поместить каждое совпадение с моим выражением reg в массив, а затем исключить каждое из них.строка во многих файлах .vcf с уникальным именем

(я планировал добавить строки «BEGIN: VCARD» и «END: VCARD» в начало и конец каждого файла позже.)

это отрывок данных, с которыми мы работаем:

BEGIN:VCARD
VERSION:3.0
PRODID:-//Apple Inc.//Mac OS X 10.13.4//EN
N:;;;;
TEL;type=CELL;type=VOICE;type=pref:+18005555555
UID:3fe8e0-421c-4c6a-bfa-38c75df8c07
X-ABUID:3FE8490-421C-4C6A-B2FA-38C15DF8C07:ABPerson
END:VCARD

BEGIN:VCARD
VERSION:3.0
PRODID:-//Apple Inc.//Mac OS X 10.13.4//EN
N:;<blah@company.org>;;;
FN:<blah@company.org>
item1.EMAIL;type=INTERNET;type=pref:blah@company.org
item1.X-ABLabel:_$!<Other>!$_
UID:5ad596-a879-4c98-9f56-2ef90efe32f
X-ABUD:DB5C20C-6DFC-450F-A752-D57964F6F3A:ABPerson
END:VCARD

...

Я подошел к приведенному ниже коду, но он возвращает только первое совпадение

$String = cat C:\temp\contacts.txt            
$Regex = [Regex]::new("(?<=BEGIN:VCARD)(.*?)(?=END:VCARD)")            
$Match = $Regex.Match($String)            
if($Match.Success)            
{            
    $Match.Value            
}

всегдаукажите свой источник

Мне нужно, чтобы он проанализировал всю строку и нашел все совпадения, как этот чувак:

$matches_found = @()
cat myfile.txt | %{
if ($_ -match '(?<=BEGIN:VCARD)(.*?)(?=END:VCARD)'){
    $matches_found += $matches[1]
    }
}

всегда цитируйте свой источник

но когда я вставляю свое регулярное выражение в этот код, он не находит совпадений

Ответы [ 3 ]

0 голосов
/ 03 июня 2018

Этот сценарий PowerShell

  • использует нерасходующий RegEx для разделения ввода на части, начиная с BEGIN:VCARD.
  • . Он проверяет, есть ли внутри UID, и присваивает имя выходному файлу.соответственно
  • все остальные именуются NoUID#0000.vcf с инкрементным счетчиком

EDIT упрощенный вариант с использованием только счетчика для имени выходного файла

## Q:\Test\2018\06\02\SO_50659915.ps1
$InFile = '.\sample.vcf'
$Delimiter = 'BEGIN:VCARD'
$Split     = "(?!^)(?=$Delimiter)"
(Get-Content $InFile -Raw) -split $Split | ForEach-Object {$I=0}{
    $I++
    $_ | Out-File -FilePath ("Whatever{0:0000}.vcf" -f $I) -Encoding UTF8
}

## Q:\Test\2018\06\02\SO_50659915.ps1
$InFile = '.\sample.vcf'

$Delimiter = 'BEGIN:VCARD'
# If the Delimiter contains chars that would be interpreted as special RE chars
# they need to be escaped, either manually or with the following command
# $Escaped   = [regex]::Escape($Delimiter)
$Split     = "(?!^)(?=$Delimiter)"

(Get-Content $InFile -Raw) -split $Split | ForEach-Object {$I=0}{
    if ($_ -match 'UID:(?<UID>[0-9a-f\-]{32})'){
        $_ | Out-File -FilePath ($Matches.UID+".vcf") -Encoding UTF8
    } else {
        $I++
        $_ | Out-File -FilePath ("NoUID#{0:0000}.vcf" -f $I) -Encoding UTF8
    }
}

Пример полученного результата:

> ls

    Directory: Q:\Test\2018\06\02

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       2018-06-03     20:05            236 3fe8e0-421c-4c6a-bfa-38c75df8c07.vcf
-a----       2018-06-03     20:05            311 5ad596-a879-4c98-9f56-2ef90efe32.vcf
-a----       2018-06-03     20:05            236 NoUID#0001.vcf
-a----       2018-06-03     20:05            311 NoUID#0002.vcf
-a----       2018-06-02     21:45            537 sample.vcf
-a----       2018-06-03     19:41            416 SO_50659915.ps1

> Get-Content .\3fe8e0-421c-4c6a-bfa-38c75df8c07.vcf
BEGIN:VCARD
VERSION:3.0
PRODID:-//Apple Inc.//Mac OS X 10.13.4//EN
N:;;;;
TEL;type=CELL;type=VOICE;type=pref:+18005555555
UID:3fe8e0-421c-4c6a-bfa-38c75df8c07
X-ABUID:3FE8490-421C-4C6A-B2FA-38C15DF8C07:ABPerson
END:VCARD


> Get-Content .\5ad596-a879-4c98-9f56-2ef90efe32.vcf
BEGIN:VCARD
VERSION:3.0
PRODID:-//Apple Inc.//Mac OS X 10.13.4//EN
N:;<blah@company.org>;;;
FN:<blah@company.org>
item1.EMAIL;type=INTERNET;type=pref:blah@company.org
item1.X-ABLabel:_$!<Other>!$_
UID:5ad596-a879-4c98-9f56-2ef90efe32f
X-ABUD:DB5C20C-6DFC-450F-A752-D57964F6F3A:ABPerson
END:VCARD

>
0 голосов
/ 05 июня 2018

LotPings победили меня в этом.Во всяком случае, вот мое решение

# Enter the full path and filename of your large combined vcf file here
$InputFile  = '<The full path and filename to your vcf file>'
# The path where yhou want the output vcf files. Below defaults to a folder 'VCards' within your Temp directory
$OutputPath = Join-Path $env:TEMP 'VCards'

# Read the input file in a single string
$VCardData  = Get-Content $InputFile -Raw

# Create the output folder if it does not already exist
if (!(Test-Path $OutputPath -PathType Container)) {
    New-Item -ItemType Directory -Force -Path $OutputPath | Out-Null
}

# Use RegEx match to search for strings across line breaks.
# This regex will keep the "BEGIN:VCARD" and "END:VCARD" for each array element intact 
$VcardRegex = '(?s)((?:BEGIN:VCARD).*?(?:END:VCARD))'
# This regex is for parsing out the UID value of the vcard if present
$UidRegex   = '\b(?:UID:)(?:urn:)?(?:uuid:)?([0-9a-f\-]*)\b'

# Select all matches
$VCardArray = [RegEx]::Matches($VCardData,$VcardRegex).Value

# Save results to $OutputPath as separate .vcf files
# using the UID value as filename. If no UID is found in the VCard element,
# a safety name is generated using a simple counter $i.

# Each file is encoded in UTF-8 encoding. If you use the Set-Content commandlet with option -Encoding UTF8
# it will create files prefixed with a byte order mark (BOM).
# Because it is usually advisable to create the file without the BOM, i use [System.IO.File]::WriteAllText
# using an encoding object

$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $false

$i = 0
$VCardArray | ForEach-Object {
    if ($_ -match $UidRegex) {
        $fileName = $matches[1] + '.vcf'
    } 
    else {
        $fileName = 'Vcard_{0:000}.vcf' -f $i++
    }
    $fileOut = Join-Path $OutputPath $fileName

    try {
    [System.IO.File]::WriteAllText($fileOut, $_, $Utf8NoBomEncoding)
    Write-Host "Saved file '$fileOut'"
    }
    catch {
    Write-Error "Could not write file '$fileOut':`r`n$($_.Exception.Message)"
    }
}
0 голосов
/ 02 июня 2018

Вы запрашиваете только одно совпадение в каждом из опубликованных вами кодовых блоков.Вместо этого вы захотите использовать совпадения RegEx.

Это должно дать вам то, что вы ищете:

$VCardData = @'
BEGIN:VCARD
VERSION:3.0
PRODID:-//Apple Inc.//Mac OS X 10.13.4//EN
N:;;;;
TEL;type=CELL;type=VOICE;type=pref:+18005555555
UID:3fe8e0-421c-4c6a-bfa-38c75df8c07
X-ABUID:3FE8490-421C-4C6A-B2FA-38C15DF8C07:ABPerson
END:VCARD

BEGIN:VCARD
VERSION:3.0
PRODID:-//Apple Inc.//Mac OS X 10.13.4//EN
N:;<blah@company.org>;;;
FN:<blah@company.org>
item1.EMAIL;type=INTERNET;type=pref:blah@company.org
item1.X-ABLabel:_$!<Other>!$_
UID:5ad596-a879-4c98-9f56-2ef90efe32f
X-ABUD:DB5C20C-6DFC-450F-A752-D57964F6F3A:ABPerson
END:VCARD
'@

# Use RegEx match to search for strings across line breaks. 
$VcardRegEx = '(?s)(?<=BEGIN:VCARD).*?(?=END:VCARD)'

# Select all matches
[RegEx]::Matches($VCardData,$VcardRegEx).Value

#results

VERSION:3.0
PRODID:-//Apple Inc.//Mac OS X 10.13.4//EN
N:;;;;
TEL;type=CELL;type=VOICE;type=pref:+18005555555
UID:3fe8e0-421c-4c6a-bfa-38c75df8c07
X-ABUID:3FE8490-421C-4C6A-B2FA-38C15DF8C07:ABPerson


VERSION:3.0
PRODID:-//Apple Inc.//Mac OS X 10.13.4//EN
N:;<blah@company.org>;;;
FN:<blah@company.org>
item1.EMAIL;type=INTERNET;type=pref:blah@company.org
item1.X-ABLabel:_$!<Other>!$_
UID:5ad596-a879-4c98-9f56-2ef90efe32f
X-ABUD:DB5C20C-6DFC-450F-A752-D57964F6F3A:ABPerson

Обновление в соответствии с последующим вопросом ОП

# How many records are in the set
([RegEx]::Matches($VCardData,$VcardRegEx).Value).Count

# Results 
2

# Output each record as a separate file

# Set the counter 
$VCardCounter = 0

# Loop through the dataset and output to a new file for each 

ForEach($Vcard in ([RegEx]::Matches($VCardData,$VcardRegEx).Value))
{
    $VCardFileName = 'VCard' + ++$VCardCounter + ".txt"
    New-Item -Path $pwd -ItemType File -Name $VCardFileName
    Add-Content -Value $Vcard -Path "$pwd\$VCardFileName"
}

Get-ChildItem -Path "$pwd\Vcard*"

# List the new files

    Directory: D:\Scripts


Mode                LastWriteTime         Length Name 
----                -------------         ------ ---- 
-a----        03-Jun-18     15:36            209 VCard1.txt 
-a----        03-Jun-18     15:36            286 VCard2.txt


# Review the contents of the new files
Get-Content (Get-ChildItem -Path "$pwd\Vcard*")

# Results

VERSION:3.0
PRODID:-//Apple Inc.//Mac OS X 10.13.4//EN
N:;;;;
TEL;type=CELL;type=VOICE;type=pref:+18005555555
UID:3fe8e0-421c-4c6a-bfa-38c75df8c07
X-ABUID:3FE8490-421C-4C6A-B2FA-38C15DF8C07:ABPerson


VERSION:3.0
PRODID:-//Apple Inc.//Mac OS X 10.13.4//EN
N:;<blah@company.org>;;;
FN:<blah@company.org>
item1.EMAIL;type=INTERNET;type=pref:blah@company.org
item1.X-ABLabel:_$!<Other>!$_
UID:5ad596-a879-4c98-9f56-2ef90efe32f
X-ABUD:DB5C20C-6DFC-450F-A752-D57964F6F3A:ABPerson
...