Tek veya çift tırnak içine alınmadığında bir dizeyi boşluk kullanarak bölmek için normal ifade


114

Normal ifadeler konusunda yeniyim ve yardımınız için minnettarım. Tek veya çift tırnaklarla çevrili olmayan tüm boşlukları kullanarak örnek dizeyi bölecek bir ifade oluşturmaya çalışıyorum. Son denemem şuna benziyor: (?!")ve pek işe yaramıyor. Alıntıdan önceki boşlukta bölünüyor.

Örnek giriş:

This is a string that "will be" highlighted when your 'regular expression' matches something.

Istenilen çıktı:

This
is
a
string
that
will be
highlighted
when
your
regular expression
matches
something.

Bunu not edin "will be"ve 'regular expression'kelimeler arasındaki boşluğu koruyun.


Gerçekten "split" yöntemini mi kullanıyorsunuz yoksa Matcher'da "bul" yöntemiyle döngü yapmak yeterli olur mu?
erickson

9
"ve şimdi iki sorunu var"

Yanıtlar:


251

Diğerlerinin neden bu kadar karmaşık düzenli ifadeler veya bu kadar uzun kodlar önerdiğini anlamıyorum. Esasen, dizenizden iki tür şey almak istiyorsunuz: boşluk veya tırnak olmayan karakter dizileri ve aralarında tırnak işareti olmadan bir alıntıyla başlayan ve biten karakter dizileri, iki tür tırnak için. Bunları şu normal ifadeyle kolayca eşleştirebilirsiniz:

[^\s"']+|"([^"]*)"|'([^']*)'

Alıntıları listede istemediğiniz için yakalama gruplarını ekledim.

Bu Java kodu listeyi oluşturur, eğer eşleşmişse tırnakları hariç tutmak için yakalama grubunu ekler ve yakalama grubu eşleşmezse (alıntılanmamış bir kelime eşleşmişse) genel normal ifade eşleşmesini ekler.

List<String> matchList = new ArrayList<String>();
Pattern regex = Pattern.compile("[^\\s\"']+|\"([^\"]*)\"|'([^']*)'");
Matcher regexMatcher = regex.matcher(subjectString);
while (regexMatcher.find()) {
    if (regexMatcher.group(1) != null) {
        // Add double-quoted string without the quotes
        matchList.add(regexMatcher.group(1));
    } else if (regexMatcher.group(2) != null) {
        // Add single-quoted string without the quotes
        matchList.add(regexMatcher.group(2));
    } else {
        // Add unquoted word
        matchList.add(regexMatcher.group());
    }
} 

Döndürülen listede alıntıların olması sizin için sorun değilse, çok daha basit bir kod kullanabilirsiniz:

List<String> matchList = new ArrayList<String>();
Pattern regex = Pattern.compile("[^\\s\"']+|\"[^\"]*\"|'[^']*'");
Matcher regexMatcher = regex.matcher(subjectString);
while (regexMatcher.find()) {
    matchList.add(regexMatcher.group());
} 

1
Jan, cevabın için teşekkürler. BTW, EditPad'in büyük bir hayranıyım.
carlsz

Dizelerde kaçan alıntılara izin vermek istersem ne olur \"?
Monstieur

3
Bu cevabın problemi eşsiz alıntı: John's mothersonuçlar [John, s, mother]
bölünmüş

2
Sorun leonbloy hatlarını düzeltmek için, biraz işlenenlerini düzen yeniden ve boşluk-grubundan tırnak atlayabilirsiniz: "([^"]*)"|'([^']*)'|[^\s]+.
Ghostkeeper

1
Bu ve diğer cevaplar Bina üzerine aşağıdaki regex tırnak içine karakterleri kaçan sağlar: "([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'|[^\s]+. Stackoverflow.com/questions/5695240/… sayfasına
Limnic

15

StackOverflow'da, normal ifadeler kullanan çeşitli bağlamlarda bu aynı soruyu ele alan birkaç soru vardır. Örneğin:

