RegEx เพื่อแยกการแข่งขันทั้งหมดจากสตริงโดยใช้ RegExp.exec


175

ฉันพยายามแยกสตริงประเภทต่อไปนี้:

[key:"val" key2:"val2"]

โดยที่มีคีย์ที่กำหนดเอง: "val" จับคู่อยู่ภายใน ฉันต้องการคว้าชื่อคีย์และค่า สำหรับผู้ที่อยากรู้อยากเห็นฉันพยายามที่จะแยกรูปแบบฐานข้อมูลของนักรบงาน

นี่คือสตริงทดสอบของฉัน:

[description:"aoeu" uuid:"123sth"]

ซึ่งมีวัตถุประสงค์เพื่อเน้นว่าสิ่งใดก็ตามที่สามารถอยู่ในคีย์หรือค่านอกเหนือจากช่องว่างไม่มีช่องว่างรอบโคลอนและค่าอยู่ในเครื่องหมายคำพูดคู่เสมอ

ในโหนดนี่คือผลลัพธ์ของฉัน:

[deuteronomy][gatlin][~]$ node
> var re = /^\[(?:(.+?):"(.+?)"\s*)+\]$/g
> re.exec('[description:"aoeu" uuid:"123sth"]');
[ '[description:"aoeu" uuid:"123sth"]',
  'uuid',
  '123sth',
  index: 0,
  input: '[description:"aoeu" uuid:"123sth"]' ]

แต่description:"aoeu"ยังตรงกับรูปแบบนี้ ฉันจะกลับมาแข่งขันทั้งหมดได้อย่างไร?


อาจเป็นเพราะ regex ของฉันผิดและ / หรือว่าฉันเพียงแค่ใช้สิ่งอำนวยความสะดวก regex ใน JavaScript ไม่ถูกต้อง ดูเหมือนว่าจะใช้ได้:> var s = "สิบห้าคือ 15 และแปดคือ 8"; > var re = / \ d + / g; > var m = s.match (อีกครั้ง); m = ['15', '8']
gatlin

6
Javascript ในขณะนี้มี. match () ฟังก์ชั่น: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… ใช้แบบนี้:"some string".match(/regex/g)
Stefnotch

คำตอบ:


237

โทรหาre.exec(s)วนซ้ำเพื่อรับการแข่งขันทั้งหมด:

var re = /\s*([^[:]+):\"([^"]+)"/g;
var s = '[description:"aoeu" uuid:"123sth"]';
var m;

do {
    m = re.exec(s);
    if (m) {
        console.log(m[1], m[2]);
    }
} while (m);

ลองใช้ด้วย JSFiddle นี้: https://jsfiddle.net/7yS2V/


8
ทำไมไม่ลองwhileแทนdo … whileล่ะ
Gumbo

15
การใช้ while loop ทำให้อึดอัดเล็กน้อยในการเริ่มต้น m คุณอาจจะต้องเขียนwhile(m = re.exec(s))ซึ่งเป็นรูปแบบการป้องกัน IMO m = re.exec(s); while (m) { ... m = re.exec(s); }หรือคุณต้องเขียน ฉันชอบdo ... if ... whileสำนวน แต่เทคนิคอื่น ๆ ก็ใช้ได้เหมือนกัน
lawnsea

14
การทำเช่นนี้ในโครเมียมทำให้แท็บของฉันหยุดทำงาน
EdgeCaseBerg

47
@EdgeCaseBerg คุณต้องมีการgตั้งค่าสถานะมิฉะนั้นตัวชี้ภายในจะไม่ถูกย้ายไปข้างหน้า เอกสาร
ทิม

12
อีกจุดคือถ้า regex สามารถจับคู่สตริงว่างได้มันจะวนซ้ำไม่สิ้นสุด
FabioCosta

139

str.match(pattern)หากpatternมีธงทั่วโลกgจะส่งคืนการแข่งขันทั้งหมดเป็นอาร์เรย์

ตัวอย่างเช่น:

const str = 'All of us except @Emran, @Raju and @Noman was there';
console.log(
  str.match(/@\w*/g)
);
// Will log ["@Emran", "@Raju", "@Noman"]


