2009-04-17 30 views
20

Tôi cần có khả năng phân tích cú pháp cả tệp CSV và TSV. Tôi không thể dựa vào người dùng để biết sự khác biệt, vì vậy tôi muốn tránh yêu cầu người dùng chọn loại. Có cách nào đơn giản để phát hiện dấu phân cách nào đang được sử dụng không?Tôi nên phát hiện dấu phân cách nào được sử dụng trong tệp văn bản?

Một cách sẽ được đọc ở mọi dòng và đếm cả tab và dấu phẩy và tìm ra cách nào được sử dụng nhất quán trong mọi dòng. Tất nhiên, dữ liệu có thể bao gồm dấu phẩy hoặc tab, để có thể nói dễ hơn làm.

Chỉnh sửa: Một khía cạnh thú vị khác của dự án này là tôi cũng sẽ cần phải phát hiện giản đồ của tệp khi đọc nó bởi vì nó có thể là một trong nhiều. Điều này có nghĩa là tôi sẽ không biết mình có bao nhiêu trường cho đến khi tôi có thể phân tích nó.

Trả lời

14

Bạn có thể hiển thị kết quả trong cửa sổ xem trước - tương tự như cách Excel thực hiện. Nó là khá rõ ràng khi delimiter sai đang được sử dụng trong trường hợp đó. Sau đó, bạn có thể cho phép họ chọn một loạt các dấu phân tách và có bản cập nhật xem trước trong thời gian thực.

Sau đó, bạn có thể chỉ cần thực hiện một dự đoán đơn giản làm dấu phân tách để bắt đầu (ví dụ: dấu phẩy hoặc tab đến trước).

+2

Hiển thị cho người dùng kết quả trước khi nhập là một bước đi tốt mà tôi nghĩ, nhưng việc đoán thông minh cũng là điều tuyệt vời đối với chúng tôi. Vì vậy, sự kết hợp thực sự tốt đẹp! – BerggreenDK

+0

một đề xuất-Nếu bạn đang làm một cửa sổ xem trước và bạn muốn "đoán" đó là dấu phân cách chính xác, sau đó bạn có thể chia trên một delim có thể. và xem liệu mười dòng đầu tiên có cùng số trường hay không, so sánh với tất cả các delims bình thường khác. Đó là một cược tốt có thể hoạt động với cùng một số trường trong suốt quá trình. Như [Jon Skeet đã nói] (https://stackoverflow.com/questions/761932/how-should-i-detect-which-delimiter-is-used-in-a-text-file/761949#761949) nó hoàn toàn có thể đó là một tab được phân cách bằng dấu phẩy và dấu phẩy hợp lệ, nhưng tab đó là lựa chọn dự định. – PsychoData

4

Bạn có biết có bao nhiêu trường nên có mặt trên mỗi dòng không? Nếu vậy, tôi sẽ đọc vài dòng đầu tiên của tập tin và kiểm tra dựa trên đó.

Theo kinh nghiệm của tôi, dữ liệu "bình thường" thường chứa dấu phẩy nhưng hiếm khi chứa các ký tự tab. Điều này sẽ gợi ý rằng bạn nên kiểm tra số lượng tab nhất quán trong một vài dòng đầu tiên và đi với lựa chọn đó như một dự đoán được ưa thích. Tất nhiên, nó phụ thuộc vào chính xác những dữ liệu bạn đã có.

Cuối cùng, bạn hoàn toàn có thể có một tệp hoàn toàn hợp lệ cho cả hai định dạng - vì vậy bạn không thể làm cho nó hoàn toàn dễ dàng. Nó sẽ phải là một công việc "nỗ lực tốt nhất".

1

Không có cách "hiệu quả".

2

Tôi tưởng tượng rằng giải pháp được đề xuất của bạn sẽ là cách tốt nhất để đi. Trong tệp CSV hoặc TSV được định dạng đúng, số lượng dấu phẩy hoặc tab tương ứng trên mỗi dòng phải là hằng số (không có biến thể nào). Thực hiện đếm từng dòng cho mỗi dòng của tệp và kiểm tra xem hằng số nào là hằng số cho tất cả các dòng. Dường như không thể đếm được cả hai dấu phân cách cho mỗi dòng là giống hệt nhau, nhưng trong trường hợp hiếm có này, bạn có thể nhắc người dùng.

Nếu không có số lượng tab hoặc dấu phẩy không đổi, thì hiển thị thông báo cho người dùng nói rằng tệp không đúng định dạng nhưng chương trình cho rằng tệp đó là tệp (bất kỳ định dạng nào có độ lệch chuẩn nhỏ nhất trên mỗi dòng) .

1

Giả sử rằng có một số trường cố định trên mỗi dòng và bất kỳ dấu phẩy hoặc tab nào trong giá trị được kèm theo dấu ngoặc kép ("), bạn có thể làm việc với tần suất của mỗi ký tự trong mỗi dòng. các trường không cố định, điều này khó hơn và nếu dấu ngoặc kép không được sử dụng để kèm theo các ký tự phân cách khác, nó sẽ là, tôi nghi ngờ, gần như không thể (và tùy thuộc vào dữ liệu, địa phương cụ thể)

1

In kinh nghiệm của tôi, dữ liệu hiếm khi chứa các tab, do đó, một dòng các trường được phân cách bằng tab (thường) sẽ khá rõ ràng. n chứa số lượng lớn dấu phẩy nếu bạn đang đọc tệp được tạo ra từ quốc gia, vì số dấu chấm động thường sẽ chứa chúng.

Cuối cùng, điều an toàn duy nhất, thường là thử, sau đó trình bày cho người dùng và cho phép họ điều chỉnh, đặc biệt nếu dữ liệu của bạn sẽ chứa dấu phẩy và/hoặc tab.

1

Tôi giả định rằng trong văn bản thông thường, các tab rất hiếm, ngoại trừ ký tự đầu tiên trên một dòng - hãy suy nghĩ các đoạn văn thụt lề hoặc mã nguồn. Tôi nghĩ nếu bạn tìm thấy các tab được nhúng (ví dụ: các tab không theo dấu phẩy), bạn có thể giả định rằng các tab đang được sử dụng làm dấu phân tách và chính xác phần lớn thời gian. Đây chỉ là linh cảm, không được xác minh với bất kỳ nghiên cứu nào. Dĩ nhiên tôi muốn cung cấp cho người dùng tùy chọn ghi đè chế độ được tính tự động.

2

Chỉ cần đọc một vài dòng, đếm số lượng dấu phẩy và số lượng tab và so sánh chúng. Nếu có 20 dấu phẩy và không có tab, thì đó là trong CSV. Nếu có 20 tab và 2 dấu phẩy (có thể trong dữ liệu), nó nằm trong TSV.

1

Giả sử bạn có một bộ tiêu chuẩn của cột bạn sẽ mong đợi ...

Tôi sẽ sử dụng FileHelper (dự án mã nguồn mở trên SourceForge). http://filehelpers.sourceforge.net/

Xác định hai mẫu trình đọc, một dành cho hôn mê, một cho tab.

Nếu lần đầu tiên không thành công, hãy thử thứ hai.

+0

Điều này thật thú vị. Tôi sẽ đọc trong nhiều lược đồ và cố gắng tìm ra lược đồ mà tệp hiện tại dựa trên bố cục tệp (số trường, thứ tự trường, v.v.). FileHelper có thể xác định lớp nào sẽ sử dụng khi chạy không? – samiz

14

Trong Python, có một lớp Sniffer trong mô-đun csv có thể được sử dụng để đoán dấu phân cách và ký tự trích dẫn của tệp đã cho. chiến lược của nó được (trích dẫn từ docstrings csv.py của):


[Đầu tiên, nhìn] cho văn bản kèm theo giữa hai dấu ngoặc kép giống hệt (các quotechar có thể xảy ra) được trước và sau bởi cùng một ký tự (các có thể xảy ra delimiter). Ví dụ:

  ,'some text', 

Trích dẫn có nhiều chiến thắng nhất, cùng với dấu tách. Nếu không có quotechar thì dấu phân cách không thể được xác định theo cách này.

Trong trường hợp đó, hãy thử như sau:

Delimiter nên xảy ra cùng một số lần trên mỗi hàng. Tuy nhiên, do dữ liệu không đúng định dạng, nó có thể không. Chúng tôi không muốn một cách tiếp cận tất cả hoặc không có gì, vì vậy chúng tôi cho phép các biến thể nhỏ trong số này .

  1. tạo bảng tần số mỗi ký tự trên mỗi dòng.
  2. tạo bảng tần suất tần suất (tần số meta?), Ví dụ: 'x xảy ra 5 lần trong 10 dòng, 6 lần trong 1000 dòng, 7 lần trong 2 hàng'
  3. sử dụng chế độ của siêu tần số để xác định dự kiến ​​ tần số cho rằng nhân vật
  4. tìm hiểu tần suất các nhân vật thực sự đáp ứng được mục tiêu đó
  5. nhân vật đáp ứng tốt nhất mục tiêu của nó là delimiter

Đối performan lý do ce, dữ liệu được đánh giá theo khối, vì vậy nó có thể thử và đánh giá phần nhỏ nhất của dữ liệu có thể, đánh giá khối bổ sung khi cần thiết.


Tôi sẽ không trích dẫn mã nguồn ở đây - nó nằm trong thư mục Lib của mọi cài đặt Python.

Hãy nhớ rằng CSV cũng có thể sử dụng dấu chấm phẩy thay vì dấu phẩy như delimiters (ví dụ như trong các phiên bản Đức của Excel, CSV được dấu chấm phẩy được phân định bởi vì dấu phẩy được sử dụng như dải phân cách thập phân ở Đức ...)

3

Đó là trong PHP nhưng này có vẻ là khá đáng tin cậy:

$csv = 'something;something;something 
someotherthing;someotherthing;someotherthing 
'; 
$candidates = array(',', ';', "\t"); 
$csvlines = explode("\n", $csv); 
foreach ($candidates as $candidatekey => $candidate) { 
$lastcnt = 0; 
foreach ($csvlines as $csvline) { 
    if (strlen($csvline) <= 2) continue; 
    $thiscnt = substr_count($csvline, $candidate); 
    if (($thiscnt == 0) || ($thiscnt != $lastcnt) && ($lastcnt != 0)) { 
    unset($candidates[$candidatekey]); 
    break; 
    } 
    $lastcnt = $thiscnt; 
} 
} 
$delim = array_shift($candidates); 
echo $delim; 

gì nó làm là như sau: Đối với mỗi delimiter có thể xác định, nó đọc tất cả các dòng trong CSV và kiểm tra xem số lần mỗi seperator xảy ra là không đổi. Nếu không, seperator ứng cử viên được loại bỏ và cuối cùng bạn nên kết thúc với một seperator.

0

Bạn có thể kiểm tra xem một dòng đang sử dụng một delimiter này hay cách khác như thế này:

while ((line = readFile.ReadLine()) != null) 
{ 
    if (line.Split('\t').Length > line.Split(',').Length) // tab delimited or comma delimited? 
     row = line.Split('\t'); 
    else 
     row = line.Split(','); 

    parsedData.Add(row); 
} 
+3

Điều gì sẽ xảy ra nếu nó được phân định bằng dấu phẩy bằng một loạt dấu phẩy trong dữ liệu hoặc ngược lại? Điều này có khả năng có thể cố gắng phân tích cú pháp cùng một tệp ở cả hai định dạng phân cách bằng dấu phẩy hoặc phân tách bằng dấu phẩy dựa trên dữ liệu trong các dòng. – samiz

2

tôi chạy vào một nhu cầu tương tự và nghĩ rằng tôi sẽ chia sẻ những gì tôi đã đưa ra. Tôi chưa chạy nhiều dữ liệu thông qua nó, vì vậy có những trường hợp có thể xảy ra. Ngoài ra, hãy nhớ rằng mục tiêu của hàm này không phải là 100% chắc chắn của dấu phân tách, nhưng tốt nhất là nên được trình bày cho người dùng.

/// <summary> 
/// Analyze the given lines of text and try to determine the correct delimiter used. If multiple 
/// candidate delimiters are found, the highest frequency delimiter will be returned. 
/// </summary> 
/// <example> 
/// string discoveredDelimiter = DetectDelimiter(dataLines, new char[] { '\t', '|', ',', ':', ';' }); 
/// </example> 
/// <param name="lines">Lines to inspect</param> 
/// <param name="delimiters">Delimiters to search for</param> 
/// <returns>The most probable delimiter by usage, or null if none found.</returns> 
public string DetectDelimiter(IEnumerable<string> lines, IEnumerable<char> delimiters) { 
    Dictionary<char, int> delimFrequency = new Dictionary<char, int>(); 

    // Setup our frequency tracker for given delimiters 
    delimiters.ToList().ForEach(curDelim => 
    delimFrequency.Add(curDelim, 0) 
); 

    // Get a total sum of all occurrences of each delimiter in the given lines 
    delimFrequency.ToList().ForEach(curDelim => 
    delimFrequency[curDelim.Key] = lines.Sum(line => line.Count(p => p == curDelim.Key)) 
); 

    // Find delimiters that have a frequency evenly divisible by the number of lines 
    // (correct & consistent usage) and order them by largest frequency 
    var possibleDelimiters = delimFrequency 
        .Where(f => f.Value > 0 && f.Value % lines.Count() == 0) 
        .OrderByDescending(f => f.Value) 
        .ToList(); 

    // If more than one possible delimiter found, return the most used one 
    if (possibleDelimiters.Any()) { 
    return possibleDelimiters.First().Key.ToString(); 
    } 
    else { 
    return null; 
    } 

} 
Các vấn đề liên quan