Thêm dấu cách trước chữ in hoa


193

Đưa ra chuỗi "ThisStringHasNoSpacesButItDoesHaveCapitals" cách tốt nhất để thêm khoảng trắng trước chữ in hoa là gì. Vì vậy, chuỗi kết thúc sẽ là "Chuỗi này không có không gian nhưng nó có chữ hoa"

Đây là nỗ lực của tôi với RegEx

System.Text.RegularExpressions.Regex.Replace(value, "[A-Z]", " $0")

2
Bạn có phàn nàn cụ thể về phương pháp bạn đã thực hiện không? Điều đó có thể giúp chúng tôi cải thiện phương pháp của bạn.
Blair Conrad

Nếu regex hoạt động, thì tôi sẽ gắn bó với điều đó. Regex được tối ưu hóa cho thao tác chuỗi.
Michael Meadows

Tôi chỉ tò mò là có một cách tiếp cận tốt hơn hoặc thậm chí có thể được xây dựng. Tôi thậm chí tò mò muốn xem các cách tiếp cận khác với các ngôn ngữ khác.
Bob

2
Mã của bạn đơn giản là không hoạt động vì chuỗi đã sửa đổi là giá trị trả về của hàm 'Thay thế'. Với dòng mã này: 'System.Text.RegularExpressions.Regex.Replace (value, "[AZ]", "$ 0"). Trim ();' nó sẽ hoạt động hoàn hảo. (Chỉ bình luận vì tôi tình cờ thấy bài đăng này và không ai thực sự nhìn thấy, có gì sai với mã của bạn.)
Mattu485

Regex.Replace ("ThisStringHasNoSpacesButItDoesHaveCapitals", @ "\ B [AZ]", m => "" + m);
saquib adil

Câu trả lời:


203

Các biểu thức sẽ hoạt động tốt (tôi thậm chí đã bình chọn câu trả lời của Martin Browns), nhưng chúng rất tốn kém (và cá nhân tôi thấy bất kỳ mô hình nào dài hơn một vài ký tự bị cấm đoán)

Chức năng này

string AddSpacesToSentence(string text, bool preserveAcronyms)
{
        if (string.IsNullOrWhiteSpace(text))
           return string.Empty;
        StringBuilder newText = new StringBuilder(text.Length * 2);
        newText.Append(text[0]);
        for (int i = 1; i < text.Length; i++)
        {
            if (char.IsUpper(text[i]))
                if ((text[i - 1] != ' ' && !char.IsUpper(text[i - 1])) ||
                    (preserveAcronyms && char.IsUpper(text[i - 1]) && 
                     i < text.Length - 1 && !char.IsUpper(text[i + 1])))
                    newText.Append(' ');
            newText.Append(text[i]);
        }
        return newText.ToString();
}

Sẽ làm điều đó 100.000 lần trong 2.968.750 tick, regex sẽ mất 25.000.000 tick (và đó là với regex được biên dịch).

Nó tốt hơn, với một giá trị nhất định tốt hơn (tức là nhanh hơn) tuy nhiên đó là nhiều mã hơn để duy trì. "Tốt hơn" thường thỏa hiệp các yêu cầu cạnh tranh.

Hi vọng điêu nay co ich :)

Cập nhật
Đã lâu rồi kể từ khi tôi xem xét điều này và tôi mới nhận ra thời gian không được cập nhật kể từ khi mã thay đổi (nó chỉ thay đổi một chút).

Trên một chuỗi có 'Abbbbbbbbb' được lặp lại 100 lần (tức là 1.000 byte), một chuỗi 100.000 chuyển đổi có chức năng mã hóa bằng tay 4,517,177 tick và Regex bên dưới mất 59,435,719 để chức năng Mã hóa tay chạy trong 7,6% thời gian Regex.

Cập nhật 2 Nó sẽ đưa các từ viết tắt vào tài khoản? Nó sẽ ngay bây giờ! Logic của statment khá mơ hồ, vì bạn có thể thấy việc mở rộng nó sang ...

if (char.IsUpper(text[i]))
    if (char.IsUpper(text[i - 1]))
        if (preserveAcronyms && i < text.Length - 1 && !char.IsUpper(text[i + 1]))
            newText.Append(' ');
        else ;
    else if (text[i - 1] != ' ')
        newText.Append(' ');

... Không giúp được gì cả!

Đây là phương pháp đơn giản ban đầu không lo lắng về các từ viết tắt

string AddSpacesToSentence(string text)
{
        if (string.IsNullOrWhiteSpace(text))
           return "";
        StringBuilder newText = new StringBuilder(text.Length * 2);
        newText.Append(text[0]);
        for (int i = 1; i < text.Length; i++)
        {
            if (char.IsUpper(text[i]) && text[i - 1] != ' ')
                newText.Append(' ');
            newText.Append(text[i]);
        }
        return newText.ToString();
}

8
if (char.IsUpper (text [i]) && text [i - 1]! = '') Nếu bạn chạy lại mã ở trên, nó tiếp tục thêm khoảng trắng, điều này sẽ dừng khoảng trắng được thêm vào nếu có khoảng trắng trước chữ hoa lá thư.
Paul Talbot

Tôi không chắc lắm nên tôi nghĩ tôi sẽ hỏi, liệu phương pháp này có xử lý các từ viết tắt như được mô tả trong câu trả lời của Martin Brown "DriveIsSCSICompomp" sẽ lý tưởng trở thành "Drive Is SCSI Tương thích"
Coops 23/07/13

Điều đó làm cho nó 1 ký tự bằng cách thay thế nội dung của câu lệnh for của bạn bằng câu lệnh mới được cập nhật, tôi có thể làm gì sai không?
Coops

1
Việc thêm một kiểm tra cho char.IsLetter (văn bản [i + 1]) sẽ giúp các từ viết tắt có các ký tự và chữ số đặc biệt (ví dụ ABC_DEF sẽ không bị chia thành AB C_DEF).
HeXanon