15
ระวัง: การจับคู่ไม่ตรงกับวัตถุ แต่เป็นสตริงที่ตรงกัน ตัวอย่างเช่นไม่มีการเข้าถึงกลุ่มใน"All of us except @Emran:emran26, @Raju:raju13 and @Noman:noman42".match(/@(\w+):(\w+)/g)(ซึ่งจะกลับมา["@Emran:emran26", "@Raju:raju13", "@Noman:noman42"])
madprog

4
@madprog ใช่มันเป็นวิธีที่ง่ายที่สุด แต่ไม่เหมาะเมื่อค่ากลุ่มมีความสำคัญ
Anis

1
มันไม่ทำงานสำหรับฉัน ฉันได้นัดแรกเท่านั้น
Anthony Roberts

7
@AnthonyRoberts คุณต้องเพิ่มธง "g" /@\w/gหรือnew RegExp("@\\w", "g")
Aruna Herath

88

ในการวนซ้ำการแข่งขันทั้งหมดคุณสามารถใช้replaceฟังก์ชัน:

var re = /\s*([^[:]+):\"([^"]+)"/g;
var s = '[description:"aoeu" uuid:"123sth"]';

s.replace(re, function(match, g1, g2) { console.log(g1, g2); });

ฉันคิดว่ามันซับซ้อนเกินไป อย่างไรก็ตามมันเป็นเรื่องดีที่ได้ทราบเกี่ยวกับวิธีการต่าง ๆ ในการทำสิ่งต่าง ๆ (ฉันโหวตคำตอบของคุณ)
Arashsoft

24
มันเป็นรหัสที่ใช้ง่าย คุณไม่ได้“ แทนที่” สิ่งใดในแง่ที่มีความหมาย มันใช้ประโยชน์จากฟังก์ชั่นบางอย่างเพื่อจุดประสงค์ที่แตกต่างกัน
Luke Maurer

6
@dewewad หากวิศวกรเพียงทำตามกฎโดยไม่ต้องคิดนอกกรอบเราจะไม่คิดถึงการไปเยี่ยมดาวเคราะห์ดวงอื่นในตอนนี้ ;-)
Christophe

1
@dudewad ขออภัยฉันไม่เห็นส่วนขี้เกียจที่นี่ หากวิธีการเดียวกันนี้ถูกเรียกว่า "กระบวนการ" แทน "แทนที่" คุณจะไม่เป็นไร ฉันเกรงว่าคุณจะติดอยู่กับคำศัพท์
Christophe

1
@ Christophe แน่นอนฉันไม่ได้ติดอยู่กับคำศัพท์ ฉันติดอยู่กับรหัสที่สะอาด การใช้สิ่งต่าง ๆ ที่มีจุดประสงค์เพื่อจุดประสงค์ที่แตกต่างกันนั้นเรียกว่า "แฮ็ค" ด้วยเหตุผล มันสร้างรหัสที่สับสนซึ่งยากต่อการเข้าใจและบ่อยกว่าไม่ได้รับผลกระทบจากประสิทธิภาพ ความจริงที่ว่าคุณตอบคำถามนี้โดยไม่ต้องมี regex ในตัวของมันเองและทำให้มันเป็นคำตอบที่ไม่ถูกต้องเนื่องจาก OP จะขอวิธีการทำกับ regex อย่างไรก็ตามฉันพบว่ามันสำคัญที่จะทำให้ชุมชนนี้มีมาตรฐานสูงซึ่งเป็นเหตุผลที่ฉันยืนหยัดในสิ่งที่ฉันพูดไว้ข้างต้น
dudewad

56

นี่คือทางออก

var s = '[description:"aoeu" uuid:"123sth"]';

var re = /\s*([^[:]+):\"([^"]+)"/g;
var m;
while (m = re.exec(s)) {
  console.log(m[1], m[2]);
}

นี่คือคำตอบของ lawnsea แต่สั้นกว่า

