Penggunaan fungsi PUNCT pada PHP

Penggunaan fungsi PUNCT pada PHP

Table of Contents

  • Opsi 1 - re.sub
  • Opsi 2 - str.replace
  • Opsi 3 - functools.reduce
  • Fungsi lambda satu-lapis dengan pemahaman daftar:
  • Fungsi (tradisional)
  • Fungsi Umum tanpa Lambda atau Daftar Pemahaman


Saya pikir apa yang ingin saya lakukan adalah tugas yang cukup umum tetapi saya tidak menemukan referensi di web. Saya memiliki teks dengan tanda baca, dan saya ingin daftar kata-kata.

"Hey, you - what are you doing here!?"

seharusnya

['hey', 'you', 'what', 'are', 'you', 'doing', 'here']

Tapi Python str.split()hanya bekerja dengan satu argumen, jadi saya memiliki semua kata dengan tanda baca setelah saya berpisah dengan spasi putih. Ada ide?



Jawaban:


Kasus di mana ekspresi reguler dibenarkan:

import re
DATA = "Hey, you - what are you doing here!?"
print re.findall(r"[\w']+", DATA)
# Prints ['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']






re.split ()

re.split (pola, string [, maxsplit = 0])

Pisahkan string dengan kemunculan pola. Jika menangkap tanda kurung digunakan dalam pola, maka teks semua grup dalam pola juga dikembalikan sebagai bagian dari daftar yang dihasilkan. Jika maxsplit bukan nol, paling banyak terjadi perpecahan maxsplit, dan sisa string dikembalikan sebagai elemen terakhir dari daftar. (Catatan Ketidakcocokan: dalam rilis Python 1.5 asli, maxsplit diabaikan. Ini telah diperbaiki pada rilis selanjutnya.)

>>> re.split('\W+', 'Words, words, words.')
['Words', 'words', 'words', '']
>>> re.split('(\W+)', 'Words, words, words.')
['Words', ', ', 'words', ', ', 'words', '.', '']
>>> re.split('\W+', 'Words, words, words.', 1)
['Words', 'words, words.']






Cara cepat lain untuk melakukan ini tanpa regexp adalah mengganti karakter terlebih dahulu, seperti di bawah ini:

>>> 'a;bcd,ef g'.replace(';',' ').replace(',',' ').split()
['a', 'bcd', 'ef', 'g']






Begitu banyak jawaban, namun saya tidak dapat menemukan solusi yang efisien apa yang diminta oleh judul pertanyaan (memisahkan pada beberapa pemisah yang mungkin — sebagai gantinya, banyak jawaban terpecah pada apa pun yang bukan kata, yang berbeda). Jadi di sini adalah jawaban untuk pertanyaan dalam judul, yang bergantung pada remodul standar dan efisien Python :

>>> import re  # Will be splitting on: ,  - ! ? :
>>> filter(None, re.split("[, \-!?:]+", "Hey, you - what are you doing here!?"))
['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']

dimana:

  • yang […]pertandingan salah satu pemisah yang terdaftar di dalam,
  • yang \-dalam ekspresi reguler di sini untuk mencegah interpretasi khusus -sebagai indikator berbagai karakter (seperti dalam A-Z),
  • yang +melompat satu atau lebih pembatas (itu bisa dihilangkan berkat filter(), tapi ini tidak perlu akan menghasilkan string kosong antara pemisah cocok), dan
  • filter(None, …) menghapus string kosong yang mungkin dibuat oleh pemisah leading dan trailing (karena string kosong memiliki nilai boolean palsu).

Ini re.split()tepatnya "terbagi dengan beberapa pemisah", seperti yang diminta dalam judul pertanyaan.

Solusi ini juga kebal terhadap masalah dengan karakter non-ASCII dalam kata-kata yang ditemukan dalam beberapa solusi lain (lihat komentar pertama untuk jawaban ghostdog74 ).

The reModul jauh lebih efisien (dalam kecepatan dan amputasi) daripada melakukan Python loop dan tes "dengan tangan"!







Cara lain, tanpa regex