GÜNCELLEME : Tek ve çift tırnaklı dizeleri işlemek için örnek normal ifade. Ref: Tırnak işaretleri dışında bir dizeyi nasıl bölebilirim?

m/('.*?'|".*?"|\S+)/g 

Bunu hızlı bir Perl parçacığı ile test ettik ve çıktı aşağıda yeniden üretildi. Ayrıca, tırnak işaretleri arasındaysa (istenip istenmediğinden emin değillerse) boş dizeler veya yalnızca boşluk içeren dizeler için de çalışır.

This
is
a
string
that
"will be"
highlighted
when
your
'regular expression'
matches
something.

Bunun, eşleşen değerlerde tırnak karakterlerini içerdiğini unutmayın, ancak bunu bir dize ile değiştirebilir veya normal ifadeyi bunları içermeyecek şekilde değiştirebilirsiniz. Bunu şimdilik okuyucu veya başka bir poster için bir alıştırma olarak bırakacağım, çünkü saat 02:00 artık normal ifadelerle uğraşmak için çok geç;)


Normal ifadenizin eşleşmeyen alıntılara izin verdiğini düşünüyorum, örneğin "olacak" ve "normal ifadeler".
Zach Scrivena

@Zach - haklısın, öyle ... her ihtimale karşı bunu düzeltmek için güncelledi
Jay


3

Jan Goyvaerts'ten gelen normal ifade şu ana kadar bulduğum en iyi çözüm, ancak aynı zamanda programında hariç tuttuğu boş (boş) eşleşmeler de yaratıyor. Bu boş eşleşmeler aynı zamanda normal ifade test edicilerinden de (ör. Rubular.com) görünür. Aramaları etrafına çevirirseniz (önce alıntılanan kısımları ve ardından kelimelerle ayrılmış boşlukları arayın), o zaman bunu bir kerede yapabilirsiniz:

("[^"]*"|'[^']*'|[\S]+)+