โปรดสังเกตว่าต้องตั้งค่าสถานะ `g 'เพื่อเลื่อนตัวชี้ภายในไปข้างหน้าระหว่างการเรียกใช้


17
str.match(/regex/g)

ส่งคืนการแข่งขันทั้งหมดเป็นอาร์เรย์

หากด้วยเหตุผลบางอย่างที่ลึกลับคุณต้องการข้อมูลเพิ่มเติมมาพร้อมกับexecทางเลือกอื่นสำหรับคำตอบก่อนหน้าคุณสามารถทำได้ด้วยฟังก์ชั่นวนซ้ำแทนที่จะวนซ้ำดังนี้ (ซึ่งก็ดูเย็นกว่า)

function findMatches(regex, str, matches = []) {
   const res = regex.exec(str)
   res && matches.push(res) && findMatches(regex, str, matches)
   return matches
}

// Usage
const matches = findMatches(/regex/g, str)

ดังที่ระบุไว้ในความคิดเห็นก่อนหน้านี้สิ่งสำคัญคือต้องมีgคำอธิบายท้าย regex เพื่อย้ายตัวชี้ไปข้างหน้าในการดำเนินการแต่ละครั้ง


1
ใช่. ดูซ้ำและสง่างาม วนซ้ำอยู่ตรงหน้าง่ายต่อการบำรุงรักษาและตรวจแก้จุดบกพร่อง
Andy N

11

เราเป็นที่สุดเริ่มเห็นในตัวmatchAllฟังก์ชั่นให้ดูที่นี่สำหรับรายละเอียดและความเข้ากันได้ของตาราง ดูเหมือนว่าตั้งแต่เดือนพฤษภาคม 2020, Chrome, Edge, Firefox และ Node.js (12+) รองรับ แต่ไม่ใช่ IE, Safari และ Opera ดูเหมือนว่าจะถูกร่างขึ้นในเดือนธันวาคม 2018ดังนั้นให้เวลาในการเข้าถึงเบราว์เซอร์ทั้งหมด แต่ฉันเชื่อว่ามันจะไปถึงที่นั่น

ในตัวmatchAllฟังก์ชั่นเป็นสิ่งที่ดีเพราะมันส่งกลับiterable มันยังส่งคืนกลุ่มการจับสำหรับทุกแมทช์! ดังนั้นคุณสามารถทำสิ่งต่าง ๆ เช่น

// get the letters before and after "o"
let matches = "stackoverflow".matchAll(/(\w)o(\w)/g);

for (match of matches) {
    console.log("letter before:" + match[1]);
    console.log("letter after:" + match[2]);
}

arrayOfAllMatches = [...matches]; // you can also turn the iterable into an array

match()นอกจากนี้ยังดูเหมือนวัตถุแข่งขันทุกใช้รูปแบบเดียวกับ ดังนั้นแต่ละวัตถุคืออาร์เรย์ของกลุ่มการจับคู่และการจับภาพพร้อมกับคุณสมบัติเพิ่มเติมสามรายการindex , และinput groupsดังนั้นดูเหมือนว่า:

[<match>, <group1>, <group2>, ..., index: <match offset>, input: <original string>, groups: <named capture groups>]

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการmatchAllนอกจากนี้ยังมีหน้านักพัฒนา Google นอกจากนี้ยังมีpolyfills / shimsให้บริการ


ฉันชอบสิ่งนี้จริง ๆ แต่มันยังไม่ได้ลงที่ Firefox 66.0.3 Caniuseยังไม่มีรายการสนับสนุนเกี่ยวกับเรื่องนี้ ฉันหวังว่าจะได้หนึ่งนี้ ฉันเห็นว่ามันทำงานได้ใน Chromium 74.0.3729.108
Lonnie Best

1
@LonnieBest ใช่คุณสามารถดูส่วนความเข้ากันได้ของหน้า MDNที่ฉันเชื่อมโยง ดูเหมือนว่า Firefox จะเริ่มให้การสนับสนุนในรุ่น 67 แต่ก็ยังไม่แนะนำให้ใช้หากคุณพยายามจัดส่งผลิตภัณฑ์ มี polyfills / shims ซึ่งฉันเพิ่มในคำตอบของฉันคือ
woojoo666

10

ขึ้นอยู่กับฟังก์ชั่นของ Agus แต่ฉันชอบที่จะคืนค่าตรงกับ:

var bob = "&gt; bob &lt;";
function matchAll(str, regex) {
    var res = [];
    var m;
    if (regex.global) {
        while (m = regex.exec(str)) {
            res.push(m[1]);
        }
    } else {
        if (m = regex.exec(str)) {
            res.push(m[1]);
        }
    }
    return res;
}
var Amatch = matchAll(bob, /(&.*?;)/g);
console.log(Amatch);  // yeilds: [&gt;, &lt;]

8

Iterables เป็น nicer:

const matches = (text, pattern) => ({
  [Symbol.iterator]: function * () {
    const clone = new RegExp(pattern.source, pattern.flags);
    let match = null;
    do {
      match = clone.exec(text);
      if (match) {
        yield match;
      }
    } while (match);
  }
});

การใช้งานในวง:

for (const match of matches('abcdefabcdef', /ab/g)) {
  console.log(match);
}

หรือถ้าคุณต้องการอาร์เรย์:

[ ...matches('abcdefabcdef', /ab/g) ]

1
Typo: if (m)ควรเป็นif (match)
Botje

อาร์เรย์มีการทำซ้ำแล้วดังนั้นทุกคนที่ส่งคืนอาเรย์ของการแข่งขันก็กลับมาทำซ้ำเหมือนกัน สิ่งที่ดีกว่าคือถ้าคุณคอนโซลบันทึกอาร์เรย์เบราว์เซอร์สามารถพิมพ์เนื้อหาได้ แต่การบันทึกคอนโซลแบบ iterable ทั่วไปเพียงทำให้คุณ [object Object] {... }
StJohn3D

อาร์เรย์ทั้งหมดสามารถทำซ้ำได้ แต่ไม่ใช่ทุกชนิดที่ทำซ้ำได้คืออาร์เรย์ การทำซ้ำได้ดีกว่าถ้าคุณไม่รู้ว่าผู้เรียกจะต้องทำอะไร ตัวอย่างเช่นหากคุณต้องการให้การจับคู่ครั้งแรกการทำซ้ำนั้นมีประสิทธิภาพมากขึ้น
sdgfsdh

ความฝันของคุณกำลังกลายเป็นจริงเบราว์เซอร์กำลังเปิดตัวการสนับสนุนสำหรับบิวท์อินmatchAllที่ส่งคืน iterable : D
woojoo666

1
ฉันเคยพบคำตอบนี้หลังการจับคู่การใช้งานทั้งหมด ฉันเขียนโค้ดบางส่วนสำหรับเบราว์เซอร์ JS ซึ่งรองรับ แต่โหนดจริงไม่ได้ สิ่งนี้ทำงานเหมือนกันทุกประการดังนั้นฉันจึงไม่ต้องเขียนข้อมูลใหม่ - ไชโย!
37309

8

หากคุณมี ES9

(หมายความว่าระบบของคุณ: Chrome, Node.js, Firefox และอื่น ๆ รองรับ Ecmascript 2019 หรือใหม่กว่า)

ใช้ใหม่yourString.matchAll( /your-regex/ )ใช้ใหม่

หากคุณไม่มี ES9

หากคุณมีระบบที่เก่ากว่านี่คือฟังก์ชันสำหรับการคัดลอกและวางที่ง่ายดาย

function findAll(regexPattern, sourceString) {
    let output = []
    let match
    // make sure the pattern has the global flag
    let regexPatternWithGlobal = RegExp(regexPattern,"g")
    while (match = regexPatternWithGlobal.exec(sourceString)) {
        // get rid of the string copy
        delete match.input
        // store the match data
        output.push(match)
    } 
    return output
}

ตัวอย่างการใช้งาน:

console.log(   findAll(/blah/g,'blah1 blah2')   ) 

เอาท์พุท:

[ [ 'blah', index: 0 ], [ 'blah', index: 6 ] ]

5

นี่คือฟังก์ชั่นของฉันเพื่อรับการแข่งขัน:

function getAllMatches(regex, text) {
    if (regex.constructor !== RegExp) {
        throw new Error('not RegExp');
    }

    var res = [];
    var match = null;

    if (regex.global) {
        while (match = regex.exec(text)) {
            res.push(match);
        }
    }
    else {
        if (match = regex.exec(text)) {
            res.push(match);
        }
    }

    return res;
}

// Example:

var regex = /abc|def|ghi/g;
var res = getAllMatches(regex, 'abcdefghi');

res.forEach(function (item) {
    console.log(item[0]);
});

โซลูชันนี้ป้องกันการวนซ้ำไม่สิ้นสุดเมื่อคุณลืมเพิ่มการตั้งค่าสถานะส่วนกลาง
user68311

2

ตั้งแต่ ES9 ตอนนี้มีวิธีที่ง่ายกว่าและดีกว่าสำหรับการแข่งขันทั้งหมดพร้อมกับข้อมูลเกี่ยวกับกลุ่มการจับกุมและดัชนี

const string = 'Mice like to dice rice';
const regex = /.ice/gu;
for(const match of string.matchAll(regex)) {
    console.log(match);
}

// ["หนู", ดัชนี: 0, อินพุต: "หนูชอบลูกเต๋าข้าว", กลุ่ม: ไม่ได้กำหนด]

// ["ลูกเต๋า", ดัชนี: 13, อินพุต: "หนูชอบลูกเต๋าข้าว", กลุ่ม: ไม่ได้กำหนด]

// ["ข้าว", ดัชนี: 18, อินพุต: "หนูชอบลูกเต๋าข้าว", กลุ่ม: ไม่ได้กำหนด]

ขณะนี้รองรับ Chrome, Firefox, Opera ตรวจสอบขึ้นอยู่กับเมื่อคุณอ่านลิงค์นี้เพื่อดูการสนับสนุนปัจจุบัน


สุดยอด! แต่ก็ยังคงมีความสำคัญที่จะเก็บไว้ในใจ regex ควรมีธงgและมันlastIndexควรจะตั้งค่าให้เป็น 0 matchAllก่อนที่จะอุทธรณ์ของ
N. Kudryavtsev

1

ใช้สิ่งนี้ ...

var all_matches = your_string.match(re);
console.log(all_matches)

มันจะคืนอาเรย์ของการแข่งขันทั้งหมด ... ที่จะใช้งานได้ดี .... แต่จำไว้ว่าจะไม่นำกลุ่มในบัญชี .. มันจะกลับมาแข่งขันเต็ม ...


0

ฉันขอแนะนำอย่างแน่นอนให้ใช้ฟังก์ชัน String.match () และสร้าง RegEx ที่เกี่ยวข้องให้กับมัน ตัวอย่างของฉันคือรายการของสตริงซึ่งบ่อยครั้งที่จำเป็นเมื่อสแกนอินพุตของผู้ใช้เพื่อหาคำและวลี

    // 1) Define keywords
    var keywords = ['apple', 'orange', 'banana'];

    // 2) Create regex, pass "i" for case-insensitive and "g" for global search
    regex = new RegExp("(" + keywords.join('|') + ")", "ig");
    => /(apple|orange|banana)/gi

    // 3) Match it against any string to get all matches 
    "Test string for ORANGE's or apples were mentioned".match(regex);
    => ["ORANGE", "apple"]

หวังว่านี่จะช่วยได้!


0

นี่จะไม่ช่วยปัญหาที่ซับซ้อนมากขึ้นของคุณ แต่ฉันโพสต์สิ่งนี้เพราะมันเป็นทางออกที่ง่ายสำหรับคนที่ไม่ได้ทำการค้นหาทั่วโลกเหมือนคุณ

ฉันทำให้ regex ง่ายขึ้นในคำตอบเพื่อให้ชัดเจนขึ้น (นี่ไม่ใช่วิธีแก้ปัญหาที่แน่นอนของคุณ)

var re = /^(.+?):"(.+)"$/
var regExResult = re.exec('description:"aoeu"');
var purifiedResult = purify_regex(regExResult);

// We only want the group matches in the array
function purify_regex(reResult){

  // Removes the Regex specific values and clones the array to prevent mutation
  let purifiedArray = [...reResult];

  // Removes the full match value at position 0
  purifiedArray.shift();

  // Returns a pure array without mutating the original regex result
  return purifiedArray;
}

// purifiedResult= ["description", "aoeu"]

ที่ดูเหมือน verbose มากกว่าเพราะความคิดเห็นนี่คือสิ่งที่ดูเหมือนว่าไม่มีความคิดเห็น

var re = /^(.+?):"(.+)"$/
var regExResult = re.exec('description:"aoeu"');
var purifiedResult = purify_regex(regExResult);

function purify_regex(reResult){
  let purifiedArray = [...reResult];
  purifiedArray.shift();
  return purifiedArray;
}

โปรดทราบว่ากลุ่มใด ๆ ที่ไม่ตรงกันจะถูกระบุไว้ในอาร์เรย์เป็น undefinedค่า

โซลูชันนี้ใช้ตัวดำเนินการกระจาย ES6 เพื่อชำระอาร์เรย์ของค่าเฉพาะของ regex คุณจะต้องเรียกใช้รหัสของคุณผ่านBabelหากคุณต้องการการสนับสนุน IE11


0

นี่คือโซลูชันหนึ่งบรรทัดที่ไม่มีลูปวิธีการแก้ปัญหาหนึ่งบรรทัดโดยไม่ต้องห่วงขณะ

คำสั่งซื้อจะถูกเก็บไว้ในรายการผลลัพธ์

ข้อเสียที่เป็นไปได้คือ

  1. มันโคลนนิ่ง regex สำหรับทุกนัด
  2. ผลลัพธ์อยู่ในรูปแบบที่แตกต่างจากโซลูชันที่คาดไว้ คุณจะต้องดำเนินการอีกครั้ง
let re = /\s*([^[:]+):\"([^"]+)"/g
let str = '[description:"aoeu" uuid:"123sth"]'

(str.match(re) || []).map(e => RegExp(re.source, re.flags).exec(e))

[ [ 'description:"aoeu"',
    'description',
    'aoeu',
    index: 0,
    input: 'description:"aoeu"',
    groups: undefined ],
  [ ' uuid:"123sth"',
    'uuid',
    '123sth',
    index: 0,
    input: ' uuid:"123sth"',
    groups: undefined ] ]

0

ฉันเดาว่าถ้าจะมีกรณีขอบเช่นช่องว่างพิเศษหรือหายไปการแสดงออกนี้มีขอบเขตน้อยอาจเป็นตัวเลือก:

^\s*\[\s*([^\s\r\n:]+)\s*:\s*"([^"]*)"\s*([^\s\r\n:]+)\s*:\s*"([^"]*)"\s*\]\s*$

หากคุณต้องการที่จะสำรวจ / ง่าย / แก้ไขการแสดงออกก็ถูกอธิบายในแผงด้านบนขวาของ regex101.com หากคุณต้องการคุณยังสามารถดูในลิงค์นี้ว่ามันจะตรงกับอินพุตตัวอย่างบางส่วนได้อย่างไร


ทดสอบ

const regex = /^\s*\[\s*([^\s\r\n:]+)\s*:\s*"([^"]*)"\s*([^\s\r\n:]+)\s*:\s*"([^"]*)"\s*\]\s*$/gm;
const str = `[description:"aoeu" uuid:"123sth"]
[description : "aoeu" uuid: "123sth"]
[ description : "aoeu" uuid: "123sth" ]
 [ description : "aoeu"   uuid : "123sth" ]
 [ description : "aoeu"uuid  : "123sth" ] `;
let m;

while ((m = regex.exec(str)) !== null) {
    // This is necessary to avoid infinite loops with zero-width matches
    if (m.index === regex.lastIndex) {
        regex.lastIndex++;
    }
    
    // The result can be accessed through the `m`-variable.
    m.forEach((match, groupIndex) => {
        console.log(`Found match, group ${groupIndex}: ${match}`);
    });
}

วงจร RegEx

jex.imเห็นภาพการแสดงออกปกติ:

ป้อนคำอธิบายรูปภาพที่นี่


-5

นี่คือคำตอบของฉัน:

var str = '[me nombre es] : My name is. [Yo puedo] is the right word'; 

var reg = /\[(.*?)\]/g;

var a = str.match(reg);

a = a.toString().replace(/[\[\]]/g, "").split(','));

3
สตริงป้อนข้อมูลของคุณ ( str) มีรูปแบบที่ไม่ถูกต้อง (วงเล็บใหญ่เกินไป) คุณจับคีย์เท่านั้นไม่ใช่ค่า รหัสของคุณมีข้อผิดพลาดทางไวยากรณ์และและไม่ดำเนินการ (วงเล็บสุดท้าย) หากคุณตอบคำถาม "เก่า" ด้วยคำตอบที่ยอมรับแล้วตรวจสอบให้แน่ใจว่าคุณเพิ่มความรู้และคำตอบที่ดีขึ้น ฉันไม่คิดว่าคำตอบของคุณจะเป็นเช่นนั้น
ล้าง
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.