import string
punc = string.punctuation
thestring = "Hey, you - what are you doing here!?"
s = list(thestring)
''.join([o for o in s if not o in punc]).split()






Pro-Tip: Gunakan string.translateuntuk operasi string tercepat yang dimiliki Python.

Beberapa bukti ...

Pertama, cara lambat (maaf pprzemek):

>>> import timeit
>>> S = 'Hey, you - what are you doing here!?'
>>> def my_split(s, seps):
...     res = [s]
...     for sep in seps:
...         s, res = res, []
...         for seq in s:
...             res += seq.split(sep)
...     return res
... 
>>> timeit.Timer('my_split(S, punctuation)', 'from __main__ import S,my_split; from string import punctuation').timeit()
54.65477919578552

Selanjutnya, kami menggunakan re.findall()(seperti yang diberikan oleh jawaban yang disarankan). Lebih cepat:

>>> timeit.Timer('findall(r"\w+", S)', 'from __main__ import S; from re import findall').timeit()
4.194725036621094

Akhirnya, kami menggunakan translate:

>>> from string import translate,maketrans,punctuation 
>>> T = maketrans(punctuation, ' '*len(punctuation))
>>> timeit.Timer('translate(S, T).split()', 'from __main__ import S,T,translate').timeit()
1.2835021018981934

Penjelasan:

string.translatediimplementasikan dalam C dan tidak seperti banyak fungsi manipulasi string di Python, string.translate tidak menghasilkan string baru. Jadi ini tentang secepat Anda bisa mendapatkan untuk penggantian string.

Ini agak canggung, karena membutuhkan tabel terjemahan untuk melakukan keajaiban ini. Anda dapat membuat tabel terjemahan dengan maketrans()fungsi kenyamanan. Tujuannya di sini adalah untuk menerjemahkan semua karakter yang tidak diinginkan ke spasi. Pengganti satu-satu. Sekali lagi, tidak ada data baru yang dihasilkan. Jadi ini cepat !

Selanjutnya, kami menggunakan tua yang baik split(). split()secara default akan beroperasi pada semua karakter spasi, mengelompokkannya untuk pemisahan. Hasilnya akan menjadi daftar kata yang Anda inginkan. Dan pendekatan ini hampir 4x lebih cepat dari re.findall()!






Saya memiliki dilema yang sama dan tidak ingin menggunakan modul 're'.

def my_split(s, seps):
    res = [s]
    for sep in seps:
        s, res = res, []
        for seq in s:
            res += seq.split(sep)
    return res

print my_split('1111  2222 3333;4444,5555;6666', [' ', ';', ','])
['1111', '', '2222', '3333', '4444', '5555', '6666']



Pertama, saya ingin setuju dengan orang lain bahwa regex atau str.translate(...)solusi berbasis paling performan. Untuk kasus penggunaan saya, kinerja fungsi ini tidak signifikan, jadi saya ingin menambahkan ide yang saya pertimbangkan dengan kriteria itu.

Tujuan utama saya adalah untuk menggeneralisasi ide dari beberapa jawaban lain menjadi satu solusi yang dapat bekerja untuk string yang berisi lebih dari sekedar kata regex (yaitu, daftar hitam subset eksplisit karakter tanda baca vs karakter kata daftar putih).

Perhatikan bahwa, dalam pendekatan apa pun, orang mungkin juga mempertimbangkan untuk menggunakan string.punctuationmenggantikan daftar yang ditentukan secara manual.

Opsi 1 - re.sub

Saya terkejut melihat tidak ada jawaban sejauh ini menggunakan re.sub (...) . Saya menemukan ini pendekatan yang sederhana dan alami untuk masalah ini.

import re

my_str = "Hey, you - what are you doing here!?"

words = re.split(r'\s+', re.sub(r'[,\-!?]', ' ', my_str).strip())

Dalam solusi ini, saya menyarangkan panggilan ke re.sub(...)dalam re.split(...)- tetapi jika kinerja sangat penting, mengkompilasi regex di luar bisa bermanfaat - untuk kasus penggunaan saya, perbedaannya tidak signifikan, jadi saya lebih suka kesederhanaan dan keterbacaan.