1
Tôi không chắc phần viết tắt là chính xác khi nó TẮT. Tôi vừa chạy thử nghiệm "ASentenceABC" mở rộng thành "ASentence AB C". Nên là "A Sentence AB C"
Tim Rutter

149

Giải pháp của bạn có một vấn đề ở chỗ nó đặt một khoảng trắng trước chữ cái đầu tiên T để bạn nhận được

" This String..." instead of "This String..."

Để giải quyết vấn đề này, hãy tìm chữ cái viết thường trước nó và sau đó chèn khoảng trắng ở giữa:

newValue = Regex.Replace(value, "([a-z])([A-Z])", "$1 $2");

Chỉnh sửa 1:

Nếu bạn sử dụng @"(\p{Ll})(\p{Lu})"nó sẽ nhận các ký tự có dấu là tốt.

Chỉnh sửa 2:

Nếu chuỗi của bạn có thể chứa các từ viết tắt, bạn có thể muốn sử dụng điều này:

newValue = Regex.Replace(value, @"((?<=\p{Ll})\p{Lu})|((?!\A)\p{Lu}(?>\p{Ll}))", " $0");

Vì vậy, "DriveIsSCSIC tương thích" trở thành "Ổ đĩa tương thích với SCSI"


3
Bạn cũng không thể giữ kết quả RegEx và Trim () ban đầu sao?
PandaWood

3
@PandaWood bạn có thể nhưng nó sẽ yêu cầu cấp phát bộ nhớ và sao chép chuỗi khác. Điều đó nói rằng nếu hiệu suất là một lo lắng thì Regex có lẽ không phải là cách tốt nhất để đi.
Martin Brown

Bạn cũng có thể sử dụng "([^A-Z\\s])([A-Z])", ngay cả với các từ viết tắt?
Ruben9922

82

Không kiểm tra hiệu năng, nhưng ở đây trong một dòng với linq:

var val = "ThisIsAStringToTest";
val = string.Concat(val.Select(x => Char.IsUpper(x) ? " " + x : x.ToString())).TrimStart(' ');

18

Tôi biết đây là một phần cũ, nhưng đây là phần mở rộng tôi sử dụng khi tôi cần làm điều này:

public static class Extensions
{
    public static string ToSentence( this string Input )
    {
        return new string(Input.SelectMany((c, i) => i > 0 && char.IsUpper(c) ? new[] { ' ', c } : new[] { c }).ToArray());
    }
}

Điều này sẽ cho phép bạn sử dụng MyCasedString.ToSentence()


Tôi thích ý tưởng này như một phương thức mở rộng, nếu bạn thêm TrimStart(' ')nó sẽ loại bỏ không gian hàng đầu.
dùng1069816

1
Cảm ơn @ user1069816. Tôi đã thay đổi tiện ích mở rộng để sử dụng quá tải trong SelectManyđó bao gồm một chỉ mục, theo cách này nó tránh được chữ cái đầu tiên và chi phí tiềm năng không cần thiết của một cuộc gọi bổ sung tới TrimStart(' '). Cướp.
Rob Hardy

8

Chào mừng bạn đến với Unicode

Tất cả những giải pháp này về cơ bản là sai cho văn bản hiện đại. Bạn cần phải sử dụng một cái gì đó hiểu trường hợp. Vì Bob yêu cầu các ngôn ngữ khác, tôi sẽ tặng một đôi cho Perl.

Tôi cung cấp bốn giải pháp, từ tồi tệ nhất đến tốt nhất. Chỉ có điều tốt nhất luôn luôn đúng. Những người khác có vấn đề. Đây là bản chạy thử để cho bạn thấy những gì hoạt động và những gì không, và ở đâu. Tôi đã sử dụng dấu gạch dưới để bạn có thể thấy vị trí của không gian được đặt và tôi đã đánh dấu là sai bất cứ điều gì, tốt, sai.

Testing TheLoneRanger
               Worst:    The_Lone_Ranger
               Ok:       The_Lone_Ranger
               Better:   The_Lone_Ranger
               Best:     The_Lone_Ranger
Testing MountMKinleyNationalPark
     [WRONG]   Worst:    Mount_MKinley_National_Park
     [WRONG]   Ok:       Mount_MKinley_National_Park
     [WRONG]   Better:   Mount_MKinley_National_Park
               Best:     Mount_M_Kinley_National_Park
Testing ElÁlamoTejano
     [WRONG]   Worst:    ElÁlamo_Tejano
               Ok:       El_Álamo_Tejano
               Better:   El_Álamo_Tejano
               Best:     El_Álamo_Tejano
Testing TheÆvarArnfjörðBjarmason
     [WRONG]   Worst:    TheÆvar_ArnfjörðBjarmason
               Ok:       The_Ævar_Arnfjörð_Bjarmason
               Better:   The_Ævar_Arnfjörð_Bjarmason
               Best:     The_Ævar_Arnfjörð_Bjarmason
Testing IlCaffèMacchiato
     [WRONG]   Worst:    Il_CaffèMacchiato
               Ok:       Il_Caffè_Macchiato
               Better:   Il_Caffè_Macchiato
               Best:     Il_Caffè_Macchiato
Testing MisterDženanLjubović
     [WRONG]   Worst:    MisterDženanLjubović
     [WRONG]   Ok:       MisterDženanLjubović
               Better:   Mister_Dženan_Ljubović
               Best:     Mister_Dženan_Ljubović
Testing OleKingHenry
     [WRONG]   Worst:    Ole_King_Henry
     [WRONG]   Ok:       Ole_King_Henry
     [WRONG]   Better:   Ole_King_Henry
               Best:     Ole_King_Henry_