2
(?<!\G".{0,99999})\s|(?<=\G".{0,99999}")\s

Bu, çift tırnak içine alınmayan boşluklarla eşleşecektir. Minimum, maksimum {0,99999} kullanmalıyım çünkü Java * ve arkaya bakıldığında + desteklemiyor.


1

Diziyi aramak, her bir parçayı kapmak veya ayırmak muhtemelen daha kolay olacaktır.

Sebep, onu önceki ve sonraki boşluklara bölebilirsiniz "will be". Ancak, bir bölünmenin içindeki boşluğu görmezden gelmeyi belirtmenin bir yolunu düşünemiyorum.

(gerçek Java değil)

string = "This is a string that \"will be\" highlighted when your 'regular expression' matches something.";

regex = "\"(\\\"|(?!\\\").)+\"|[^ ]+"; // search for a quoted or non-spaced group
final = new Array();

while (string.length > 0) {
    string = string.trim();
    if (Regex(regex).test(string)) {
        final.push(Regex(regex).match(string)[0]);
        string = string.replace(regex, ""); // progress to next "word"
    }
}

Ayrıca, tek alıntıları yakalamak bazı sorunlara yol açabilir:

"Foo's Bar 'n Grill"

//=>

"Foo"
"s Bar "
"n"
"Grill"

Çözümünüz, Carl'ın örneğinin bir parçası olan tek tırnaklı dizeleri işlemiyor.
Jan Goyvaerts

1

String.split()tırnak içindeki boşlukları (ayırmayın) ve dışındakileri (ayırma) ayırt etmenin bir yolu olmadığından burada yararlı değildir. Matcher.lookingAt()muhtemelen ihtiyacınız olan şey:

String str = "This is a string that \"will be\" highlighted when your 'regular expression' matches something.";
str = str + " "; // add trailing space
int len = str.length();
Matcher m = Pattern.compile("((\"[^\"]+?\")|('[^']+?')|([^\\s]+?))\\s++").matcher(str);

for (int i = 0; i < len; i++)
{
    m.region(i, len);

    if (m.lookingAt())
    {
        String s = m.group(1);

        if ((s.startsWith("\"") && s.endsWith("\"")) ||
            (s.startsWith("'") && s.endsWith("'")))
        {
            s = s.substring(1, s.length() - 1);
        }

        System.out.println(i + ": \"" + s + "\"");
        i += (m.group(0).length() - 1);
    }
}

aşağıdaki çıktıyı üretir:

0: "This"
5: "is"
8: "a"
10: "string"
17: "that"
22: "will be"
32: "highlighted"
44: "when"
49: "your"
54: "regular expression"
75: "matches"
83: "something."

1

Marcus'un yaklaşımını beğendim, ancak tırnak işaretlerinin yanında metne izin verebilmek ve hem "hem de" alıntı karakterlerini destekleyebilmek için onu değiştirdim. Örneğin, [a =, "olarak bölmemek için a =" bir değere "ihtiyacım vardı. bir değer "].

(?<!\\G\\S{0,99999}[\"'].{0,99999})\\s|(?<=\\G\\S{0,99999}\".{0,99999}\"\\S{0,99999})\\s|(?<=\\G\\S{0,99999}'.{0,99999}'\\S{0,99999})\\s"

1

Jan'ın yaklaşımı harika ama işte kayıt için bir tane daha.

Aslında bölünmüş istese de tırnak tutarak başlığında belirtildiği gibi "will be"ve 'regular expression', o zaman düz out olan bu yöntemi kullanabilirsiniz s1, s2, s3 vb durumlar dışında bir desen Maçın (veya değiştirin)

Normal ifade:

'[^']*'|\"[^\"]*\"|( )

İki sol dönüşüm tamamlandı 'quoted strings've "double-quoted strings". Bu maçları görmezden geleceğiz. Sağ taraf, Grup 1 ile eşleşir ve boşlukları yakalar ve bunların doğru boşluklar olduğunu biliyoruz çünkü soldaki ifadelerle eşleşmiyorlar. Bunları değiştirip SplitHeresonra ayırıyoruz SplitHere. Yine, bu, istediğiniz gerçek bir bölünmüş durum içindir "will be", değil will be.

İşte tam bir çalışma uygulaması ( çevrimiçi demodaki sonuçlara bakın ).

import java.util.*;
import java.io.*;
import java.util.regex.*;
import java.util.List;

class Program {
public static void main (String[] args) throws java.lang.Exception  {

String subject = "This is a string that \"will be\" highlighted when your 'regular expression' matches something.";
Pattern regex = Pattern.compile("\'[^']*'|\"[^\"]*\"|( )");
Matcher m = regex.matcher(subject);
StringBuffer b= new StringBuffer();
while (m.find()) {
    if(m.group(1) != null) m.appendReplacement(b, "SplitHere");
    else m.appendReplacement(b, m.group(0));
}
m.appendTail(b);
String replaced = b.toString();
String[] splits = replaced.split("SplitHere");
for (String split : splits) System.out.println(split);
} // end main
} // end Program

1

C # kullanıyorsanız, kullanabilirsiniz

string input= "This is a string that \"will be\" highlighted when your 'regular expression' matches <something random>";

List<string> list1 = 
                Regex.Matches(input, @"(?<match>\w+)|\""(?<match>[\w\s]*)""|'(?<match>[\w\s]*)'|<(?<match>[\w\s]*)>").Cast<Match>().Select(m => m.Groups["match"].Value).ToList();

foreach(var v in list1)
   Console.WriteLine(v);

Grup cümleleri için herhangi bir karakter belirtebileceğinizi vurgulamak için özellikle " | <(? [\ W \ s] *)> " ekledim . (Bu durumda gruplamak için <> kullanıyorum .

Çıktı:

This
is
a
string
that
will be
highlighted
when
your
regular expression 
matches
something random

0

Bunun tek başına normal ifadeler kullanılarak mümkün olmadığına makul ölçüde eminim. Başka bir etiketin içinde bir şeyin bulunup bulunmadığını kontrol etmek bir ayrıştırma işlemidir. Bu, XML'i bir normal ifadeyle ayrıştırmaya çalışmakla aynı sorun gibi görünüyor - doğru şekilde yapılamaz. İstediğiniz sonucu, alıntılanan dizelerle eşleşen açgözlü olmayan, genel olmayan bir normal ifadeyi tekrar tekrar uygulayarak elde edebilirsiniz, sonra başka bir şey bulamadığınızda, onu boşluklara ayırın ... tüm alt dizelerin orijinal sırasını takip etmek dahil sorunlar. En iyi bahsiniz, dizge üzerinde yinelenen ve istediğiniz jetonları çıkaran gerçekten basit bir işlev yazmaktır.


Bir regex ile mümkündür, bağlantı kurduğum örneklerden bazılarına bakın. Bunun birkaç varyasyonu var ve bunu normal ifadelerle ele alan SO'da birkaç benzer soru gördüm.
Jay

1
Ne zaman normal ifadeyi kullanmayacağınızı bilmek, bir (?: (['"]) (. *?) (? <! \) (?> \\\) * \ 1 | ([ ^ \ s] +))
Rene

0

Jan'ın kabul edilen cevabında umarız yardımcı olacak birkaç değişiklik:

(['"])((?:\\\1|.)+?)\1|([^\s"']+)
  • Alıntılanmış dizeler içinde kaçan alıntılara izin verir
  • Tek ve çift alıntı için kalıbı tekrarlamaktan kaçınır; bu ayrıca gerekirse daha fazla alıntı sembolü eklemeyi de kolaylaştırır (bir yakalama grubu daha pahasına)

Bu, içinde kesme işareti bulunan kelimeleri kırar, örneğinyou're
Design by Adrian

0

Ayrıca şunu da deneyebilirsiniz:

    String str = "This is a string that \"will be\" highlighted when your 'regular expression' matches something";
    String ss[] = str.split("\"|\'");
    for (int i = 0; i < ss.length; i++) {
        if ((i % 2) == 0) {//even
            String[] part1 = ss[i].split(" ");
            for (String pp1 : part1) {
                System.out.println("" + pp1);
            }
        } else {//odd
            System.out.println("" + ss[i]);
        }
    }

Bunun neden işe yaraması gerektiğine dair gerçekten bir açıklama eklemelisiniz - kodun kendisine yorumların yanı sıra kod da ekleyebilirsiniz - mevcut haliyle, topluluğun geri kalanının ne olduğunu anlamasına yardımcı olabilecek herhangi bir açıklama sağlamaz. soruyu çözmek / cevaplamak için yaptın. Bu, özellikle zaten cevapları olan sorular için önemlidir.
ishmaelMakitla

0

Aşağıdaki, bir dizi argüman döndürür. Bağımsız değişkenler, tek veya çift tırnak içine alınmadıkça, boşluklara bölünmüş değişken "komut" dur. Eşleşmeler daha sonra tek ve çift tırnak işaretlerini kaldırmak için değiştirilir.

using System.Text.RegularExpressions;

var args = Regex.Matches(command, "[^\\s\"']+|\"([^\"]*)\"|'([^']*)'").Cast<Match>
().Select(iMatch => iMatch.Value.Replace("\"", "").Replace("'", "")).ToArray();

2
Başkalarının daha kolay anlayabilmesi için cevabınıza biraz açıklama ekleyebilir misiniz? İdeal olarak, yalnızca kod yanıtlarından kaçınmak istiyoruz.
Jaquez

0

String.split () kullanan ilk tek satırlık

String s = "This is a string that \"will be\" highlighted when your 'regular expression' matches something.";
String[] split = s.split( "(?<!(\"|').{0,255}) | (?!.*\\1.*)" );

[This, is, a, string, that, "will be", highlighted, when, your, 'regular expression', matches, something.]

boşluk tek veya çift tırnak içine alınmışsa boşta bölme
sola 255 karakter ve boşluğun sağındaki tüm karakterler olmadığında boşluğa bölünmüşse

orijinal gönderiden uyarlanmıştır (yalnızca çift tırnak işaretlerini kullanır)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.