Ai đó sẽ sớm đưa ra câu trả lời thực hiện điều này với một regex duy nhất. Tôi không thông minh, nhưng chỉ vì lợi ích của sự cân bằng, đây là một gợi ý mà không sử dụng một regex hoàn toàn. Dựa trên câu ngạn ngữ cũ khi bạn cố gắng giải quyết vấn đề với regex, bạn sẽ gặp phải hai vấn đề. :)
Cá nhân cho tôi thiếu regex-fu, tôi muốn làm một trong các cách sau:
- Sử dụng đơn giản regex dựa trên
Replace
để thoát khỏi bất kỳ dấu phẩy bên dấu ngoặc kép với cái gì khác (ví dụ: ","
). Sau đó, bạn có thể làm một đơn giản string.Split()
trên kết quả và unescape mỗi mục trong mảng kết quả trước khi bạn sử dụng nó. Đây là yucky. Một phần vì nó xử lý mọi thứ, và một phần vì nó cũng sử dụng các regex. Boooo!
- Phân tích cú pháp bằng tay, char bằng char. Chuyển đổi chuỗi thành một mảng char, sau đó lặp qua nó, lưu ý xem bạn có phải là "dấu ngoặc kép bên trong" hay không, và xây dựng mảng kết quả một char tại một thời điểm.
- Tương tự như đề xuất trước, nhưng sử dụng csv-parser từ một người nào đó trên internet. Ví dụ tôi tạo dưới đây không chính xác vượt qua tất cả các bài kiểm tra từ đặc tả csv, vì vậy nó chỉ thực sự là một hướng dẫn để minh họa quan điểm của tôi.
Có một cơ hội tốt về các tùy chọn không phải regex sẽ hoạt động tốt hơn nếu được viết tốt bởi vì regex có thể tốn kém một chút khi quét chuỗi nội bộ tìm kiếm mẫu.
Thực sự, tôi chỉ muốn chỉ ra rằng bạn không phải sử dụng regex. :)
Đây là một triển khai khá ngây thơ về đề xuất thứ hai của tôi. Trên PC của tôi, nó phân tích cú pháp 1 triệu chuỗi 15 cột trong vòng hơn 4,5 giây.
public class ManualParser : IParser
{
public IEnumerable<string> Parse(string line)
{
if (string.IsNullOrWhiteSpace(line)) return new List<string>();
line = line.Trim();
if (line.Contains(",") == false) return new[] { line.Trim('"') };
if (line.Contains("\"") == false) return line.Split(',').Select(c => c.Trim());
bool withinQuotes = false;
var builder = new List<string>();
var trimChars = new[] { ' ', '"' };
int left = 0;
int right = 0;
for (right = 0; right < line.Length; right++)
{
char c = line[right];
if (c == '"')
{
withinQuotes = !withinQuotes;
continue;
}
if (c == ',' && !withinQuotes)
{
builder.Add(line.Substring(left, right - left).Trim(trimChars));
right++; // Jump the comma
left = right;
}
}
builder.Add(line.Substring(left, right - left).Trim(trimChars));
return builder;
}
}
Dưới đây là một số xét nghiệm đơn vị cho nó:
[TestFixture]
public class ManualParserTests
{
[Test]
public void Parse_GivenStringWithNoQuotesAndNoCommas_ShouldReturnThatString()
{
// Arrange
var parser = new ManualParser();
// Act
string[] result = parser.Parse("This is my data").ToArray();
// Assert
Assert.AreEqual(1, result.Length, "Should only be one column returned");
Assert.AreEqual("This is my data", result[0], "Incorrect value is returned");
}
[Test]
public void Parse_GivenStringWithNoQuotesAndOneComma_ShouldReturnTwoColumns()
{
// Arrange
var parser = new ManualParser();
// Act
string[] result = parser.Parse("This is, my data").ToArray();
// Assert
Assert.AreEqual(2, result.Length, "Should be 2 columns returned");
Assert.AreEqual("This is", result[0], "First value is incorrect");
Assert.AreEqual("my data", result[1], "Second value is incorrect");
}
[Test]
public void Parse_GivenStringWithQuotesAndNoCommas_ShouldReturnColumnWithoutQuotes()
{
// Arrange
var parser = new ManualParser();
// Act
string[] result = parser.Parse("\"This is my data\"").ToArray();
// Assert
Assert.AreEqual(1, result.Length, "Should be 1 column returned");
Assert.AreEqual("This is my data", result[0], "Value is incorrect");
}
[Test]
public void Parse_GivenStringWithQuotesAndCommas_ShouldReturnColumnsWithoutQuotes()
{
// Arrange
var parser = new ManualParser();
// Act
string[] result = parser.Parse("\"This is\", my data").ToArray();
// Assert
Assert.AreEqual(2, result.Length, "Should be 2 columns returned");
Assert.AreEqual("This is", result[0], "First value is incorrect");
Assert.AreEqual("my data", result[1], "Second value is incorrect");
}
[Test]
public void Parse_GivenStringWithQuotesContainingCommasAndCommas_ShouldReturnColumnsWithoutQuotes()
{
// Arrange
var parser = new ManualParser();
// Act
string[] result = parser.Parse("\"This, is\", my data").ToArray();
// Assert
Assert.AreEqual(2, result.Length, "Should be 2 columns returned");
Assert.AreEqual("This, is", result[0], "First value is incorrect");
Assert.AreEqual("my data", result[1], "Second value is incorrect");
}
}
Và đây là một ứng dụng mẫu mà tôi đã thử nghiệm thông với:
class Program
{
static void Main(string[] args)
{
RunTest();
}
private static void RunTest()
{
var parser = new ManualParser();
string csv = Properties.Resources.Csv;
var result = new StringBuilder();
var s = new Stopwatch();
for (int test = 0; test < 3; test++)
{
int lineCount = 0;
s.Start();
for (int i = 0; i < 1000000/50; i++)
{
foreach (var line in csv.Split(new[] { Environment.NewLine }, StringSplitOptions.None))
{
string cur = line + s.ElapsedTicks.ToString();
result.AppendLine(parser.Parse(cur).ToString());
lineCount++;
}
}
s.Stop();
Console.WriteLine("Completed {0} lines in {1}ms", lineCount, s.ElapsedMilliseconds);
s.Reset();
result = new StringBuilder();
}
}
}
Bạn đang cần thiết để sử dụng regex? – rownage
hui, Hãy bình luận, b) upvote và c) chọn câu trả lời giúp bạn nhiều nhất. :) –