Testing CarlosⅤºElEmperador
     [WRONG]   Worst:    CarlosⅤºEl_Emperador
     [WRONG]   Ok:       CarlosⅤº_El_Emperador
     [WRONG]   Better:   CarlosⅤº_El_Emperador
               Best:     Carlos_Ⅴº_El_Emperador

BTW, hầu hết mọi người ở đây đã chọn cách đầu tiên, cách được đánh dấu là "Tệ nhất". Một số đã chọn cách thứ hai, được đánh dấu "OK". Nhưng không ai khác trước tôi đã chỉ cho bạn cách thực hiện phương pháp "Tốt hơn" hay "Tốt nhất".

Đây là chương trình thử nghiệm với bốn phương pháp:

#!/usr/bin/env perl
use utf8;
use strict;
use warnings;

# First I'll prove these are fine variable names:
my (
    $TheLoneRanger              ,
    $MountMKinleyNationalPark  ,
    $ElÁlamoTejano              ,
    $TheÆvarArnfjörðBjarmason   ,
    $IlCaffèMacchiato           ,
    $MisterDženanLjubović         ,
    $OleKingHenry              ,
    $CarlosⅤºElEmperador        ,
);

# Now I'll load up some string with those values in them:
my @strings = qw{
    TheLoneRanger
    MountMKinleyNationalPark
    ElÁlamoTejano
    TheÆvarArnfjörðBjarmason
    IlCaffèMacchiato
    MisterDženanLjubović
    OleKingHenry
    CarlosⅤºElEmperador
};

my($new, $best, $ok);
my $mask = "  %10s   %-8s  %s\n";

for my $old (@strings) {
    print "Testing $old\n";
    ($best = $old) =~ s/(?<=\p{Lowercase})(?=[\p{Uppercase}\p{Lt}])/_/g;

    ($new = $old) =~ s/(?<=[a-z])(?=[A-Z])/_/g;
    $ok = ($new ne $best) && "[WRONG]";
    printf $mask, $ok, "Worst:", $new;

    ($new = $old) =~ s/(?<=\p{Ll})(?=\p{Lu})/_/g;
    $ok = ($new ne $best) && "[WRONG]";
    printf $mask, $ok, "Ok:", $new;

    ($new = $old) =~ s/(?<=\p{Ll})(?=[\p{Lu}\p{Lt}])/_/g;
    $ok = ($new ne $best) && "[WRONG]";
    printf $mask, $ok, "Better:", $new;

    ($new = $old) =~ s/(?<=\p{Lowercase})(?=[\p{Uppercase}\p{Lt}])/_/g;
    $ok = ($new ne $best) && "[WRONG]";
    printf $mask, $ok, "Best:", $new;
}

Khi bạn có thể ghi điểm giống như "Tốt nhất" trên bộ dữ liệu này, bạn sẽ biết mình đã thực hiện đúng. Cho đến lúc đó, bạn đã không. Không ai khác ở đây đã làm tốt hơn "Ok", và hầu hết đã làm điều đó "Tệ nhất". Tôi mong muốn được nhìn thấy ai đó gửi mã chính xác.

Tôi nhận thấy rằng mã tô sáng của StackOverflow lại khốn khổ một lần nữa. Họ đang làm cho tất cả cùng khập khiễng như (hầu hết nhưng không phải tất cả) của phần còn lại của các phương pháp tiếp cận nghèo nàn được đề cập ở đây đã thực hiện. Có phải đã quá lâu để đặt ASCII nghỉ ngơi? Nó không còn ý nghĩa nữa và giả vờ rằng tất cả những gì bạn có chỉ đơn giản là sai. Nó làm cho mã xấu.


câu trả lời 'Tốt nhất' của bạn có vẻ gần nhất cho đến nay, nhưng có vẻ như nó không phải là dấu chấm câu hàng đầu hoặc các chữ cái không viết thường hàng đầu khác. Điều này có vẻ hoạt động tốt nhất đối với tôi (trong java): thayTất cả ("(? <= [^^ \\ p {javaUpperCase}]) (? = [\\ p {javaUpperCase}])", "");
Randyaa

Hừm. Tôi không chắc chữ số La Mã thực sự nên được tính là chữ hoa trong ví dụ này. Ví dụ modifer thư chắc chắn không nên được tính. Nếu bạn vào McDonalds.com bạn sẽ thấy nó được viết mà không có khoảng trắng.
Martin Brown

Cũng cần lưu ý rằng bạn sẽ không bao giờ có được điều này là hoàn hảo. Ví dụ, tôi muốn xem một ví dụ sắp xếp "AlexandervonHumboldt", kết thúc là "Alexander von Humboldt". Tất nhiên, có những ngôn ngữ không có chữ viết hoa và chữ thường.
Martin Brown

8

Tôi đã đặt ra một phương thức mở rộng đơn giản dựa trên mã Binary Woreller, nó sẽ xử lý các từ viết tắt đúng và có thể lặp lại (sẽ không mang các từ đã cách nhau). Đây là kết quả của tôi.

public static string UnPascalCase(this string text)
{
    if (string.IsNullOrWhiteSpace(text))
        return "";
    var newText = new StringBuilder(text.Length * 2);
    newText.Append(text[0]);
    for (int i = 1; i < text.Length; i++)
    {
        var currentUpper = char.IsUpper(text[i]);
        var prevUpper = char.IsUpper(text[i - 1]);
        var nextUpper = (text.Length > i + 1) ? char.IsUpper(text[i + 1]) || char.IsWhiteSpace(text[i + 1]): prevUpper;
        var spaceExists = char.IsWhiteSpace(text[i - 1]);
        if (currentUpper && !spaceExists && (!nextUpper || !prevUpper))
                newText.Append(' ');
        newText.Append(text[i]);
    }
    return newText.ToString();
}

Dưới đây là các trường hợp kiểm tra đơn vị chức năng này vượt qua. Tôi đã thêm hầu hết các trường hợp được đề xuất của tchrist vào danh sách này. Ba trong số đó không vượt qua (hai chỉ là số La Mã) được nhận xét:

Assert.AreEqual("For You And I", "ForYouAndI".UnPascalCase());
Assert.AreEqual("For You And The FBI", "ForYouAndTheFBI".UnPascalCase());
Assert.AreEqual("A Man A Plan A Canal Panama", "AManAPlanACanalPanama".UnPascalCase());
Assert.AreEqual("DNS Server", "DNSServer".UnPascalCase());
Assert.AreEqual("For You And I", "For You And I".UnPascalCase());
Assert.AreEqual("Mount Mᶜ Kinley National Park", "MountMᶜKinleyNationalPark".UnPascalCase());
Assert.AreEqual("El Álamo Tejano", "ElÁlamoTejano".UnPascalCase());
Assert.AreEqual("The Ævar Arnfjörð Bjarmason", "TheÆvarArnfjörðBjarmason".UnPascalCase());
Assert.AreEqual("Il Caffè Macchiato", "IlCaffèMacchiato".UnPascalCase());
//Assert.AreEqual("Mister Dženan Ljubović", "MisterDženanLjubović".UnPascalCase());
//Assert.AreEqual("Ole King Henry Ⅷ", "OleKingHenryⅧ".UnPascalCase());
//Assert.AreEqual("Carlos Ⅴº El Emperador", "CarlosⅤºElEmperador".UnPascalCase());
Assert.AreEqual("For You And The FBI", "For You And The FBI".UnPascalCase());
Assert.AreEqual("A Man A Plan A Canal Panama", "A Man A Plan A Canal Panama".UnPascalCase());
Assert.AreEqual("DNS Server", "DNS Server".UnPascalCase());
Assert.AreEqual("Mount Mᶜ Kinley National Park", "Mount Mᶜ Kinley National Park".UnPascalCase());

Tương tự như các giải pháp khác được đăng ở đây, nó thất bại với chuỗi "Chính quy". Nó trả về "Thông thường O Ts"
Patee Gutee

4

Nhị phân nhị phân, tôi đã sử dụng mã được đề xuất của bạn và nó khá tốt, tôi chỉ có một bổ sung nhỏ cho nó:

public static string AddSpacesToSentence(string text)
{
    if (string.IsNullOrEmpty(text))
        return "";
    StringBuilder newText = new StringBuilder(text.Length * 2);
    newText.Append(text[0]);
            for (int i = 1; i < result.Length; i++)
            {
                if (char.IsUpper(result[i]) && !char.IsUpper(result[i - 1]))
                {
                    newText.Append(' ');
                }
                else if (i < result.Length)
                {
                    if (char.IsUpper(result[i]) && !char.IsUpper(result[i + 1]))
                        newText.Append(' ');

                }
                newText.Append(result[i]);
            }
    return newText.ToString();
}

Tôi đã thêm một điều kiện !char.IsUpper(text[i - 1]). Điều này đã sửa một lỗi có thể khiến một cái gì đó như 'AverageNOX' bị biến thành 'Trung bình NO X', điều này rõ ràng là sai, vì nó nên đọc 'NOX trung bình'.

Đáng buồn thay, điều này vẫn có một lỗi mà nếu bạn có dòng chữ 'FromAStart', bạn sẽ nhận được 'Từ AStart'.

Bất kỳ suy nghĩ về việc sửa lỗi này?