Opsi 2 - str.replace

Ini adalah beberapa baris lagi, tetapi memiliki keuntungan diperluas tanpa harus memeriksa apakah Anda perlu melarikan diri dari karakter tertentu di regex.

my_str = "Hey, you - what are you doing here!?"

replacements = (',', '-', '!', '?')
for r in replacements:
    my_str = my_str.replace(r, ' ')

words = my_str.split()

Akan lebih baik untuk dapat memetakan str.replace ke string sebagai gantinya, tapi saya tidak berpikir itu bisa dilakukan dengan string yang tidak dapat diubah, dan sementara memetakan terhadap daftar karakter akan bekerja, menjalankan setiap penggantian terhadap setiap karakter terdengar berlebihan. (Edit: Lihat opsi selanjutnya untuk contoh fungsional.)

(Dalam Python 2, reducetersedia di namespace global tanpa mengimpornya dari functools.)

import functools

my_str = "Hey, you - what are you doing here!?"

replacements = (',', '-', '!', '?')
my_str = functools.reduce(lambda s, sep: s.replace(sep, ' '), replacements, my_str)
words = my_str.split()



join = lambda x: sum(x,[])  # a.k.a. flatten1([[1],[2,3],[4]]) -> [1,2,3,4]
# ...alternatively...
join = lambda lists: [x for l in lists for x in l]

Maka ini menjadi tiga baris:

fragments = [text]
for token in tokens:
    fragments = join(f.split(token) for f in fragments)

Penjelasan

Inilah yang di Haskell dikenal sebagai List monad. Gagasan di balik monad adalah bahwa sekali "di monad" Anda "tetap di monad" sampai sesuatu membawa Anda keluar. Misalnya di Haskell, katakan Anda memetakan range(n) -> [1,2,...,n]fungsi python di atas Daftar. Jika hasilnya Daftar, itu akan ditambahkan ke Daftar di tempat, sehingga Anda akan mendapatkan sesuatu seperti map(range, [3,4,1]) -> [0,1,2,0,1,2,3,0]. Ini dikenal sebagai peta-append (atau mappend, atau mungkin sesuatu seperti itu). Idenya di sini adalah bahwa Anda memiliki operasi ini yang Anda terapkan (membagi pada token), dan setiap kali Anda melakukannya, Anda bergabung hasilnya ke dalam daftar.

Anda dapat mengabstraksi ini menjadi fungsi dan tokens=string.punctuationsecara default.

Keuntungan dari pendekatan ini:

  • Pendekatan ini (tidak seperti pendekatan berbasis regex naif) dapat bekerja dengan token sewenang-wenang (yang regex juga dapat lakukan dengan sintaksis yang lebih maju).
  • Anda tidak terbatas pada token belaka; Anda dapat memiliki logika arbitrer sebagai ganti masing-masing token, misalnya salah satu dari "token" bisa berupa fungsi yang terbagi sesuai dengan bagaimana tanda kurung bersarang.






coba ini:

import re

phrase = "Hey, you - what are you doing here!?"
matches = re.findall('\w+', phrase)
print matches

ini akan dicetak ['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']


Gunakan ganti dua kali:

a = '11223FROM33344INTO33222FROM3344'
a.replace('FROM', ',,,').replace('INTO', ',,,').split(',,,')

menghasilkan:

['11223', '33344', '33222', '3344']

Saya suka re , tapi ini solusi saya tanpanya:

from itertools import groupby
sep = ' ,-!?'
s = "Hey, you - what are you doing here!?"
print [''.join(g) for k, g in groupby(s, sep.__contains__) if not k]

September .__ berisi__ adalah metode yang digunakan oleh operator 'in'. Pada dasarnya sama dengan

lambda ch: ch in sep

tetapi lebih nyaman di sini.

groupby mendapatkan string dan fungsi kami. Ini membagi string dalam kelompok menggunakan fungsi itu: setiap kali nilai fungsi berubah - grup baru dihasilkan. Jadi, September .__ berisi__ persis apa yang kita butuhkan.

