텍스트 파일에 어떤 구분 기호가 사용되었는지 어떻게 감지해야 합니까?

StackOverflow https://stackoverflow.com/questions/761932

  •  11-09-2019
  •  | 
  •  

문제

CSV와 TSV 파일을 모두 구문 분석할 수 있어야 합니다.사용자가 차이점을 안다고 믿을 수는 없기 때문에 사용자에게 유형을 선택하라고 요구하는 것은 피하고 싶습니다.사용 중인 구분 기호를 감지하는 간단한 방법이 있습니까?

한 가지 방법은 모든 줄을 읽고 탭과 쉼표를 모두 세어 모든 줄에서 가장 일관되게 사용되는 것이 무엇인지 알아내는 것입니다.물론 데이터에 쉼표나 탭이 포함될 수 있으므로 말처럼 쉽지는 않습니다.

편집하다: 이 프로젝트의 또 다른 재미있는 측면은 파일을 읽을 때 파일의 스키마를 감지해야 한다는 것입니다. 왜냐하면 파일이 많은 것 중 하나일 수 있기 때문입니다.이는 분석할 수 있을 때까지 얼마나 많은 필드가 있는지 알 수 없음을 의미합니다.

도움이 되었습니까?

해결책

Excel에서 수행하는 방식과 유사하게 미리 보기 창에 결과를 표시할 수 있습니다.이 경우 잘못된 구분 기호가 사용되는 경우는 매우 분명합니다.그런 다음 사용자가 다양한 구분 기호를 선택하고 미리보기가 실시간으로 업데이트되도록 허용할 수 있습니다.

그런 다음 시작할 구분 기호에 대해 간단히 추측할 수 있습니다(예:쉼표나 탭이 먼저 오나요?)

다른 팁

Python의 csv 모듈에는 주어진 파일의 구분 기호와 인용 문자를 추측하는 데 사용할 수 있는 Sniffer 클래스가 있습니다.그 전략은 다음과 같습니다(csv.py의 독스트링에서 인용):


먼저, 두 개의 동일한 인용문 (가능한 quotechar) 사이에 둘러싸인 텍스트는 앞에 있으며 동일한 문자 (가능성있는 구분자)가 뒤 따릅니다.예를 들어:

         ,'some text',

가장 많은 승리를 거둔 인용문이며 구분 기호와 동일합니다.quotchar가 없다면 구분자는 이런 식으로 결정할 수 없습니다.

이 경우 다음을 시도해 보십시오.

구분 기호 ~해야 한다 각 행에서 같은 횟수로 발생합니다.그러나 잘못된 데이터로 인해 그렇지 않을 수도 있습니다.우리는 전부 또는 전혀 접근 방식을 원하지 않으므로이 숫자의 작은 변형을 허용합니다.

  1. 모든 라인에서 각 문자의 빈도 테이블을 구축하십시오.
  2. 이 주파수 (메타 주파수?)의 Freqencies 테이블을 구축하십시오.'X는 10 행에서 5 번, 1000 행에서 6 회, 2 행에서 7 회 발생했습니다.'
  3. 메타 주파수 모드를 사용하여 결정하십시오 예상되는해당 캐릭터의 빈도
  4. 캐릭터가 실제로 그 목표를 얼마나 자주 만나는 지 알아보십시오.
  5. 목표를 가장 잘 충족시키는 캐릭터는 Delimiter입니다.

성능의 이유로 데이터는 청크에서 평가되므로 가능한 데이터의 가장 작은 부분을 평가하여 필요에 따라 추가 청크를 평가할 수 있습니다.


여기서는 소스 코드를 인용하지 않겠습니다. 소스 코드는 모든 Python 설치의 Lib 디렉터리에 있습니다.

CSV에서는 쉼표 대신 세미콜론을 구분 기호로 사용할 수도 있습니다(예:g.독일어 버전의 Excel에서는 독일에서 쉼표가 소수 구분 기호로 사용되기 때문에 CSV는 세미콜론으로 구분됩니다...)

필드가 몇 개인지 아시나요? ~해야 한다 한 줄에 존재합니까?그렇다면 파일의 처음 몇 줄을 읽고 이를 기반으로 확인합니다.

내 경험에 따르면 "일반" 데이터에는 쉼표가 포함되는 경우가 많지만 탭 문자가 포함되는 경우는 거의 없습니다.이는 처음 몇 줄의 탭 수가 일정한지 확인하고 해당 선택을 선호하는 추측으로 사용해야 함을 의미합니다.물론, 정확히 어떤 데이터를 가지고 있는지에 따라 다릅니다.

궁극적으로 두 형식 모두에 완전히 유효한 파일을 갖는 것이 가능하므로 완전히 완벽하게 만들 수는 없습니다."최선을 다하는" 작업이어야 합니다.

나도 비슷한 필요에 부딪혔고 내가 생각해낸 것을 공유하겠다고 생각했습니다.아직 많은 데이터를 실행하지 않았으므로 극단적인 경우가 있을 수 있습니다.또한 이 기능의 목표는 구분 기호의 100% 확실성이 아니라 사용자에게 제시되는 최선의 추측임을 명심하십시오.

/// <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;
  }   

}

PHP에 있지만 이것은 매우 안정적인 것 같습니다.

$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;

그것이 하는 일은 다음과 같습니다:지정된 모든 구분 기호에 대해 CSV의 모든 줄을 읽고 각 구분 기호가 나타나는 횟수가 일정한지 확인합니다.그렇지 않은 경우 후보 구분 기호가 제거되고 최종적으로 하나의 구분 기호가 생성됩니다.

귀하가 제안한 솔루션이 가장 좋은 방법이라고 생각합니다.올바른 형식의 CSV 또는 TSV 파일에서 각 줄당 쉼표 또는 탭 수는 일정해야 합니다(전혀 변화가 없어야 함).파일의 모든 줄에 대해 각각의 수를 계산하고 모든 줄에 대해 어느 것이 일정한지 확인하십시오.각 줄에 대한 두 구분 기호의 개수가 동일할 가능성은 거의 없지만, 이 상상할 수 없을 정도로 드문 경우에는 물론 사용자에게 메시지를 표시할 수 있습니다.

탭 개수나 쉼표 개수가 모두 일정하지 않으면 파일 형식이 잘못되었지만 프로그램에서는 해당 파일이 (줄당 구분 기호의 표준 편차가 가장 낮은 형식에 관계없이) 파일이라고 생각한다는 메시지를 사용자에게 표시합니다.

몇 줄만 읽고 쉼표 개수와 탭 개수를 세어 비교해 보세요.쉼표가 20개이고 탭이 없으면 CSV 형식입니다.20개의 탭과 2개의 쉼표(데이터에 있을 수 있음)가 있으면 TSV에 있습니다.

"효율적인" 방법은 없습니다.

줄당 고정된 수의 필드가 있고 값 내의 쉼표나 탭이 따옴표(")로 묶여 있다고 가정하면 각 줄의 각 문자 빈도를 계산할 수 있습니다.필드가 고정되어 있지 않으면 이것이 더 어렵고, 구분 문자를 묶는 데 따옴표를 사용하지 않으면 거의 불가능할 것입니다(데이터에 따라 로케일별로 다름).

내 경험에 따르면 데이터에 탭이 포함되는 경우는 거의 없으므로 탭으로 구분된 필드 줄은 (일반적으로) 상당히 명확합니다.

하지만 쉼표는 더 어렵습니다. 특히 미국 이외의 지역에서 데이터를 읽는 경우에는 더욱 그렇습니다.부동 소수점 숫자에 쉼표가 포함되는 경우가 많기 때문에 해외에서 생성된 파일을 읽는 경우 숫자 데이터에는 엄청난 수의 쉼표가 포함될 수 있습니다.

하지만 결국 유일한 안전한 방법은 일반적으로 시도한 다음 사용자에게 제시하고 조정하도록 허용하는 것입니다. 특히 데이터에 쉼표 및/또는 탭이 포함된 경우 더욱 그렇습니다.

나는 일반 텍스트에서 탭이 줄의 첫 번째 문자를 제외하고는 매우 드물다고 가정합니다. 들여쓰기된 단락이나 소스 코드를 생각해보세요.내 생각에 내장된 탭(예:쉼표를 따르지 않는 탭) 탭이 구분 기호로 사용되고 있으며 대부분의 경우 정확하다고 가정할 수 있습니다.이것은 단지 직감일 뿐이며 어떠한 연구로도 검증되지 않았습니다.물론 사용자에게 자동 계산 모드를 무시할 수 있는 옵션을 제공할 것입니다.

기대하는 표준 열 집합이 있다고 가정합니다.

저는 FileHelper(SourceForge의 오픈 소스 프로젝트)를 사용하겠습니다.http://filehelpers.sourceforge.net/

두 개의 리더 템플릿을 정의합니다. 하나는 쉼표용이고 다른 하나는 탭용입니다.

첫 번째 방법이 실패하면 두 번째 방법을 시도해 보세요.

다음과 같이 줄이 하나의 구분 기호를 사용하는지 아니면 다른 구분 기호를 사용하는지 확인할 수 있습니다.

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);
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top