Có thể một cái gì đó như thế này sẽ hoạt động: char.IsUpper (văn bản [i]) && (char.IsLower (văn bản [i - 1]) || (char.IsLower (văn bản [i + 1]))
Martin Brown

1
Đây là if (char.IsUpper(text[i]) && !(char.IsUpper(text[i - 1]) && char.IsUpper(text[i + 1])))kết quả đúng: Kết quả kiểm tra: "Từ bắt đầu", "Từ bắt đầu", "Từ bắt đầu" nhưng bạn cần i < text.Length - 1trong điều kiện vòng lặp for để bỏ qua ký tự cuối cùng và tránh ngoại lệ phạm vi.
CallMeLaNN

Oh nó chỉ giống nhau. ! (a && b) và (! a ||! b) vì thấp hơn =! trên.
CallMeLaNN

3

Đây là của tôi:

private string SplitCamelCase(string s) 
{ 
    Regex upperCaseRegex = new Regex(@"[A-Z]{1}[a-z]*"); 
    MatchCollection matches = upperCaseRegex.Matches(s); 
    List<string> words = new List<string>(); 
    foreach (Match match in matches) 
    { 
        words.Add(match.Value); 
    } 
    return String.Join(" ", words.ToArray()); 
}

Đó có phải là C # không? Nếu vậy không gian tên là List trong? Ý bạn là ArrayList hay List <string>?
Martin Brown

Danh sách <chuỗi> sẽ ổn. Xin lỗi vì điều đó.
Cory Foy

@Martin Anh ấy luôn có cú pháp đúng, nó chỉ bị ẩn trong một <pre><code>code</code></pre>khối thay vì cú pháp Markdown. Không cần phải hạ bệ anh ta (nếu đó là bạn).
George Stocker

3

Hãy chắc chắn rằng bạn không đặt khoảng trắng ở đầu chuỗi, nhưng bạn đang đặt chúng giữa các chữ viết hoa liên tiếp. Một số câu trả lời ở đây không đề cập đến một hoặc cả hai điểm đó. Có nhiều cách khác ngoài regex, nhưng nếu bạn thích sử dụng nó, hãy thử cách này:

Regex.Replace(value, @"\B[A-Z]", " $0")

Đây \Blà một phủ định \b, vì vậy nó đại diện cho một ranh giới không từ. Nó có nghĩa là mẫu khớp với "Y" trong XYzabc, nhưng không phải trong Yzabchoặc X Yzabc. Như một phần thưởng nhỏ, bạn có thể sử dụng chuỗi này trên một chuỗi có khoảng trắng trong đó và nó sẽ không nhân đôi chúng.


3

Regex này đặt một ký tự khoảng trắng trước mỗi chữ in hoa:

using System.Text.RegularExpressions;

const string myStringWithoutSpaces = "ThisIsAStringWithoutSpaces";
var myStringWithSpaces = Regex.Replace(myStringWithoutSpaces, "([A-Z])([a-z]*)", " $1$2");

Hãy để ý không gian phía trước nếu "$ 1 $ 2", đây là những gì sẽ hoàn thành.

Đây là kết quả:

"This Is A String Without Spaces"

1
Nếu bạn muốn số cũng được tách riêng, thay vào đó hãy sử dụng mẫu biểu thức chính quy này:"([A-Z0-9])([a-z]*)"
Matthias Thomann

2

Những gì bạn có hoạt động hoàn hảo. Chỉ cần nhớ gán valuelại giá trị trả về của hàm này.

value = System.Text.RegularExpressions.Regex.Replace(value, "[A-Z]", " $0");

2

Đây là cách bạn có thể làm điều đó trong SQL

create  FUNCTION dbo.PascalCaseWithSpace(@pInput AS VARCHAR(MAX)) RETURNS VARCHAR(MAX)
BEGIN
    declare @output varchar(8000)

set @output = ''


Declare @vInputLength        INT
Declare @vIndex              INT
Declare @vCount              INT
Declare @PrevLetter varchar(50)
SET @PrevLetter = ''

SET @vCount = 0
SET @vIndex = 1
SET @vInputLength = LEN(@pInput)

WHILE @vIndex <= @vInputLength
BEGIN
    IF ASCII(SUBSTRING(@pInput, @vIndex, 1)) = ASCII(Upper(SUBSTRING(@pInput, @vIndex, 1)))
       begin 

        if(@PrevLetter != '' and ASCII(@PrevLetter) = ASCII(Lower(@PrevLetter)))
            SET @output = @output + ' ' + SUBSTRING(@pInput, @vIndex, 1)
            else
            SET @output = @output +  SUBSTRING(@pInput, @vIndex, 1) 

        end
    else
        begin
        SET @output = @output +  SUBSTRING(@pInput, @vIndex, 1) 

        end

set @PrevLetter = SUBSTRING(@pInput, @vIndex, 1) 

    SET @vIndex = @vIndex + 1
END


return @output
END

2

Lấy cảm hứng từ @MartinBrown, Two Lines of Simple Regex, sẽ giải quyết tên của bạn, bao gồm cả Acyronym ở bất cứ đâu trong chuỗi.

public string ResolveName(string name)
{
   var tmpDisplay = Regex.Replace(name, "([^A-Z ])([A-Z])", "$1 $2");
   return Regex.Replace(tmpDisplay, "([A-Z]+)([A-Z][^A-Z$])", "$1 $2").Trim();
}

Tôi thích giải pháp này. Nó ngắn và nhanh Tuy nhiên, tương tự như các giải pháp khác, Nó thất bại với chuỗi "Chính quy". Mọi giải pháp tôi đã thử ở đây đều trả về "Thông thường O Ts"
Patee Gutee

@PateeGutee OP muốn có không gian trước capitols, anh ấy không đề cập đến chữ viết tắt, chúng tôi có một bản sửa lỗi cho điều đó trong sản xuất cod
johnny 5

Bạn có thể hiển thị các sửa chữa? Tôi có các chuỗi như thế này trong dữ liệu của mình và nó cho tôi kết quả không chính xác. Cảm ơn.
Patee Gutee

@PateeGutee Xin lỗi, tôi đã đọc sai những gì bạn muốn. Đa nguyên hóa là một vấn đề khác nhau, 'Chính quy' những gì bạn đang mong đợi sẽ xảy ra "OT thông thường" hoặc "OT thông thường"
johnny 5

1
@PateeGutee Tôi đã cập nhật câu trả lời của mình cho bạn, tôi tin rằng nó nên hoạt động
johnny 5

1
replaceAll("(?<=[^^\\p{Uppercase}])(?=[\\p{Uppercase}])"," ");

1
static string AddSpacesToColumnName(string columnCaption)
    {
        if (string.IsNullOrWhiteSpace(columnCaption))
            return "";
        StringBuilder newCaption = new StringBuilder(columnCaption.Length * 2);
        newCaption.Append(columnCaption[0]);
        int pos = 1;
        for (pos = 1; pos < columnCaption.Length-1; pos++)
        {               
            if (char.IsUpper(columnCaption[pos]) && !(char.IsUpper(columnCaption[pos - 1]) && char.IsUpper(columnCaption[pos + 1])))
                newCaption.Append(' ');
            newCaption.Append(columnCaption[pos]);
        }
        newCaption.Append(columnCaption[pos]);
        return newCaption.ToString();
    }

1

Trong Ruby, thông qua Regapi:

"FooBarBaz".gsub(/(?!^)(?=[A-Z])/, ' ') # => "Foo Bar Baz"

1
Ối xin lỗi. Tôi đã bỏ lỡ rằng đó là câu hỏi cụ thể C # và được đăng ở đây Câu trả lời của Ruby :(
Artem

1

Tôi lấy giải pháp tuyệt vời của Kevin Strikers và chuyển đổi sang VB. Vì tôi bị khóa vào .NET 3.5, tôi cũng phải viết IsNullOrWhiteSpace. Điều này vượt qua tất cả các bài kiểm tra của mình.

<Extension()>
Public Function IsNullOrWhiteSpace(value As String) As Boolean
    If value Is Nothing Then
        Return True
    End If
    For i As Integer = 0 To value.Length - 1
        If Not Char.IsWhiteSpace(value(i)) Then
            Return False
        End If
    Next
    Return True
End Function

<Extension()>
Public Function UnPascalCase(text As String) As String
    If text.IsNullOrWhiteSpace Then
        Return String.Empty
    End If

    Dim newText = New StringBuilder()
    newText.Append(text(0))
    For i As Integer = 1 To text.Length - 1
        Dim currentUpper = Char.IsUpper(text(i))
        Dim prevUpper = Char.IsUpper(text(i - 1))
        Dim nextUpper = If(text.Length > i + 1, Char.IsUpper(text(i + 1)) Or Char.IsWhiteSpace(text(i + 1)), prevUpper)
        Dim spaceExists = Char.IsWhiteSpace(text(i - 1))
        If (currentUpper And Not spaceExists And (Not nextUpper Or Not prevUpper)) Then
            newText.Append(" ")
        End If
        newText.Append(text(i))
    Next
    Return newText.ToString()
End Function

1

Câu hỏi hơi cũ nhưng ngày nay có một thư viện đẹp trên Nuget thực hiện chính xác điều này cũng như nhiều chuyển đổi khác thành văn bản có thể đọc được của con người.

Kiểm tra Humanizer trên GitHub hoặc Nuget.

Thí dụ

"PascalCaseInputStringIsTurnedIntoSentence".Humanize() => "Pascal case input string is turned into sentence"
"Underscored_input_string_is_turned_into_sentence".Humanize() => "Underscored input string is turned into sentence"
"Underscored_input_String_is_turned_INTO_sentence".Humanize() => "Underscored input String is turned INTO sentence"

// acronyms are left intact
"HTML".Humanize() => "HTML"

Chỉ cần thử điều đó và liên kết đầu tiên bây giờ bị hỏng. NuGet hoạt động, nhưng gói không biên dịch trong giải pháp của tôi. Một ý tưởng tốt, nếu nó làm việc.
philw

1

Có vẻ như một cơ hội tốt cho Aggregate. Điều này được thiết kế để có thể đọc được, không nhất thiết phải đặc biệt nhanh.

someString
.Aggregate(
   new StringBuilder(),
   (str, ch) => {
      if (char.IsUpper(ch) && str.Length > 0)
         str.Append(" ");
      str.Append(ch);
      return str;
   }
).ToString();

0

Ngoài câu trả lời của Martin Brown, tôi cũng có vấn đề với các con số. Ví dụ: "Location2" hoặc "Jan22" lần lượt phải là "Location 2" và "22/1".

Đây là Biểu thức thường xuyên của tôi để làm điều đó, sử dụng câu trả lời của Martin Brown:

"((?<=\p{Ll})\p{Lu})|((?!\A)\p{Lu}(?>\p{Ll}))|((?<=[\p{Ll}\p{Lu}])\p{Nd})|((?<=\p{Nd})\p{Lu})"

Dưới đây là một vài trang web tuyệt vời để tìm hiểu ý nghĩa của từng phần:

Trình phân tích biểu thức chính quy dựa trên Java (nhưng hoạt động với hầu hết các .ex regex)

Phân tích dựa trên kịch bản hành động

Regex ở trên sẽ không hoạt động trên trang web tập lệnh hành động trừ khi bạn thay thế tất cả \p{Ll}bằng [a-z], \p{Lu}bằng [A-Z]\p{Nd}bằng [0-9].


0

Đây là giải pháp của tôi, dựa trên đề xuất của Binary Worrier và xây dựng theo ý kiến ​​của Richard Priddys, nhưng cũng tính đến việc khoảng trắng có thể tồn tại trong chuỗi được cung cấp, vì vậy nó sẽ không thêm khoảng trắng bên cạnh khoảng trắng hiện có.

public string AddSpacesBeforeUpperCase(string nonSpacedString)
    {
        if (string.IsNullOrEmpty(nonSpacedString))
            return string.Empty;

        StringBuilder newText = new StringBuilder(nonSpacedString.Length * 2);
        newText.Append(nonSpacedString[0]);

        for (int i = 1; i < nonSpacedString.Length; i++)
        {
            char currentChar = nonSpacedString[i];

            // If it is whitespace, we do not need to add another next to it
            if(char.IsWhiteSpace(currentChar))
            {
                continue;
            }

            char previousChar = nonSpacedString[i - 1];
            char nextChar = i < nonSpacedString.Length - 1 ? nonSpacedString[i + 1] : nonSpacedString[i];

            if (char.IsUpper(currentChar) && !char.IsWhiteSpace(nextChar) 
                && !(char.IsUpper(previousChar) && char.IsUpper(nextChar)))
            {
                newText.Append(' ');
            }
            else if (i < nonSpacedString.Length)
            {
                if (char.IsUpper(currentChar) && !char.IsWhiteSpace(nextChar) && !char.IsUpper(nextChar))
                {
                    newText.Append(' ');
                }
            }

            newText.Append(currentChar);
        }

        return newText.ToString();
    }

0

Đối với bất cứ ai đang tìm kiếm một hàm C ++ trả lời cùng câu hỏi này, bạn có thể sử dụng như sau. Điều này được mô hình hóa sau câu trả lời được đưa ra bởi @Binary Worrier. Phương pháp này chỉ bảo tồn các từ viết tắt tự động.

using namespace std;

void AddSpacesToSentence(string& testString)
        stringstream ss;
        ss << testString.at(0);
        for (auto it = testString.begin() + 1; it != testString.end(); ++it )
        {
            int index = it - testString.begin();
            char c = (*it);
            if (isupper(c))
            {
                char prev = testString.at(index - 1);
                if (isupper(prev))
                {
                    if (index < testString.length() - 1)
                    {
                        char next = testString.at(index + 1);
                        if (!isupper(next) && next != ' ')
                        {
                            ss << ' ';
                        }
                    }
                }
                else if (islower(prev)) 
                {
                   ss << ' ';
                }
            }

            ss << c;
        }

        cout << ss.str() << endl;

Các chuỗi kiểm tra tôi đã sử dụng cho chức năng này và kết quả là:

  • "helloWorld" -> "xin chào thế giới"
  • "HelloWorld" -> "Xin chào thế giới"
  • "HelloABCWorld" -> "Xin chào thế giới ABC"
  • "HelloWorldABC" -> "Xin chào thế giới ABC"
  • "ABCHelloWorld" -> "Thế giới xin chào ABC"
  • "THẾ GIỚI ABC HELLO" -> "THẾ GIỚI ABC HELLO"
  • "ABCHELLOWORLD" -> "ABCHELLOWORLD"
  • "A" -> "A"

0

Một C # giải pháp cho một chuỗi đầu vào mà chỉ bao gồm các ký tự ASCII. Các regex kết hợp lookbehind tiêu cực để bỏ qua một chữ cái viết hoa (chữ in hoa) xuất hiện ở phần đầu của chuỗi. Sử dụng Regex.Replace () để trả về chuỗi mong muốn.

Cũng xem bản demo regex101.com .

using System;
using System.Text.RegularExpressions;

public class RegexExample
{
    public static void Main()
    {
        var text = "ThisStringHasNoSpacesButItDoesHaveCapitals";

        // Use negative lookbehind to match all capital letters
        // that do not appear at the beginning of the string.
        var pattern = "(?<!^)([A-Z])";

        var rgx = new Regex(pattern);
        var result = rgx.Replace(text, " $1");
        Console.WriteLine("Input: [{0}]\nOutput: [{1}]", text, result);
    }
}

Đầu ra dự kiến:

Input: [ThisStringHasNoSpacesButItDoesHaveCapitals]
Output: [This String Has No Spaces But It Does Have Capitals]

Cập nhật: Đây là một biến thể cũng sẽ xử lý các từ viết tắt (chuỗi các chữ cái viết hoa).

Đồng thời xem bản demo regex101.combản demo ideone.com .

using System;
using System.Text.RegularExpressions;

public class RegexExample
{
    public static void Main()
    {
        var text = "ThisStringHasNoSpacesASCIIButItDoesHaveCapitalsLINQ";

        // Use positive lookbehind to locate all upper-case letters
        // that are preceded by a lower-case letter.
        var patternPart1 = "(?<=[a-z])([A-Z])";

        // Used positive lookbehind and lookahead to locate all
        // upper-case letters that are preceded by an upper-case
        // letter and followed by a lower-case letter.
        var patternPart2 = "(?<=[A-Z])([A-Z])(?=[a-z])";

        var pattern = patternPart1 + "|" + patternPart2;
        var rgx = new Regex(pattern);
        var result = rgx.Replace(text, " $1$2");

        Console.WriteLine("Input: [{0}]\nOutput: [{1}]", text, result);
    }
}

Đầu ra dự kiến:

Input: [ThisStringHasNoSpacesASCIIButItDoesHaveCapitalsLINQ]
Output: [This String Has No Spaces ASCII But It Does Have Capitals LINQ]

0

Đây là một giải pháp kỹ lưỡng hơn mà không đặt khoảng trắng trước các từ:

Lưu ý: Tôi đã sử dụng nhiều Regex (không súc tích nhưng nó cũng sẽ xử lý các từ viết tắt và các từ đơn)

Dim s As String = "ThisStringHasNoSpacesButItDoesHaveCapitals"
s = System.Text.RegularExpressions.Regex.Replace(s, "([a-z])([A-Z](?=[A-Z])[a-z]*)", "$1 $2")
s = System.Text.RegularExpressions.Regex.Replace(s, "([A-Z])([A-Z][a-z])", "$1 $2")
s = System.Text.RegularExpressions.Regex.Replace(s, "([a-z])([A-Z][a-z])", "$1 $2")
s = System.Text.RegularExpressions.Regex.Replace(s, "([a-z])([A-Z][a-z])", "$1 $2") // repeat a second time

Trong :

"ThisStringHasNoSpacesButItDoesHaveCapitals"
"IAmNotAGoat"
"LOLThatsHilarious!"
"ThisIsASMSMessage"

Ra :

"This String Has No Spaces But It Does Have Capitals"
"I Am Not A Goat"
"LOL Thats Hilarious!"
"This Is ASMS Message" // (Difficult to handle single letter words when they are next to acronyms.)

Điều này xuất ra "Chuỗi này không có không gian nhưng nó có chữ viết hoa"
Andy Robinson

Xin chào @AndyRobinson, cảm ơn. Tôi đã thay đổi để sử dụng nhiều Regex thay thế. Không chắc chắn nếu có một cách ngắn gọn hơn, nhưng nó hoạt động ngay bây giờ.
CrazyTim

0

Tất cả các phản ứng trước đây nhìn quá phức tạp.

Tôi đã có chuỗi có hỗn hợp chữ hoa và _ được sử dụng, chuỗi.Replace () để tạo _, "" và sử dụng đoạn sau để thêm khoảng trắng ở chữ in hoa.

for (int i = 0; i < result.Length; i++)
{
    if (char.IsUpper(result[i]))
    {
        counter++;
        if (i > 1) //stops from adding a space at if string starts with Capital
        {
            result = result.Insert(i, " ");
            i++; //Required** otherwise stuck in infinite 
                 //add space loop over a single capital letter.
        }
    }
}

0

Lấy cảm hứng từ câu trả lời của Binary Worrier, tôi đã thay đổi.

Đây là kết quả:

/// <summary>
/// String Extension Method
/// Adds white space to strings based on Upper Case Letters
/// </summary>
/// <example>
/// strIn => "HateJPMorgan"
/// preserveAcronyms false => "Hate JP Morgan"
/// preserveAcronyms true => "Hate JPMorgan"
/// </example>
/// <param name="strIn">to evaluate</param>
/// <param name="preserveAcronyms" >determines saving acronyms (Optional => false) </param>
public static string AddSpaces(this string strIn, bool preserveAcronyms = false)
{
    if (string.IsNullOrWhiteSpace(strIn))
        return String.Empty;

    var stringBuilder = new StringBuilder(strIn.Length * 2)
        .Append(strIn[0]);

    int i;

    for (i = 1; i < strIn.Length - 1; i++)
    {
        var c = strIn[i];

        if (Char.IsUpper(c) && (Char.IsLower(strIn[i - 1]) || (preserveAcronyms && Char.IsLower(strIn[i + 1]))))
            stringBuilder.Append(' ');

        stringBuilder.Append(c);
    }

    return stringBuilder.Append(strIn[i]).ToString();
}

Đã kiểm tra bằng cách sử dụng đồng hồ bấm giờ chạy 10000000 lần lặp và các độ dài và kết hợp chuỗi khác nhau.

Trung bình 50% (có thể nhiều hơn một chút) nhanh hơn câu trả lời Binary Worrier.


0
    private string GetProperName(string Header)
    {
        if (Header.ToCharArray().Where(c => Char.IsUpper(c)).Count() == 1)
        {
            return Header;
        }
        else
        {
            string ReturnHeader = Header[0].ToString();
            for(int i=1; i<Header.Length;i++)
            {
                if (char.IsLower(Header[i-1]) && char.IsUpper(Header[i]))
                {
                    ReturnHeader += " " + Header[i].ToString();
                }
                else
                {
                    ReturnHeader += Header[i].ToString();
                }
            }

            return ReturnHeader;
        }

        return Header;
    }

0

Câu này bao gồm các từ viết tắt và các từ viết tắt số nhiều và nhanh hơn một chút so với câu trả lời được chấp nhận:

public string Sentencify(string value)
{
    if (string.IsNullOrWhiteSpace(value))
        return string.Empty;

    string final = string.Empty;
    for (int i = 0; i < value.Length; i++)
    {
        if (i != 0 && Char.IsUpper(value[i]))
        {
            if (!Char.IsUpper(value[i - 1]))
                final += " ";
            else if (i < (value.Length - 1))
            {
                if (!Char.IsUpper(value[i + 1]) && !((value.Length >= i && value[i + 1] == 's') ||
                                                     (value.Length >= i + 1 && value[i + 1] == 'e' && value[i + 2] == 's')))
                    final += " ";
            }
        }

        final += value[i];
    }

    return final;
}

Vượt qua các bài kiểm tra này:

string test1 = "RegularOTs";
string test2 = "ThisStringHasNoSpacesASCIIButItDoesHaveCapitalsLINQ";
string test3 = "ThisStringHasNoSpacesButItDoesHaveCapitals";

câu trả lời được chấp nhận liên quan đến trường hợp giá trị là null
Chris F Carroll

Điều này thêm một khoảng trắng phía trước đầu ra, tức là HireDate => "Ngày thuê". Cần một Final.TrimStart hoặc một cái gì đó. Tôi nghĩ đó là những gì một trong những câu trả lời khác được chỉ ra bên dưới nhưng vì sắp xếp lại nên tôi không chắc anh ấy có nói chuyện với bạn không vì câu trả lời của anh ấy dựa trên RegEx.
b_levitt

Bắt tốt ... nên đã thêm điểm đánh dấu bắt đầu và kết thúc vào các thử nghiệm của tôi ... đã được sửa ngay bây giờ.
Serj Sagan

Tương tự như các giải pháp khác được đăng ở đây, nó thất bại với chuỗi "Chính quy". Nó trả về "Thông thường O Ts"
Patee Gutee

Cảm ơn bạn đã đưa ra số nhiều từ viết tắt, tôi cũng đã cập nhật để làm việc này.
Serj Sagan

0

Một triển khai với fold, còn được gọi là Aggregate:

    public static string SpaceCapitals(this string arg) =>
       new string(arg.Aggregate(new List<Char>(),
                      (accum, x) => 
                      {
                          if (Char.IsUpper(x) &&
                              accum.Any() &&
                              // prevent double spacing
                              accum.Last() != ' ' &&
                              // prevent spacing acronyms (ASCII, SCSI)
                              !Char.IsUpper(accum.Last()))
                          {
                              accum.Add(' ');
                          }

                          accum.Add(x);

                          return accum;
                      }).ToArray());

Ngoài yêu cầu, việc triển khai này còn lưu chính xác các không gian hàng đầu, bên trong, dấu và chữ viết tắt, ví dụ,

" SpacedWord " => " Spaced Word ",  

"Inner Space" => "Inner Space",  

"SomeACRONYM" => "Some ACRONYM".

0

Một cách đơn giản để thêm khoảng trắng sau chữ in thường, chữ in hoa hoặc chữ số.

    string AddSpacesToSentence(string value, bool spaceLowerChar = true, bool spaceDigitChar = true, bool spaceSymbolChar = false)
    {
        var result = "";

        for (int i = 0; i < value.Length; i++)
        {
            char currentChar = value[i];
            char nextChar = value[i < value.Length - 1 ? i + 1 : value.Length - 1];

            if (spaceLowerChar && char.IsLower(currentChar) && !char.IsLower(nextChar))
            {
                result += value[i] + " ";
            }
            else if (spaceDigitChar && char.IsDigit(currentChar) && !char.IsDigit(nextChar))
            {
                result += value[i] + " ";
            }
            else if(spaceSymbolChar && char.IsSymbol(currentChar) && !char.IsSymbol(nextChar))
            {
                result += value[i];
            }
            else
            {
                result += value[i];
            }
        }

        return result;
    }

1
Câu trả lời chỉ có mã được khuyến khích. Vui lòng nhấp vào chỉnh sửa và thêm một số từ tóm tắt cách mã của bạn giải quyết câu hỏi hoặc có thể giải thích câu trả lời của bạn khác với câu trả lời / câu trả lời trước đó như thế nào. Từ đánh giá
Nick
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.