groupby mengembalikan urutan pasangan, di mana pasangan [0] adalah hasil dari fungsi kami dan pasangan [1] adalah grup. Menggunakan 'jika bukan k' kami memfilter grup dengan pemisah (karena hasil September .__ berisi__ adalah True on separator). Yah, itu saja - sekarang kita memiliki urutan grup di mana masing-masing adalah kata (grup sebenarnya adalah iterable jadi kami menggunakan gabungan untuk mengubahnya menjadi string).

Solusi ini cukup umum, karena menggunakan fungsi untuk memisahkan string (Anda dapat membaginya dengan kondisi apa pun yang Anda butuhkan). Juga, itu tidak membuat string / daftar menengah (Anda dapat menghapus bergabung dan ekspresi akan menjadi malas, karena setiap grup adalah iterator)


Alih-alih menggunakan fungsi modul re.split Anda dapat mencapai hasil yang sama dengan menggunakan metode panda series.str.split.

Pertama, buat seri dengan string di atas dan kemudian terapkan metode ke seri.

thestring = pd.Series("Hey, you - what are you doing here!?") thestring.str.split(pat = ',|-')

pat parameter mengambil pembatas dan mengembalikan string split sebagai array. Di sini dua pembatas dilewatkan menggunakan | (atau operator). Outputnya adalah sebagai berikut:

[Hey, you , what are you doing here!?]



Saya kembali berkenalan dengan Python dan membutuhkan hal yang sama. Solusi findall mungkin lebih baik, tetapi saya datang dengan ini:

tokens = [x.strip() for x in data.split(',')]


menggunakan maketrans dan terjemahkan Anda dapat melakukannya dengan mudah dan rapi

import string
specials = ',.!?:;"()<>[]#$=-/'
trans = string.maketrans(specials, ' '*len(specials))
body = body.translate(trans)
words = body.strip().split()


Di Python 3, Anda bisa menggunakan metode dari PY4E - Python untuk Semua Orang .

Kita dapat memecahkan kedua masalah ini dengan menggunakan metode tali lower, punctuationdan translate. Ini translateadalah metode yang paling halus. Ini dokumentasi untuk translate:

your_string.translate(your_string.maketrans(fromstr, tostr, deletestr))

Ganti karakter fromstrdengan karakter di posisi yang sama tostrdan hapus semua karakter yang ada di deletestr. The fromstrdan tostrdapat string kosong dan deletestrparameter dapat dihilangkan.

Anda dapat melihat "tanda baca":

In [10]: import string

In [11]: string.punctuation
Out[11]: '!"#$%&\'()*+,-./:;<=>[email protected][\\]^_`{|}~'  

Sebagai contoh Anda:

In [12]: your_str = "Hey, you - what are you doing here!?"

In [13]: line = your_str.translate(your_str.maketrans('', '', string.punctuation))

In [14]: line = line.lower()

In [15]: words = line.split()

In [16]: print(words)
['hey', 'you', 'what', 'are', 'you', 'doing', 'here']

Untuk informasi lebih lanjut, Anda dapat merujuk:

  • PY4E - Python untuk Semua Orang
  • str.translate
  • str.maketrans
  • Metode Python String maketrans ()



Cara lain untuk mencapai ini adalah dengan menggunakan Natural Language Tool Kit ( nltk ).

import nltk
data= "Hey, you - what are you doing here!?"
word_tokens = nltk.tokenize.regexp_tokenize(data, r'\w+')
print word_tokens

Ini mencetak: ['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']

Kelemahan terbesar dari metode ini adalah Anda harus menginstal paket nltk .

Keuntungannya adalah Anda dapat melakukan banyak hal menyenangkan dengan sisa paket nltk begitu Anda mendapatkan token Anda.


Pertama-tama, saya tidak berpikir bahwa maksud Anda adalah untuk benar-benar menggunakan tanda baca sebagai pembatas dalam fungsi split. Deskripsi Anda menunjukkan bahwa Anda hanya ingin menghilangkan tanda baca dari string yang dihasilkan.

Saya menemukan ini cukup sering, dan solusi yang biasa saya tidak perlu kembali.

Fungsi lambda satu-lapis dengan pemahaman daftar:

(membutuhkan import string):

split_without_punc = lambda text : [word.strip(string.punctuation) for word in 
    text.split() if word.strip(string.punctuation) != '']

# Call function
split_without_punc("Hey, you -- what are you doing?!")
# returns ['Hey', 'you', 'what', 'are', 'you', 'doing']

Fungsi (tradisional)

Sebagai fungsi tradisional, ini masih hanya dua baris dengan pemahaman daftar (selain import string):

def split_without_punctuation2(text):

    # Split by whitespace
    words = text.split()

    # Strip punctuation from each word
    return [word.strip(ignore) for word in words if word.strip(ignore) != '']

split_without_punctuation2("Hey, you -- what are you doing?!")
# returns ['Hey', 'you', 'what', 'are', 'you', 'doing']

Ini juga akan secara alami membuat kontraksi dan kata-kata yang ditulis dengan tanda penghubung utuh. Anda selalu dapat menggunakan text.replace("-", " ")untuk mengubah tanda hubung menjadi spasi sebelum pemisahan.

Fungsi Umum tanpa Lambda atau Daftar Pemahaman

Untuk solusi yang lebih umum (di mana Anda dapat menentukan karakter untuk dihilangkan), dan tanpa pemahaman daftar, Anda mendapatkan:

def split_without(text: str, ignore: str) -> list:

    # Split by whitespace
    split_string = text.split()

    # Strip any characters in the ignore string, and ignore empty strings
    words = []
    for word in split_string:
        word = word.strip(ignore)
        if word != '':
            words.append(word)

    return words

# Situation-specific call to general function
import string
final_text = split_without("Hey, you - what are you doing?!", string.punctuation)
# returns ['Hey', 'you', 'what', 'are', 'you', 'doing']

Tentu saja, Anda selalu dapat menggeneralisasi fungsi lambda ke string karakter tertentu juga.


Pertama-tama, selalu gunakan re.compile () sebelum melakukan operasi RegEx dalam satu lingkaran karena ia bekerja lebih cepat daripada operasi normal.

jadi untuk masalah Anda pertama-tama kompilasi pola dan kemudian lakukan tindakan padanya.

import re
DATA = "Hey, you - what are you doing here!?"
reg_tok = re.compile("[\w']+")
print reg_tok.findall(DATA)

Inilah jawabannya dengan beberapa penjelasan.

st = "Hey, you - what are you doing here!?"

# replace all the non alpha-numeric with space and then join.
new_string = ''.join([x.replace(x, ' ') if not x.isalnum() else x for x in st])
# output of new_string
'Hey  you  what are you doing here  '

# str.split() will remove all the empty string if separator is not provided
new_list = new_string.split()

# output of new_list
['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']

# we can join it to get a complete string without any non alpha-numeric character
' '.join(new_list)
# output
'Hey you what are you doing'

atau dalam satu baris, kita bisa melakukan ini:

(''.join([x.replace(x, ' ') if not x.isalnum() else x for x in st])).split()

# output
['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']

jawaban yang diperbarui


Buat fungsi yang mengambil sebagai input dua string (string sumber untuk dipisah dan string pembatas pemisah) dan mengeluarkan daftar kata-kata split:

def split_string(source, splitlist):
    output = []  # output list of cleaned words
    atsplit = True
    for char in source:
        if char in splitlist:
            atsplit = True
        else:
            if atsplit:
                output.append(char)  # append new word after split
                atsplit = False
            else: 
                output[-1] = output[-1] + char  # continue copying characters until next split
    return output

Saya suka solusi pprzemek karena tidak berasumsi bahwa pembatas adalah karakter tunggal dan tidak mencoba memanfaatkan regex (yang tidak akan bekerja dengan baik jika jumlah pemisah menjadi gila lama).

Berikut ini versi yang lebih mudah dibaca dari solusi di atas untuk kejelasan:

def split_string_on_multiple_separators(input_string, separators):
    buffer = [input_string]
    for sep in separators:
        strings = buffer
        buffer = []  # reset the buffer
        for s in strings:
            buffer = buffer + s.split(sep)

    return buffer

mendapat masalah yang sama dengan @ooboo dan menemukan topik ini @ ghostdog74 menginspirasi saya, mungkin seseorang menemukan solusi saya berguna

str1='adj:sg:nom:m1.m2.m3:pos'
splitat=':.'
''.join([ s if s not in splitat else ' ' for s in str1]).split()

masukan sesuatu di tempat spasi dan pisah menggunakan karakter yang sama jika Anda tidak ingin membagi di spasi.



Ini adalah perjalanan saya dengan beberapa pembatas:

def msplit( str, delims ):
  w = ''
  for z in str:
    if z not in delims:
        w += z
    else:
        if len(w) > 0 :
            yield w
        w = ''
  if len(w) > 0 :
    yield w

Saya pikir berikut ini adalah jawaban terbaik untuk menyesuaikan kebutuhan Anda:

\W+ mungkin cocok untuk kasus ini, tetapi mungkin tidak cocok untuk kasus lain.

filter(None, re.compile('[ |,|\-|!|?]').split( "Hey, you - what are you doing here!?")


Inilah saya mengambilnya ....

def split_string(source,splitlist):
    splits = frozenset(splitlist)
    l = []
    s1 = ""
    for c in source:
        if c in splits:
            if s1:
                l.append(s1)
                s1 = ""
        else:
            print s1
            s1 = s1 + c
    if s1:
        l.append(s1)
    return l

>>>out = split_string("First Name,Last Name,Street Address,City,State,Zip Code",",")
>>>print out
>>>['First Name', 'Last Name', 'Street Address', 'City', 'State', 'Zip Code']

Saya suka replace()cara yang terbaik. Prosedur berikut mengubah semua pemisah yang didefinisikan dalam string splitlistmenjadi pemisah pertama di splitlistkemudian memisahkan teks pada satu pemisah itu. Itu juga menjelaskan jika splitlistkebetulan adalah string kosong. Ini mengembalikan daftar kata-kata, tanpa string kosong di dalamnya.

def split_string(text, splitlist):
    for sep in splitlist:
        text = text.replace(sep, splitlist[0])
    return filter(None, text.split(splitlist[0])) if splitlist else [text]

def get_words(s):
    l = []
    w = ''
    for c in s.lower():
        if c in '-!?,. ':
            if w != '': 
                l.append(w)
            w = ''
        else:
            w = w + c
    if w != '': 
        l.append(w)
    return l

Ini adalah penggunaannya:

>>> s = "Hey, you - what are you doing here!?"
>>> print get_words(s)
['hey', 'you', 'what', 'are', 'you', 'doing', 'here']

Jika Anda ingin operasi yang dapat dibalik (mempertahankan pembatas), Anda dapat menggunakan fungsi ini:

def tokenizeSentence_Reversible(sentence):
    setOfDelimiters = ['.', ' ', ',', '*', ';', '!']
    listOfTokens = [sentence]

    for delimiter in setOfDelimiters:
        newListOfTokens = []
        for ind, token in enumerate(listOfTokens):
            ll = [([delimiter, w] if ind > 0 else [w]) for ind, w in enumerate(token.split(delimiter))]
            listOfTokens = [item for sublist in ll for item in sublist] # flattens.
            listOfTokens = filter(None, listOfTokens) # Removes empty tokens: ''
            newListOfTokens.extend(listOfTokens)

        listOfTokens = newListOfTokens

    return listOfTokens

Saya baru-baru ini perlu melakukan ini tetapi menginginkan fungsi yang agak cocok dengan str.splitfungsi perpustakaan standar , fungsi ini berperilaku sama dengan perpustakaan standar ketika dipanggil dengan argumen 0 atau 1.

def split_many(string, *separators):
    if len(separators) == 0:
        return string.split()
    if len(separators) > 1:
        table = {
            ord(separator): ord(separator[0])
            for separator in separators
        }
        string = string.translate(table)
    return string.split(separators[0])

CATATAN : Fungsi ini hanya berguna ketika pemisah Anda terdiri dari satu karakter (seperti yang digunakan pengguna saya).