Extrair string e número de uma string que está em formato múltiplo usando regex em python?
Estou tentando analisar uma string usando regex que está em um formato específico para obter detalhes dela. Posso ter minha string em dois formatos -
Primeiro formato
Uma maneira é ter um foldername-version.tgz. Aqui foldernamepode haver qualquer string em qualquer formato. Pode ter outro ou múltiplo -ou qualquer outra coisa.
Por exemplo:
- hello-1234.tgz: Isso deve me dar
FolderNamecomohelloeVersioncomo1234 - world-12345.tgz: Isso deve me dar
FolderNamecomoworldeVersioncomo12345 - hello-21234-12345.tgz: Isso deve me dar
FolderNamecomohello-21234eVersioncomo12345 - hello-21234-a-12345.tgz: Isso deve me dar
FolderNamecomohello-21234-aeVersioncomo12345
Segundo formato
Outra maneira é ter foldername-version-environment.tgz. Nesse caso, também foldernamepode ser qualquer string em qualquer formato. Também ambiente corda só pode ser dev, stage, prode nada mais, então eu preciso para adicionar controlo sobre isso também.
Por exemplo:
- hello-1234-dev.tgz: Isso deve me dar
FolderNamecomohelloeVersioncomo1234 - world-12345-stage.tgz: Isso deve me dar
FolderNamecomoworldeVersioncomo12345 - hello-21234-12345-prod.tgz: Isso deve me dar
FolderNamecomohello-21234eVersioncomo12345 - hello-21234-a-12345-prod.tgz: Isso deve me dar
FolderNamecomohello-21234-aeVersioncomo12345
Declaração do Problema
Assim, com os dois formatos acima - eu preciso extrair FolderNamee Versionda minha string. Tentei com o regex abaixo, mas não funciona nas minhas strings que estão no segundo formato, mas quero que meu código funcione em ambos os formatos.
#sample example string which can be in first or second format
exampleString = hello-21234-12345-prod.tgz
build_found = re.search(r'[\d.-]+.tgz', exampleString)
version = build_found.group().replace(".tgz", "")
folderName = exampleString.split(version)[0]
O que estou fazendo aqui de errado?
Respostas
Eu usaria:
inp = "some text hello-21234-a-12345.tgz some more text"
parts = re.findall(r'\b([^\s-]+(?:-[^-]+)*)-(\d+)(?:-[^-]+)*\.\w+\b', inp)
print("FolderName: " + parts[0][0])
print("Version: " + parts[0][1])
Isso imprime:
FolderName: hello-21234-a
Version: 12345
Use grupos para especificar as diferentes seções do padrão. Você também pode nomeá-los para uma extração mais fácil mais tarde:
pattern = re.compile(r"(?P<FolderName>.+)-(?P<Version>\d+)(?:-(?P<Env>dev|stage|prod))?\.tgz")
m = pattern.match(ex)
print(m.groups())
# ('hello-21234', '12345', 'prod')
print(m.group('FolderName'), m.group('Version'), m.group('Env'))
# ('hello-21234', '12345', 'prod')
ex2 = "hello-21234-1234.tgz" # No environment
m = pattern.match(ex)
print(m.groups())
# ('hello-21234', '12345', None)
print(m.group('FolderName'), m.group('Version'), m.group('Env'))
# ('hello-21234', '12345', None)
Veja se este padrão funciona
import re
exampleString = 'hello-21234-12345-prod.tgz'
build_found = re.search(r'([\w-]+)-(\d+)-(dev|stage|prod)?', exampleString)
folder_name = build_found[1]
version = build_found[2]
environment = build_found[3]
print(folder_name)
print(version)
print(environment)
Resultado
hello-21234
12345
prod
Certamente não é a melhor abordagem, mas aqui está uma ideia.
Comece determinando se você tem o primeiro ou o segundo caso.
-(dev|stage|prod)\.tgz$
Esta regex determinará se você tem ou não o caso 1 ou 2.
Se for o caso 1, você pode extrair o nome da pasta com:
.*-
E você pode extrair a versão com:
-\d+.tgz$
Se for o caso 2, você pode extrair o nome da pasta / número da versão combinados com:
.*-
A partir daí, você pode extrair o nome da pasta com (novamente):
.*-
E o número da versão com:
-\d+
Você precisa usar uma expressão regular que capture os componentes que você está procurando na string e, em seguida, use .groups()para extrair as capturas. Isso funcionou em meus testes:
re.search(r'^(.+)-(\d+)\D*$', exampleString)
exemplo em ipython:
In [1]: import re
In [2]: s1 = 'hello-21234-12345-prod.tgz'
In [3]: s2 = 'hello-1234.tgz'
In [4]: re.search(r'^(.+)-(\d+)\D*$', s1).groups()
Out[4]: ('hello-21234', '12345')
In [5]: re.search(r'^(.+)-(\d+)\D*$', s2).groups()
Out[5]: ('hello', '1234')
O truque são os grupos de captura ( (...)) dentro da expressão regular r'^(.+)-(\d+)\D*$'. Existem dois grupos - na verdade, é mais fácil decodificá-lo examinando primeiro o segundo grupo de captura, depois o primeiro.
A segunda parte da regex - r'(\d+)\D*$'corresponde à série final de \ddígitos. Você sabe que é a série final de dígitos, porque a \D*$parte vai combinar e engolir todos os caracteres não-dígitos até o final da string.
A primeira parte da regex - r'^(.+)-'corresponde a tudo antes da segunda parte. Ele captura tudo, exceto o "-"caractere e fornece o FolderName
Observe que você precisará de algo um pouco mais complexo se tiver qualquer dígito no seu environmentou na terminação do arquivo (como se estiver usando compactação bzip2)