Tách một chuỗi theo khoảng trắng - bảo toàn các chuỗi con được trích dẫn - trong Python


269

Tôi có một chuỗi như thế này:

this is "a test"

Tôi đang cố gắng viết một cái gì đó bằng Python để phân tách nó theo không gian trong khi bỏ qua khoảng trắng trong dấu ngoặc kép. Kết quả tôi đang tìm kiếm là:

['this','is','a test']

Tái bút Tôi biết bạn sẽ hỏi "điều gì xảy ra nếu có dấu ngoặc kép trong dấu ngoặc kép, trong ứng dụng của tôi, điều đó sẽ không bao giờ xảy ra.


1
Cảm ơn đã hỏi câu hỏi này. Nó chính xác là những gì tôi cần để sửa chữa mô-đun xây dựng pypar.
Martlark

Câu trả lời:


392

Bạn muốn split, từ các shlexmô-đun tích hợp.

>>> import shlex
>>> shlex.split('this is "a test"')
['this', 'is', 'a test']

Điều này sẽ làm chính xác những gì bạn muốn.


13
Sử dụng "posix = false" để giữ nguyên trích dẫn. shlex.split('this is "a test"', posix=False)trở về['this', 'is', '"a test"']
Boon

@MatthewG. "Khắc phục" trong Python 2.7.3 có nghĩa là việc truyền một chuỗi unicode shlex.split()sẽ kích hoạt một UnicodeEncodeErrorngoại lệ.
Rockallite

57

Có một cái nhìn vào các shlexmô-đun, đặc biệt shlex.split.

>>> import shlex
>>> shlex.split('This is "a test"')
['This', 'is', 'a test']

40

Tôi thấy cách tiếp cận regex ở đây có vẻ phức tạp và / hoặc sai. Điều này làm tôi ngạc nhiên, bởi vì cú pháp regex có thể dễ dàng mô tả "khoảng trắng hoặc vật được bao quanh bởi dấu ngoặc kép" và hầu hết các công cụ regex (bao gồm cả Python) có thể phân tách trên regex. Vì vậy, nếu bạn sẽ sử dụng regexes, tại sao không nói chính xác ý bạn là gì?:

test = 'this is "a test"'  # or "this is 'a test'"
# pieces = [p for p in re.split("( |[\\\"'].*[\\\"'])", test) if p.strip()]
# From comments, use this:
pieces = [p for p in re.split("( |\\\".*?\\\"|'.*?')", test) if p.strip()]

Giải trình:

[\\\"'] = double-quote or single-quote
.* = anything
( |X) = space or X
.strip() = remove space and empty-string separators

shlex có thể cung cấp nhiều tính năng hơn, mặc dù.


1
Tôi đã suy nghĩ tương tự, nhưng sẽ đề nghị thay vào đó [t.strip ('"') cho t trong re.findall (r '[^ \ s"] + | "[^"] * "', 'đây là" một bài kiểm tra "')]
Darius Bacon

2
+1 Tôi đang sử dụng cái này vì nó nhanh hơn nhiều so với shlex.
hanleyp

3
Tại sao ba dấu gạch chéo ngược? Sẽ không có dấu gạch chéo đơn giản làm tương tự?
Doppelganger

1
Trên thực tế, một điều tôi không thích ở đây là mọi thứ trước / sau khi trích dẫn không được phân chia hợp lý. Nếu tôi có một chuỗi như thế này thì 'PARAM val1 = "Thing" val2 = "Thing2"'. Tôi hy vọng chuỗi sẽ chia thành ba phần, nhưng nó sẽ tách thành 5. Đã một thời gian kể từ khi tôi thực hiện regex, vì vậy tôi không cảm thấy muốn giải quyết nó bằng giải pháp của bạn ngay bây giờ.
leetNightshade

1
Bạn nên sử dụng chuỗi thô khi sử dụng biểu thức thông thường.
asmeker

29

Tùy thuộc vào trường hợp sử dụng của bạn, bạn cũng có thể muốn kiểm tra csvmô-đun:

import csv
lines = ['this is "a string"', 'and more "stuff"']
for row in csv.reader(lines, delimiter=" "):
    print(row)

Đầu ra:

['this', 'is', 'a string']
['and', 'more', 'stuff']

2
hữu ích, khi shlex tước một số ký tự cần thiết
scraplesh

1
CSV sử dụng hai dấu ngoặc kép liên tiếp (như cạnh nhau "") để thể hiện một trích dẫn kép ", do đó, sẽ biến hai trích dẫn kép thành một trích dẫn 'this is "a string""''this is "a string"""'cả hai sẽ ánh xạ tới['this', 'is', 'a string"']
Boris

15

Tôi sử dụng shlex.split để xử lý 70.000.000 dòng nhật ký mực, quá chậm. Thế là tôi chuyển sang re.

Vui lòng thử điều này, nếu bạn có vấn đề về hiệu suất với shlex.

import re

def line_split(line):
    return re.findall(r'[^"\s]\S*|".+?"', line)

8

Vì câu hỏi này được gắn thẻ regex, tôi quyết định thử một cách tiếp cận regex. Trước tiên tôi thay thế tất cả các khoảng trắng trong các phần trích dẫn bằng \ x00, sau đó phân tách bằng dấu cách, sau đó thay thế \ x00 trở lại khoảng trắng trong mỗi phần.

Cả hai phiên bản đều làm điều tương tự, nhưng bộ chia dễ đọc hơn một chút sau đó là bộ chia2.

import re

s = 'this is "a test" some text "another test"'

def splitter(s):
    def replacer(m):
        return m.group(0).replace(" ", "\x00")
    parts = re.sub('".+?"', replacer, s).split()
    parts = [p.replace("\x00", " ") for p in parts]
    return parts

def splitter2(s):
    return [p.replace("\x00", " ") for p in re.sub('".+?"', lambda m: m.group(0).replace(" ", "\x00"), s).split()]

print splitter2(s)

Bạn nên sử dụng re.Scanner thay thế. Nó đáng tin cậy hơn (trên thực tế tôi đã triển khai một shlex giống như sử dụng re.Scanner).
Devin Jeanpierre

+1 Hừm, đây là một ý tưởng khá thông minh, chia vấn đề thành nhiều bước để câu trả lời không quá phức tạp. Shlex đã không làm chính xác những gì tôi cần, ngay cả khi cố gắng điều chỉnh nó. Và các giải pháp regex pass đơn đã trở nên thực sự kỳ lạ và phức tạp.
leetNightshade

6

Có vẻ như vì lý do hiệu suất relà nhanh hơn. Đây là giải pháp của tôi bằng cách sử dụng một toán tử ít tham lam nhất để giữ các trích dẫn bên ngoài:

re.findall("(?:\".*?\"|\S)+", s)

Kết quả:

['this', 'is', '"a test"']

Nó để các cấu trúc giống aaa"bla blub"bbbnhau vì các mã thông báo này không bị ngăn cách bởi các khoảng trắng. Nếu chuỗi chứa các ký tự thoát, bạn có thể khớp như vậy:

>>> a = "She said \"He said, \\\"My name is Mark.\\\"\""
>>> a
'She said "He said, \\"My name is Mark.\\""'
>>> for i in re.findall("(?:\".*?[^\\\\]\"|\S)+", a): print(i)
...
She
said
"He said, \"My name is Mark.\""

Xin lưu ý rằng điều này cũng phù hợp với chuỗi trống ""bằng \Smột phần của mẫu.


1
Một ưu điểm quan trọng khác của giải pháp này là tính linh hoạt của nó đối với đặc tính phân định (ví dụ ,thông qua '(?:".*?"|[^,])+'). Điều tương tự cũng áp dụng cho (các) ký tự trích dẫn (kèm theo).
a_guest

4

Vấn đề chính với sự chấp nhận shlex cách tiếp cận là nó không bỏ qua các ký tự thoát bên ngoài các chuỗi con được trích dẫn và cho kết quả hơi bất ngờ trong một số trường hợp góc.

Tôi có trường hợp sử dụng sau đây, trong đó tôi cần một hàm phân tách để phân tách các chuỗi đầu vào sao cho các chuỗi con trích dẫn đơn hoặc trích dẫn kép được giữ nguyên, với khả năng thoát dấu ngoặc kép trong chuỗi con đó. Các trích dẫn trong một chuỗi không trích dẫn không nên được xử lý khác với bất kỳ ký tự nào khác. Một số trường hợp thử nghiệm ví dụ với đầu ra dự kiến:

chuỗi đầu vào | sản lượng dự kiến
===============================================
 'abc def' | ['abc', 'def']
 "abc \\ s def" | ['abc', '\ s', 'def']
 '"abc def" ghi' | ['abc def', 'ghi']
 "'abc def' ghi" | ['abc def', 'ghi']
 '"abc \\" def "ghi' | ['abc" def', 'ghi']
 "'abc \\' def 'ghi" | ["abc 'def",' ghi ']
 "'abc \\ s def' ghi" | ['abc \\ s def', 'ghi']
 '"abc \\ s def" ghi' | ['abc \\ s def', 'ghi']
 '"" kiểm tra' | ['', 'kiểm tra']
 "'' kiểm tra" | ['', 'kiểm tra']
 "abc'def" | ["abc'def"]
 "abc'def '" | ["abc'def '"]
 "abc'def 'ghi" | ["abc'def '",' ghi ']
 "abc'def'ghi" | ["abc'def'ghi"]
 'abc "def' | ['abc" def']
 'abc "def"' | ['abc "def"']
 'abc "def" ghi' | ['abc "def"', 'ghi']
 'abc "def" ghi' | ['abc "def" ghi']
 "r'AA 'r'. * _ xyz $ '" | ["r'AA '", "r'. * _ xyz $ '"]

Tôi đã kết thúc với hàm sau để phân tách một chuỗi sao cho kết quả đầu ra dự kiến ​​cho tất cả các chuỗi đầu vào:

import re

def quoted_split(s):
    def strip_quotes(s):
        if s and (s[0] == '"' or s[0] == "'") and s[0] == s[-1]:
            return s[1:-1]
        return s
    return [strip_quotes(p).replace('\\"', '"').replace("\\'", "'") \
            for p in re.findall(r'"(?:\\.|[^"])*"|\'(?:\\.|[^\'])*\'|[^\s]+', s)]

Ứng dụng thử nghiệm sau đây kiểm tra kết quả của các phương pháp khác ( shlexcsvhiện tại) và triển khai phân tách tùy chỉnh:

#!/bin/python2.7

import csv
import re
import shlex

from timeit import timeit

def test_case(fn, s, expected):
    try:
        if fn(s) == expected:
            print '[ OK ] %s -> %s' % (s, fn(s))
        else:
            print '[FAIL] %s -> %s' % (s, fn(s))
    except Exception as e:
        print '[FAIL] %s -> exception: %s' % (s, e)

def test_case_no_output(fn, s, expected):
    try:
        fn(s)
    except:
        pass

def test_split(fn, test_case_fn=test_case):
    test_case_fn(fn, 'abc def', ['abc', 'def'])
    test_case_fn(fn, "abc \\s def", ['abc', '\\s', 'def'])
    test_case_fn(fn, '"abc def" ghi', ['abc def', 'ghi'])
    test_case_fn(fn, "'abc def' ghi", ['abc def', 'ghi'])
    test_case_fn(fn, '"abc \\" def" ghi', ['abc " def', 'ghi'])
    test_case_fn(fn, "'abc \\' def' ghi", ["abc ' def", 'ghi'])
    test_case_fn(fn, "'abc \\s def' ghi", ['abc \\s def', 'ghi'])
    test_case_fn(fn, '"abc \\s def" ghi', ['abc \\s def', 'ghi'])
    test_case_fn(fn, '"" test', ['', 'test'])
    test_case_fn(fn, "'' test", ['', 'test'])
    test_case_fn(fn, "abc'def", ["abc'def"])
    test_case_fn(fn, "abc'def'", ["abc'def'"])
    test_case_fn(fn, "abc'def' ghi", ["abc'def'", 'ghi'])
    test_case_fn(fn, "abc'def'ghi", ["abc'def'ghi"])
    test_case_fn(fn, 'abc"def', ['abc"def'])
    test_case_fn(fn, 'abc"def"', ['abc"def"'])
    test_case_fn(fn, 'abc"def" ghi', ['abc"def"', 'ghi'])
    test_case_fn(fn, 'abc"def"ghi', ['abc"def"ghi'])
    test_case_fn(fn, "r'AA' r'.*_xyz$'", ["r'AA'", "r'.*_xyz$'"])

def csv_split(s):
    return list(csv.reader([s], delimiter=' '))[0]

def re_split(s):
    def strip_quotes(s):
        if s and (s[0] == '"' or s[0] == "'") and s[0] == s[-1]:
            return s[1:-1]
        return s
    return [strip_quotes(p).replace('\\"', '"').replace("\\'", "'") for p in re.findall(r'"(?:\\.|[^"])*"|\'(?:\\.|[^\'])*\'|[^\s]+', s)]

if __name__ == '__main__':
    print 'shlex\n'
    test_split(shlex.split)
    print

    print 'csv\n'
    test_split(csv_split)
    print

    print 're\n'
    test_split(re_split)
    print

    iterations = 100
    setup = 'from __main__ import test_split, test_case_no_output, csv_split, re_split\nimport shlex, re'
    def benchmark(method, code):
        print '%s: %.3fms per iteration' % (method, (1000 * timeit(code, setup=setup, number=iterations) / iterations))
    benchmark('shlex', 'test_split(shlex.split, test_case_no_output)')
    benchmark('csv', 'test_split(csv_split, test_case_no_output)')
    benchmark('re', 'test_split(re_split, test_case_no_output)')

Đầu ra:

shlex

[OK] abc def -> ['abc', 'def']
[FAIL] abc \ s def -> ['abc', 's', 'def']
[OK] "abc def" ghi -> ['abc def', 'ghi']
[OK] 'abc def' ghi -> ['abc def', 'ghi']
[OK] "abc \" def "ghi -> ['abc" def', 'ghi']
[FAIL] 'abc \' def 'ghi -> ngoại lệ: Không có trích dẫn kết thúc
[OK] 'abc \ s def' ghi -> ['abc \\ s def', 'ghi']
[OK] "abc \ s def" ghi -> ['abc \\ s def', 'ghi']
[OK] "" kiểm tra -> ['', 'kiểm tra']
[OK] '' kiểm tra -> ['', 'kiểm tra']
[FAIL] abc'def -> ngoại lệ: Không có trích dẫn kết thúc
[FAIL] abc'def '-> [' abcdef ']
[FAIL] abc'def 'ghi -> [' abcdef ',' ghi ']
[FAIL] abc'def'ghi -> ['abcdefghi']
[FAIL] abc "def -> ngoại lệ: Không có trích dẫn kết thúc
[FAIL] abc "def" -> ['abcdef']
[FAIL] abc "def" ghi -> ['abcdef', 'ghi']
[FAIL] abc "def" ghi -> ['abcdefghi']
[FAIL] r'AA 'r'. * _ Xyz $ '-> [' rAA ',' r. * _ Xyz $ ']

csv

[OK] abc def -> ['abc', 'def']
[OK] abc \ s def -> ['abc', '\ s', 'def']
[OK] "abc def" ghi -> ['abc def', 'ghi']
[FAIL] 'abc def' ghi -> ["'abc", "def'", 'ghi']
[FAIL] "abc \" def "ghi -> ['abc \\', 'def"', 'ghi']
[FAIL] 'abc \' def 'ghi -> ["' abc", "\\ '", "def'", 'ghi']
[FAIL] 'abc \ s def' ghi -> ["'abc",' \\ s ', "def'", 'ghi']
[OK] "abc \ s def" ghi -> ['abc \\ s def', 'ghi']
[OK] "" kiểm tra -> ['', 'kiểm tra']
[FAIL] '' kiểm tra -> ["''", 'kiểm tra']
[OK] abc'def -> ["abc'def"]
[OK] abc'def '-> ["abc'def'"]
[OK] abc'def 'ghi -> ["abc'def'", 'ghi']
[OK] abc'def'ghi -> ["abc'def'ghi"]
[OK] abc "def -> ['abc" def']
[OK] abc "def" -> ['abc "def"']
[OK] abc "def" ghi -> ['abc "def"', 'ghi']
[OK] abc "def" ghi -> ['abc "def" ghi']
[OK] r'AA 'r'. * _ Xyz $ '-> ["r'AA'", "r '. * _ Xyz $'"]

lại

[OK] abc def -> ['abc', 'def']
[OK] abc \ s def -> ['abc', '\ s', 'def']
[OK] "abc def" ghi -> ['abc def', 'ghi']
[OK] 'abc def' ghi -> ['abc def', 'ghi']
[OK] "abc \" def "ghi -> ['abc" def', 'ghi']
[OK] 'abc \' def 'ghi -> ["abc' def", 'ghi']
[OK] 'abc \ s def' ghi -> ['abc \\ s def', 'ghi']
[OK] "abc \ s def" ghi -> ['abc \\ s def', 'ghi']
[OK] "" kiểm tra -> ['', 'kiểm tra']
[OK] '' kiểm tra -> ['', 'kiểm tra']
[OK] abc'def -> ["abc'def"]
[OK] abc'def '-> ["abc'def'"]
[OK] abc'def 'ghi -> ["abc'def'", 'ghi']
[OK] abc'def'ghi -> ["abc'def'ghi"]
[OK] abc "def -> ['abc" def']
[OK] abc "def" -> ['abc "def"']
[OK] abc "def" ghi -> ['abc "def"', 'ghi']
[OK] abc "def" ghi -> ['abc "def" ghi']
[OK] r'AA 'r'. * _ Xyz $ '-> ["r'AA'", "r '. * _ Xyz $'"]

shlex: 0,281ms mỗi lần lặp
csv: 0,030ms mỗi lần lặp
lại: 0,049ms mỗi lần lặp

Vì vậy, hiệu suất tốt hơn nhiều shlexvà có thể được cải thiện hơn nữa bằng cách biên dịch biểu thức chính quy, trong trường hợp đó nó sẽ vượt trội hơn so với csvcách tiếp cận.


Không chắc chắn những gì bạn đang nói về: `` `>>> shlex.split ('đây là" một bài kiểm tra "') ['này', 'là', 'một bài kiểm tra'] >>> shlex.split (' đây là \ "a test \\" ') [' this ',' is ',' "a ',' test" '] >>> shlex.split (' đây là "a \\" test \\ " "') [' this ',' is ',' a" test "']` ``
morsik

@morsik, quan điểm của bạn là gì? Có lẽ trường hợp sử dụng của bạn không phù hợp với tôi? Khi bạn xem xét các trường hợp thử nghiệm, bạn sẽ thấy tất cả các trường hợp shlexkhông hoạt động như mong đợi đối với các trường hợp sử dụng của tôi.
Ton van den Heuvel

3

Để giữ nguyên dấu ngoặc kép, hãy sử dụng chức năng này:

def getArgs(s):
    args = []
    cur = ''
    inQuotes = 0
    for char in s.strip():
        if char == ' ' and not inQuotes:
            args.append(cur)
            cur = ''
        elif char == '"' and not inQuotes:
            inQuotes = 1
            cur += char
        elif char == '"' and inQuotes:
            inQuotes = 0
            cur += char
        else:
            cur += char
    args.append(cur)
    return args

Khi so sánh với chuỗi lớn hơn, chức năng của bạn rất chậm
Faran2007

3

Kiểm tra tốc độ của các câu trả lời khác nhau:

import re
import shlex
import csv

line = 'this is "a test"'

%timeit [p for p in re.split("( |\\\".*?\\\"|'.*?')", line) if p.strip()]
100000 loops, best of 3: 5.17 µs per loop

%timeit re.findall(r'[^"\s]\S*|".+?"', line)
100000 loops, best of 3: 2.88 µs per loop

%timeit list(csv.reader([line], delimiter=" "))
The slowest run took 9.62 times longer than the fastest. This could mean that an intermediate result is being cached.
100000 loops, best of 3: 2.4 µs per loop

%timeit shlex.split(line)
10000 loops, best of 3: 50.2 µs per loop

1

Hmm, dường như không thể tìm thấy nút "Trả lời" ... dù sao, câu trả lời này dựa trên cách tiếp cận của Kate, nhưng phân tách chính xác các chuỗi với các chuỗi chứa dấu ngoặc kép thoát và cũng loại bỏ dấu ngoặc kép bắt đầu và kết thúc của chuỗi con:

  [i.strip('"').strip("'") for i in re.split(r'(\s+|(?<!\\)".*?(?<!\\)"|(?<!\\)\'.*?(?<!\\)\')', string) if i.strip()]

Điều này hoạt động trên các chuỗi như 'This is " a \\\"test\\\"\\\'s substring"'(không may là đánh dấu điên rồ để ngăn Python xóa các lối thoát).

Nếu kết quả thoát trong chuỗi trong danh sách trả về không muốn, bạn có thể sử dụng phiên bản hàm bị thay đổi một chút này:

[i.strip('"').strip("'").decode('string_escape') for i in re.split(r'(\s+|(?<!\\)".*?(?<!\\)"|(?<!\\)\'.*?(?<!\\)\')', string) if i.strip()]

1

Để giải quyết các vấn đề về unicode trong một số phiên bản Python 2, tôi đề nghị:

from shlex import split as _split
split = lambda a: [b.decode('utf-8') for b in _split(a.encode('utf-8'))]

Đối với python 2.7.5, đây phải là: split = lambda a: [b.decode('utf-8') for b in _split(a)]nếu không bạn sẽ nhận được:UnicodeDecodeError: 'ascii' codec can't decode byte ... in position ...: ordinal not in range(128)
Peter Varo

1

Là một tùy chọn thử tssplit:

In [1]: from tssplit import tssplit
In [2]: tssplit('this is "a test"', quote='"', delimiter='')
Out[2]: ['this', 'is', 'a test']

0

Tôi đề nghị:

chuỗi kiểm tra:

s = 'abc "ad" \'fg\' "kk\'rdt\'" zzz"34"zzz "" \'\''

để chụp cũng "" và '':

import re
re.findall(r'"[^"]*"|\'[^\']*\'|[^"\'\s]+',s)

kết quả:

['abc', '"ad"', "'fg'", '"kk\'rdt\'"', 'zzz', '"34"', 'zzz', '""', "''"]

bỏ qua trống "" và '':

import re
re.findall(r'"[^"]+"|\'[^\']+\'|[^"\'\s]+',s)

kết quả:

['abc', '"ad"', "'fg'", '"kk\'rdt\'"', 'zzz', '"34"', 'zzz']

Có thể được viết là tốt re.findall("(?:\".*?\"|'.*?'|[^\s'\"]+)", s).
hochl

-3

Nếu bạn không quan tâm đến chuỗi phụ hơn là đơn giản

>>> 'a short sized string with spaces '.split()

Hiệu suất:

>>> s = " ('a short sized string with spaces '*100).split() "
>>> t = timeit.Timer(stmt=s)
>>> print "%.2f usec/pass" % (1000000 * t.timeit(number=100000)/100000)
171.39 usec/pass

Hoặc mô-đun chuỗi

>>> from string import split as stringsplit; 
>>> stringsplit('a short sized string with spaces '*100)

Hiệu suất: Mô-đun chuỗi dường như hoạt động tốt hơn các phương thức chuỗi

>>> s = "stringsplit('a short sized string with spaces '*100)"
>>> t = timeit.Timer(s, "from string import split as stringsplit")
>>> print "%.2f usec/pass" % (1000000 * t.timeit(number=100000)/100000)
154.88 usec/pass

Hoặc bạn có thể sử dụng công cụ RE

>>> from re import split as resplit
>>> regex = '\s+'
>>> medstring = 'a short sized string with spaces '*100
>>> resplit(regex, medstring)

Hiệu suất

>>> s = "resplit(regex, medstring)"
>>> t = timeit.Timer(s, "from re import split as resplit; regex='\s+'; medstring='a short sized string with spaces '*100")
>>> print "%.2f usec/pass" % (1000000 * t.timeit(number=100000)/100000)
540.21 usec/pass

Đối với các chuỗi rất dài, bạn không nên tải toàn bộ chuỗi vào bộ nhớ và thay vào đó, phân tách các dòng hoặc sử dụng một vòng lặp


11
Bạn dường như đã bỏ lỡ toàn bộ điểm của câu hỏi. Có những phần được trích dẫn trong chuỗi không cần phải phân chia.
rjmunro

-3

Thử cái này:

  def adamsplit(s):
    result = []
    inquotes = False
    for substring in s.split('"'):
      if not inquotes:
        result.extend(substring.split())
      else:
        result.append(substring)
      inquotes = not inquotes
    return result

Một số chuỗi thử nghiệm:

'This is "a test"' -> ['This', 'is', 'a test']
'"This is \'a test\'"' -> ["This is 'a test'"]

Vui lòng cung cấp repr của một chuỗi bạn nghĩ sẽ thất bại.
pjz

Suy nghĩ ? adamsplit("This is 'a test'")['This', 'is', "'a", "test'"]
Matthew Schinckel

OP chỉ nói "trong dấu ngoặc kép" và chỉ có một ví dụ với dấu ngoặc kép.
pjz
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.