簡體   English   中英

從 strtok() 獲取零長度字符串

[英]Obtaining zero-length string from strtok()

我有一個 CSV 文件,其中包含以下數據

value;name;test;etc

我試圖通過使用strtok(string, ";")進行拆分。 但是,此文件可以包含零長度數據,如下所示:

value;;test;etc

strtok()跳過。 有沒有辦法可以避免strtok像這樣跳過零長度數據?

可能的替代方法是使用BSD函數strsep()而不是strtok() (如果可用)。 手冊頁

strsep()函數旨在替代strtok()函數。 雖然strtok()函數應該是出於可移植性的首選(它符合ISO / IEC 9899:1990(“ISO C90”)),但它無法處理空字段,即檢測由兩個相鄰分隔符字符分隔的字段,或者一次用於多個字符串。 strsep()函數首先出現在4.4BSD中。

一個簡單的例子(也從該手冊頁復制):

char *token, *string, *tofree;

tofree = string = strdup("value;;test;etc");
while ((token = strsep(&string, ";")) != NULL)
    printf("token=%s\n", token);

free(tofree);

輸出:

token=value
token=
token=test
token=etc

所以空字段處理正確。

當然,正如其他人已經說過的,這些簡單的tokenizer函數都沒有正確處理引號內的分隔符,所以如果這是一個問題,你應該使用一個合適的 CSV解析庫。

沒有辦法讓strtok()不這樣做。 手冊頁

解析后的字符串中的兩個或多個連續分隔符字節的序列被視為單個分隔符。 字符串開頭或結尾的分隔符字節將被忽略。 換句話說:strtok()返回的標記總是非空的字符串。

但是你可以做的是檢查令牌之前'\\0'字符的數量,因為strtok()'\\0'替換所有遇到的令牌。 這樣你就會知道有多少令牌被跳過了。 來源信息

令牌的這一端自動替換為空字符,並且該函數返回令牌的開頭。

並提供代碼示例以顯示我的意思。

char* aStr = ...;
char* ptr = NULL;

ptr = strtok (...);

char* back = ptr;
int count = -1;
do {
  back--;
  if (back <= aStr) break; // to protect against reads before aStr
  count++;
} while (*back = '\0');

(沒有ide或測試的書面,可能是一個無效的實現,但這個想法是站立的)。

不,你不能。 來自“man strtok”:

解析后的字符串中的兩個或多個連續分隔符字符的序列被視為單個分隔符。 字符串開頭或結尾的分隔符字符將被忽略。 換句話說:strtok()返回的標記總是非空的字符串。

如果您的數據包含引號內的分隔符或任何其他“轉義”,您也可能遇到問題。

我認為最好的解決方案是獲取CSV解析庫或編寫自己的解析函數。

從最近的經驗來看,似乎 strtok() 不一定用字符串字符的結尾替換所有定界符,而是用字符串結尾字符替換它找到的第一個定界符並跳過以下定界符但將它們留在原處。

這意味着在名義情況下(在分隔符之前沒有零長度字符串),在第一次調用 strtok() 之后對 strtok() 的每次調用都將返回一個指向在\0字符之后開始的字符串的指針。

在 strtok() 讀取分隔符之間的零長度字符串的情況下, strtok() 將返回一個指向字符串的指針,該字符串在未替換為\0的分隔符字符之后開始。

這是我找出 strtok() 是否在分隔符之間跳過零長度字符串的解決方案。

// Previous code is needed to point strtok to a string and start ingesting from it.
char * field_string = strtok(NULL, ',');
// Note that this can't be done after the first call to strtok for a given buffer, since the previous character would be outside of the string's memory space.
if (*(field_string-1) == '\0') {
    // no delimiters were skipped
} else {
    // one or more delimiters were skipped
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM