對不起,我以前的答案;我誤解了數據的格式。
如前,讓我們先讀文本文件轉換成字符串單元陣列:
fid = fopen('deaths.txt');
scanned_fields = textscan(fid, '%s', 'Delimiter','\n');
text_array = scanned_fields{1};
fclose(fid);
雖然textscan
能夠一些基本分析的,這不是因爲我們正在做的事情足夠複雜。所以我們只是用它來讀取每一行作爲一個字符串:格式%s
意味着我們期待一個字符串,並且設置Delimiter
到\n
意味着字符串由換行符分隔。
在您發佈的樣本數據中,每個條目都是4行(名稱,原因,位置,日期),後跟空行。只要我們可以依賴這種格式,這提供了一種簡單的方法來分割數據(而不是我在我以前的答案中提出的regexp
解析)。
name_str_array = text_array(1:5:end);
cause_str_array = text_array(2:5:end);
loc_str_array = text_array(3:5:end);
date_str_array = text_arary(4:5:end);
因此,例如,name_strs
將是每一個5日線,從線#1。同樣,cause_strs
每隔5行,從#2行開始。請注意數據中沒有任何額外或缺失的行。
接下來我們將解析其中的每一個來獲取我們想要的信息。在我之前的回答中,我建議一次解析所有字符串,但我認爲如果我們一次只讀一個條目會更容易理解。例如,讓我們考慮第一個條目。
name_str = name_str_array{1};
loc_str = loc_str_array{1};
date_str = date_str_array{1};
讓我們先從最簡單的一個:解析日期。
date_format = 'Date of death:\s*(?<date>.*)';
parsed_fields = regexp(date_str, date_format, 'names');
DOD = parsed_fields.date;
我們正在尋找的格式字符串Date of death:
,後跟任意數量的空白字符(\s*
),然後是文本的塊(又名「令牌」),我們希望捕捉到:(?<date>.*)
圓括號表示這是我們希望捕獲的令牌,?<date>
表示我們希望將此令牌稱爲「日期」,並且.*
指定要查找哪些字符。 .
是通用通配符,即它匹配所有可能的字符。 *
表示我們對任何數量的重複都感興趣。所以本質上,這個.*
的意思是「匹配字符串中的所有剩餘字符」。
使用names
選項調用regexp
會導致它返回一個帶有指定標記的結構作爲其字段。
接下來,讓我們來做國家。這一個有點棘手,因爲有可變數量的城市/地區說明符。但這個國家永遠是最後一個國家,所以這是我們要抓的。
country_format = '(?<country>\w[ \w]*)$';
parsed_fields = regexp(loc_str, country_format, 'names');
Country = parsed_fields.country;
此格式規格是令牌(?<country>\w[ \w]*)
後跟字符串(由特殊字符$
表示)的端部。在令牌規範中,我們匹配一個字母數字字符(\w
),後跟任意數量的空格和/或字母數字字符([ \w]*
)。指定這種領先\w
的原因是,我們不匹配前一個逗號和國家名稱開頭之間的空格。
最後,我們來做一下這個年齡。這是一個棘手的問題,因爲不是每一個條目都有一個年齡。至少這很容易,因爲年齡(如果存在的話)是該行中唯一的數字數據。因此:
age_format = '(?<age>[\d]+)';
parsed_fields = regexp(name_str, age_format, 'names');
if isempty(parsed_fields)
Age = -1;
else
Age = str2double(parsed_fields.age);
end
格式說明僅僅是令牌(?<age>[\d]+)
,它指定我們尋找數字字符(\d
),和我們正在尋找一個或多個它們中的(+
)。
解析後,我們檢查是否有匹配。如果不是(parsed_fields
爲空),則我們將Age
賦值爲-1。否則,我們將解析後的年齡字段轉換爲數字。
所以把他們放在一起:
date_format = 'Date of death:\s*(?<date>.*)';
country_format = '(?<country>\w[ \w]*)[\W]?$';
age_format = '(?<age>[\d]+)';
nEntries = length(date_str_array);
DOD = cell(nEntries, 1);
Country = cell(nEntries, 1);
Age = zeros(nEntries, 1);
for ii = 1:nEntries
name_str = name_str_array{ii};
loc_str = loc_str_array{ii};
date_str = date_str_array{ii};
parsed_fields = regexp(date_str, date_format, 'names');
assert(~isempty(parsed_fields), 'Could not parse date from:\n%s', date_str);
DOD{ii} = parsed_fields.date;
parsed_fields = regexp(loc_str, country_format, 'names');
assert(~isempty(parsed_fields), 'Could not parse country from:\n%s', loc_str);
Country{ii} = parsed_fields.country;
parsed_fields = regexp(name_str, age_format, 'names');
if isempty(parsed_fields)
Age(ii) = -1;
else
Age(ii) = str2double(parsed_fields.age);
end
end
我加入了assert
語句來幫助調試發生了什麼事情,如果你在解析出現錯誤。
例如,您可能還會注意到我在國家/地區格式中添加了[\W]?
。這是因爲,在您的示例數據上運行它時,我遇到了一個國家,該國在該行末尾包含一段時間(即以「巴西」而不是「巴西」結尾)。因此,現在我們希望匹配一個非字母數字字符(\W
)重複零次或一次(?
),並且它在括號外,因此它不會被捕獲爲「國家」標記的一部分。
我需要學會提出更準確的問題。我更新了名稱列表以更好地顯示文件的外觀。由於我不瞭解如何格式化問題中的文本,因此無法準確顯示它看起來的樣子。 – Dgales4130 2014-11-25 19:31:41
這裏是實際數據[鏈接](https://docs.google.com/a/uic.edu/document/d/1AsCTRuCpJpQ2PRQp-SY7u5iXK9ayLOsuHzOcd5haWgw/edit?usp=sharing) – Dgales4130 2014-11-25 19:39:46
我看到了;格式有些不同,並不是每個條目都有相關的年齡。我將添加一個新的答案,該答案適用於您發佈的數據。 – KQS 2014-11-25 22:38:57