Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / C++ [игнор отключен] [закрыт для гостей] / Как распарсить строку например 12345;SEPARATOR=67890 на две 12345 и 67890 / 20 сообщений из 20, страница 1 из 1
07.11.2018, 05:55
    #39728978
Дмитрий77
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как распарсить строку например 12345;SEPARATOR=67890 на две 12345 и 67890
Появилась необходимость чуть подправить свои же вымученные когда-то C-коды.
В общем смысл в следующем: есть две строки, разделенные сепаратором, например.
1) "12345"
2) "67890"
Сепаратор задается/выбирается, например ";SEPARATOR=", или "#", или "@@@" (пользователем в настройках).
Его может и не быть вовсе, тогда вторая часть отсутствует.

В общем предположим он задан как ";SEPARATOR="
Т.е. исх. строка будет
12345;SEPARATOR=67890
нужно получить первую и вторую часть

В VB.Net выглядит так:
Код: vbnet
1.
2.
3.
4.
5.
6.
7.
      If (Strings.Len(.m_separator) > 0) AndAlso (InStr(strFull, (.m_separator) > 0) Then
        strPart1 = Split(strFull, .m_separator)(0)
        strPart2 = Split(strFull, .m_separator)(1)
      Else
        strPart1 = strFull
        strPart2 = ""
      End If



Но надо сделать это в C (?++).
Мучаюсь уже долго.
Код: plaintext
1.
  ROUTEDEBUG(( L"   RouteCustom (Device=%ld): pFaxRoute->CallerId       : %s", pFaxRoute->DeviceId, ValidString ( pFaxRoute->CallerId )));


В общем у меня здесь на входе pFaxRoute->CallerId (LPCWSTR)
Она имеет формат 12345;SEPARATOR=67890
Нужно получить два полагаю WCHAR
Вот нарыл swscanf непосильным трудом.
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
  //Test!!!
  WCHAR CallerId[MAX_PATH] = {0};
  WCHAR RoutingInfo[MAX_PATH] = {0};
  swscanf_s(pFaxRoute->CallerId, L"%[^];SEPARATOR=%[^]",CallerId, RoutingInfo);
  ROUTEDEBUG(( L"   RouteCustom (Device=%ld): CallerId (parsed)         : %s", pFaxRoute->DeviceId, ValidString ( CallerId )));
  ROUTEDEBUG(( L"   RouteCustom (Device=%ld): RoutingInfo (parsed)      : %s", pFaxRoute->DeviceId, ValidString ( RoutingInfo )));
  //Test!!!


Дебаг отдает пока
Код: plaintext
1.
2.
3.
11.07.2018@05:00:26.571:   RouteCustom (Device=65592): pFaxRoute->CallerId       : 12345;SEPARATOR=67890
11.07.2018@05:00:26.571:   RouteCustom (Device=65592): CallerId (parsed)         : 12345
11.07.2018@05:00:26.571:   RouteCustom (Device=65592): RoutingInfo (parsed)      : 


В RoutingInfo надо получить 67890 (пока пусто)

До кучи надо еще проверить, что в pFaxRoute->CallerId содержится мой "SEPARATOR", который в общем случае WCHAR (но это я получу), а не константа.

Поможете осилить? Чет совсем отупел, а сделать надо.

Со всякими std:: здесь не работаю, много вымучено на эту тему в связи с рантаймами и т.п.
Т.е. код использует ф-ции в духе StringCchCopy, StringCchCat, StringCchPrintf, ZeroMemory и т.п., сделан из SDK примера, компилируется с определенным рантаймом в VC++2005. Его еще надо не испортить с точки зрения запуска exe на разных OS.
...
Рейтинг: 0 / 0
07.11.2018, 08:53
    #39729009
Cerebrum
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как распарсить строку например 12345;SEPARATOR=67890 на две 12345 и 67890

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
#pragma once
#include "windows.h"
#include "tchar.h"
#include <assert.h>
#include <strsafe.h>
#include <vector>

using namespace std;

class StringSplitter
{
public:
	StringSplitter() = default;

	~StringSplitter()
	{
		Destroy();
	}

private:
	vector<WCHAR*>	m_vParts;

public:
	void Destroy(void)
	{
		while (m_vParts.size())
		{
			::free(m_vParts.back());
			m_vParts.pop_back();
		}
	}

	static size_t GetStringLen(LPCWSTR lpText)
	{
		return IsNullString(lpText) ? 0UL : ::wcslen(lpText);
	}

	static BOOL IsNullString(LPCWSTR lpCheckMe)
	{
		return lpCheckMe && lpCheckMe[0] == 0;
	}

	static int CompareString(LPCWSTR lpL, LPCWSTR lpR, DWORD dwFlags, int nLen)
	{
		if (lpL == lpR)
			return CSTR_EQUAL;

		return ::CompareStringEx(LOCALE_NAME_USER_DEFAULT, dwFlags, lpL, nLen, lpR, nLen, nullptr, NULL, 0L);
	}

	static LPWSTR CopyString(LPCWSTR lpStrToCopy, int nLen)
	{
		if (lpStrToCopy == NULL || nLen == 0)
			return NULL;
	
		size_t sz_len	= (nLen < 0) ? ::wcslen(lpStrToCopy) : nLen;
		size_t sz_mem	= (sz_len + 1UL) * sizeof(wchar_t);
		LPWSTR lpResult = static_cast<LPWSTR>(::malloc(sz_mem));
		if (lpResult == NULL)
			return NULL;

		::ZeroMemory(lpResult, sz_mem);
		HRESULT hRet = ::StringCbCopyNW(lpResult, sz_mem, lpStrToCopy, sz_mem - sizeof(wchar_t));
		if (SUCCEEDED(hRet))
			return lpResult;

		::free(lpResult);
		return NULL;
	}

	void PrintOut(void) const
	{
		for (const auto& it: m_vParts)
		{
			OutputDebugString(L"\t");
			OutputDebugString(it);
			OutputDebugString(L"\n");
		}
	}

	BOOL SplitToElements(LPCWSTR lpText, LPCWSTR lpBreaker, int nLen = -1, BOOL bCaseSensitive = FALSE)
	{
	//--------- разбивает строку на подстроки по указанной подстроке разделителю создавая собственные копии полученных подстрок
	// lpText ----------------------------------------------------------------------------- исходная строка, NULL - вернет TRUE
	// lpBreaker ------------------------------------------------------------------------- подстрока разделитель, NULL - ошибка
	// nLen ------------ длина строки lpText, символов, -1 - null-терминированная строка, расчет длины произвести автоматически
	// bCaseSensitive = TRUE -- поиск подстроки с учетом регистра, FALSE ------------------- поиск подстроки без учета регистра
	//*************************************************************************************************************************
		Destroy();

		if (IsNullString(lpText) || nLen == 0)
			return TRUE;

		if (IsNullString(lpBreaker))
			return FALSE;

		LPCWSTR pEntry	= lpText;
		LPCWSTR pEnd	= lpText + (nLen < 0 ? GetStringLen(lpText) : nLen);

		const size_t szBreakerLen = ::wcslen(lpBreaker);
		DWORD dwFlags = bCaseSensitive ? 0UL : NORM_IGNORECASE;
	
		while (pEntry && pEntry < pEnd)
		{
			LPCWSTR pBrk = pEntry;
			while (pBrk < pEnd && CompareString(pBrk, lpBreaker, dwFlags, (int)szBreakerLen) != CSTR_EQUAL)
				pBrk++;

			if (pBrk > pEntry && pBrk <= pEnd)
				m_vParts.push_back(CopyString(pEntry, (int)(pBrk - pEntry)));
			else if (pBrk == pEntry)
				m_vParts.push_back(NULL);

			pEntry = pBrk + szBreakerLen;
		}
		return TRUE;
	}
};



Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
#include "StringSplitter.h"

int APIENTRY _tWinMain(HINSTANCE, HINSTANCE, LPTSTR, int)
{
	StringSplitter spli;

	spli.SplitToElements(L"123=abc456=abc789=abc10", L"=abc");

	spli.PrintOut();

	return 0;
}
...
Рейтинг: 0 / 0
07.11.2018, 15:29
    #39729360
Дмитрий77
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как распарсить строку например 12345;SEPARATOR=67890 на две 12345 и 67890
Cerebrum,
прежде всего спасибо что откликнулись.

Меня что смущает. У Вас я так понимаю std, т.е. C++ (комментарий в конце моего первого поста).
А попроще никак нельзя? В духе C (без плюс), Microsoft SDK, классического msdn так сказать.
В SDK (6.1, 7.1) кстати примеров нигде быть не может? Сходу не нашел.

Попытаюсь как бы еще раз сформулировать задачу (в свете исходного кода):
1) код отдает мне исходную строку LPCWSTR CallerId
2) есть разделитель получаемый в коде как WCHAR sSeparator (который может быть пустой/нулевой длины)
3) надо получить WCHAR sPart1 и WCHAR sPart2 по следующему алгоритму:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
      If (разделитель не пустой/нулевой длины) AndAlso (CallerId содержит разделитель) Then
        WCHAR strPart1 = то что находится до разделителя
        WCHAR strPart2 = то что находится после разделителя
      Else
        WCHAR strPart1 = CallerId
        WCHAR strPart2 = "" (пустая)
      End If


разделитель известен или отсутствует(нулевой длины)
строка его содержит или не содержит, не более одного разделителя
регистр , там по определению точное соответствие будет, тот кто дописывает разделитель + то что после разделителя считывает тот же параметр в ini-файле что и текущая функция, через GetPrivateProfileString, с дефолтным значением "" (строка нулевой длины, не NULL, означает отсутствие разделителя и соотв. второй части)
Т.е. грубо говоря, у меня модем пишет в CallerID сам CallerID(NMBR=) и еще и DID (NDID= т.е. Called Number) через сепаратор,
а задача C -кода с которым я вожусь (моя dll -RoutingExtension) распарсить это дело обратно.
MS Fax Service написан в 90-х, там тупо нет поля DID, есть RoutingInfo, но через драйвер модема и майкрософтовский FSP в RoutingInfo DID не запихнуть (через свой FSP можно) отсюда пляски.

===
CallerId сейчас используется далее следующим образом:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
  WCHAR szCMD[2048] = { 0 };
...
  WCHAR Buffer[MAX_PATH*2] = {0};
  ULONG ulBufferSize = sizeof(Buffer);
  ULONG ulArraySize = ulBufferSize/sizeof(Buffer[0]);
...
   //CallerId
  ZeroMemory(Buffer,ulBufferSize);
  StringCchCopyN(Buffer,ulBufferSize, ValidString (CallerId ), ulArraySize - 1);
  StringCchCat(szCMD, 2048, L" /@@@CallerId=");
  StringCchCat(szCMD, 2048, Buffer);
...



Переделать надо примерно так:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
  WCHAR szCMD[2048] = { 0 };

  WCHAR Buffer[MAX_PATH*2] = {0};
  ULONG ulBufferSize = sizeof(Buffer);
  ULONG ulArraySize = ulBufferSize/sizeof(Buffer[0]);
...
   //strPart1
  ZeroMemory(Buffer,ulBufferSize);
  StringCchCopyN(Buffer,ulBufferSize, ValidString (strPart1 ), ulArraySize - 1);
  StringCchCat(szCMD, 2048, L" /@@@strPart1=");
  StringCchCat(szCMD, 2048, Buffer);
.   //strPart2
  ZeroMemory(Buffer,ulBufferSize);
  StringCchCopyN(Buffer,ulBufferSize, ValidString (strPart2 ), ulArraySize - 1);
  StringCchCat(szCMD, 2048, L" /@@@strPart2=");
  StringCchCat(szCMD, 2048, Buffer);
..



Могу конечно отдать CallerID вместе с DID нераспарсенным в VB-код (кой код собственно и делает всю логические действа) как есть, но так некрасиво.
...
Рейтинг: 0 / 0
07.11.2018, 16:29
    #39729409
Cerebrum
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как распарсить строку например 12345;SEPARATOR=67890 на две 12345 и 67890
Дмитрий77Меня что смущает. У Вас я так понимаю std, т.е. C++ (комментарий в конце моего первого поста).
wcslen, swscanf_s - это тоже std
Дмитрий77А попроще никак нельзя? В духе C (без плюс), Microsoft SDK, классического msdn так сказать.
Я тебе показал как сделать , а заменить std-функции можно на свои собственные аналоги
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
LPCWSTR FindChar(LPCWSTR lpString, WCHAR wChar)
{
	if (lpString == NULL)
		return NULL;

	while (*lpString && *lpString != wChar)
		++lpString;

	return *lpString ? lpString : NULL;
}

size_t GetStringLen(LPCWSTR lpText)
{
	if (lpText == NULL)
		return 0UL;

	size_t szLen = 0UL;
	while (*lpText++) {szLen++;}
	return szLen;
}


Не понимаю в чем проблема с std?
Если не хочется повсюду таскать vcruntime, то просто собери статическую сборку, получишь 1 красивый exe-шник, который будет работать везде, начиная с Windows XP и заканчивая Windows Server 2019
...
Рейтинг: 0 / 0
07.11.2018, 16:34
    #39729413
Cerebrum
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как распарсить строку например 12345;SEPARATOR=67890 на две 12345 и 67890
а Std::vector, который я использовал для сбора подстрок, можешь вообще выкинуть, написать вместо SplitToElements функцию в духе ForEach с callback'ом, в который пулять каждый найденный сегмент для анализа и обработки и тут же от него избавляться, а не накапливать. Или написать свой собственный класс динамического масссива, что тоже не особо трудно.

В моем коде все сделано для простоты восприятия человека забывшего про тонкости языка, как раз в духе Platform SDK.
...
Рейтинг: 0 / 0
07.11.2018, 17:00
    #39729443
Дмитрий77
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как распарсить строку например 12345;SEPARATOR=67890 на две 12345 и 67890
CerebrumЕсли не хочется повсюду таскать vcruntime, то просто собери статическую сборку, получишь 1 красивый exe-шник, который будет работать везде, начиная с Windows XP и заканчивая Windows Server 2019
Вот здесь когда-то обсуждали:
Проект dll(надо и 32 и 64 бит версии) в VS2013 Ultimate C++ -чтоб от XP до Win10
И не все там просто было особенно с "с Windows XP" на статрантаймах, в частности из-за std.
И в итоге забил на эти 13-е студии и собрал проверенным образом:
Microsoft Visual Studio 2005
Version 8.0.50727.762 (SP.050727-7600)
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type="win32" name="Microsoft.VC80.CRT" version="8.0.50727.762" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
    </dependentAssembly>
  </dependency>
</assembly>


Код: plaintext
1.
2.
3.
4.
5.
6.
7.
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type="win32" name="Microsoft.VC80.CRT" version="8.0.50727.762" processorArchitecture="amd64" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
    </dependentAssembly>
  </dependency>
</assembly>


что ИМХО есть надежно по определению, поэтому и C++ с std там не должно быть проблем с точки зрения "от и до" (проверено многолетним опытом).
Но по "религиозным соображениям" таки не хочется.

Ладно, спасибо, посмотрю вечером, м.б. чего-нибудь рожу рабочее.
Мне даже "функций" и "классов" никаких не надо (путают они меня), а просто пару абзацев реально рабочего кода который делает мне эти две WCHAR из анализа исходного LPCWSTR и сераратора WCHAR (в 4-х местах скопирую, не проблема).
То что легких путей нет, это уже дошло.
...
Рейтинг: 0 / 0
07.11.2018, 22:41
    #39729609
Siemargl
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как распарсить строку например 12345;SEPARATOR=67890 на две 12345 и 67890
Дмитрий77,

strspn / strcspn
...
Рейтинг: 0 / 0
07.11.2018, 23:24
    #39729636
Дмитрий77
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как распарсить строку например 12345;SEPARATOR=67890 на две 12345 и 67890
Siemarglstrspn / strcspn
Но оно
выполняет поиск символов строки string2 в строке string1
а мне надо поиск строки string2 в строке string1,
и потом обрезать концы справа и слева.

И честно, я задолбался. И так каждый раз, когда мне надо сделать извините какую-то мелкую ерунду, но в C.
swscanf вообще ни фига не работает
Код: plaintext
1.
swscanf(sFullString, L"%s;Separator=%s", sPart1, sPart2);


Казалось бы чем не шаблон, так нет, ей какие-то пробелы нужны или \n, а так ложит все в sPart1.
Ваш код, с уважением, но сложен он как-то для меня.
Хочется простого
Код: vbnet
1.
2.
s1=split(sFullString, sSeparator)(0)
s2=split(sFullString, sSeparator)(1)
...
Рейтинг: 0 / 0
08.11.2018, 02:32
    #39729680
Дмитрий77
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как распарсить строку например 12345;SEPARATOR=67890 на две 12345 и 67890
У меня только так заработало (с std::wstring), ну в таком духе я и раньше не раз делал:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
 #include <sstream>
...
    //!!!Test
    LPCWSTR sFull =L"1234567Separator8903987654321";
    WCHAR sSep[512] = { 0 };
    StringCchCopy(sSep, 512, L"Separator");

    std::wstring stdFull;
    std::wstringstream to_stdFull;
    to_stdFull << sFull;
    stdFull = to_stdFull.str();

    std::wstring stdSep;
    std::wstringstream to_stdSep;
    to_stdSep << sSep;
    stdSep = to_stdSep.str();

    ROUTEDEBUG (( L"   stdFull = %s\n", stdFull.c_str() ));
    ROUTEDEBUG (( L"   stdSep = %s\n", stdSep.c_str() ));

    std::wstring std1;
    std::wstring std2;
    if ((stdSep.length() != 0) && (stdFull.find(stdSep) != std::wstring::npos)){
        std2 = stdFull.substr(stdFull.find(stdSep) + stdSep.length());
        std1 = stdFull.substr(0, stdFull.length() - std2.length() - stdSep.length());
    } else {
      std1 = stdFull;
      std2.clear();
    }

    ROUTEDEBUG (( L"   std1 = %s\n", std1.c_str() ));
    ROUTEDEBUG (( L"   std2 = %s\n", std2.c_str() ));
  //!!!Test


Код: plaintext
1.
2.
3.
4.
11.08.2018@02:15:20.578:   stdFull = 1234567Separator8903987654321
11.08.2018@02:15:20.578:   stdSep = Separator
11.08.2018@02:15:20.578:   std1 = 1234567
11.08.2018@02:15:20.578:   std2 = 8903987654321


На XP и Win2019 оно конечно работать будет, ибо в правильной студии и рантайме сделано (проверено давно), но конечно не фонтан, да еще с довеском ругани
Код: plaintext
1.
2.
3.
4.
5.
1>C:\Program Files (x86)\Microsoft Visual Studio 8\VC\include\cstdio(33) : warning C4995: 'gets': name was marked as #pragma deprecated
1>C:\Program Files (x86)\Microsoft Visual Studio 8\VC\include\cstdio(37) : warning C4995: 'sprintf': name was marked as #pragma deprecated
1>C:\Program Files (x86)\Microsoft Visual Studio 8\VC\include\cstdio(40) : warning C4995: 'vsprintf': name was marked as #pragma deprecated
1>C:\Program Files (x86)\Microsoft Visual Studio 8\VC\include\cstring(20) : warning C4995: 'strcat': name was marked as #pragma deprecated
...


которая правда тоже ни на что не влияет.

А хотелось культурно, увы.
Ладно, пошел писать Г-простыни, тестировать и забывать, надеюсь надолго.
...
Рейтинг: 0 / 0
08.11.2018, 05:18
    #39729696
CEMb
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как распарсить строку например 12345;SEPARATOR=67890 на две 12345 и 67890
Дмитрий77Как распарсить строку
strtok - нарезает куски строки согласно набору разделителей.
там есть момент: функция работает непосредственно с исходной строкой и на каждый вызов вставляет нули вместо разделителей в строку(потом убирает), таким образом мы получаем токены.
Cerebrumwcslen, swscanf_s - это тоже stdэто стандартная библиотека, но не std(stl)
...
Рейтинг: 0 / 0
08.11.2018, 09:03
    #39729750
Cerebrum
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как распарсить строку например 12345;SEPARATOR=67890 на две 12345 и 67890
Дмитрий77, не нужно приходить сюда ныть, у тебя уже все есть, чтобы сделать что ты хочешь, но ты просто САМ не хочешь ничего делать

без классов, чистый WinAPI в стиле С
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
#pragma once
#include "windows.h"
#include "strsafe.h"

typedef BOOL (CALLBACK* PFNFOREACHTOKEN)(LPWSTR, LPVOID);

size_t GetStringLen(LPCWSTR lpText);
BOOL IsNullString(LPCWSTR lpCheckMe);
LPWSTR CopyString(LPCWSTR lpStrToCopy, int nLen);
int CompareString(LPCWSTR lpL, LPCWSTR lpR, DWORD dwFlags, int nLen);
BOOL ForEachToken(PFNFOREACHTOKEN pfnForEach, LPCTSTR lpText, LPCWSTR lpBreaker, int nLen = -1, BOOL bCaseSensitive = FALSE, LPVOID pvContext = NULL);

BOOL CALLBACK PrintOut(LPWSTR lpToken, LPVOID pvContext)
{
	OutputDebugString(L"\t");
	OutputDebugString(lpToken);
	OutputDebugString(L"\n");
	return TRUE;
}

int APIENTRY wWinMain(HINSTANCE, HINSTANCE, LPTSTR, int)
{
	ForEachToken(PrintOut, L"123=abc456=abc789=abc10", L"=abc");
	return 0;
}

size_t GetStringLen(LPCWSTR lpText)
{
	return IsNullString(lpText) ? 0UL : ::wcslen(lpText);
}

BOOL IsNullString(LPCWSTR lpCheckMe)
{
	return lpCheckMe && lpCheckMe[0] == 0;
}

int CompareString(LPCWSTR lpL, LPCWSTR lpR, DWORD dwFlags, int nLen)
{
	if (lpL == lpR)
		return CSTR_EQUAL;

	return ::CompareStringEx(LOCALE_NAME_USER_DEFAULT, dwFlags, lpL, nLen, lpR, nLen, nullptr, NULL, 0L);
}

LPWSTR CopyString(LPCWSTR lpStrToCopy, int nLen)
{
	if (lpStrToCopy == NULL || nLen == 0)
		return NULL;

	size_t sz_len	= (nLen < 0) ? ::wcslen(lpStrToCopy) : nLen;
	size_t sz_mem	= (sz_len + 1UL) * sizeof(wchar_t);
	LPWSTR lpResult = static_cast<LPWSTR>(::malloc(sz_mem));
	if (lpResult == NULL)
		return NULL;

	::ZeroMemory(lpResult, sz_mem);
	HRESULT hRet = ::StringCbCopyNW(lpResult, sz_mem, lpStrToCopy, sz_mem - sizeof(wchar_t));
	if (SUCCEEDED(hRet))
		return lpResult;

	::free(lpResult);
	return NULL;
}

BOOL ForEachToken(PFNFOREACHTOKEN pfnForEach, LPCTSTR lpText, LPCWSTR lpBreaker, int nLen, BOOL bCaseSensitive, LPVOID pvContext)
{
//--------- разбивает строку на подстроки по указанной подстроке разделителю создавая собственные копии полученных подстрок
// pfnForEach ---------------------------------- CALLBACK функция вызываемая для каждой обнаруженной лексемы, NULL - ошибка
// lpText ----------------------------------------------------------------------------- исходная строка, NULL - вернет TRUE
// lpBreaker ------------------------------------------------------------------------- подстрока разделитель, NULL - ошибка
// nLen ------------ длина строки lpText, символов, -1 - null-терминированная строка, расчет длины произвести автоматически
// pvContext ------------------------------------------------------------ пользовательские данные передаваемые в pfnForEach
// bCaseSensitive = TRUE -- поиск подстроки с учетом регистра, FALSE ------------------- поиск подстроки без учета регистра
//*************************************************************************************************************************
	if (pfnForEach == NULL)					return FALSE;
	if (IsNullString(lpText) || nLen == 0)			return TRUE;
	if (IsNullString(lpBreaker))				return FALSE;

	LPCWSTR pEntry	= lpText;
	LPCWSTR pEnd	= lpText + (nLen < 0 ? GetStringLen(lpText) : nLen);

	const size_t szBreakerLen = ::wcslen(lpBreaker);
	DWORD dwFlags = bCaseSensitive ? 0UL : NORM_IGNORECASE;

	BOOL bGoOn = TRUE;

	while (bGoOn && pEntry && pEntry < pEnd)
	{
		LPCWSTR pBrk = pEntry;
		while (pBrk < pEnd && CompareString(pBrk, lpBreaker, dwFlags, (int)szBreakerLen) != CSTR_EQUAL)
			pBrk++;

		if (pBrk > pEntry && pBrk <= pEnd)
		{
			LPWSTR lpToken = CopyString(pEntry, (int)(pBrk - pEntry));
			bGoOn = pfnForEach(lpToken, pvContext);
			::free(lpToken);
		}
		else if (pBrk == pEntry)
		{
			LPWSTR lpToken = ::_wcsdup(L"");
			bGoOn = pfnForEach(lpToken, pvContext);
			::free(lpToken);
		}
		pEntry = pBrk + szBreakerLen;
	}
	return TRUE;
}

...
Рейтинг: 0 / 0
08.11.2018, 16:12
    #39730018
Дмитрий77
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как распарсить строку например 12345;SEPARATOR=67890 на две 12345 и 67890
Cerebrum,

Ну, ныть не ныть а "надо дела делать".
Глядючи на ваш код, ну не до конца его понимаю.
Вот хотя бы так как здесь ,
в смысле линейно но без конвертации в std::string сделать то же самое можно?
Т.е. грубо
s1=
s2=
или
func (...,s1,s2)
Callback - это сильная путаница кода уже.
В принципе я уже сделал с std::string и вставил в продакшн, оно работает и сойдет ("дела делает", не глючит).
Почему мне хотелось без std::string я написал выше и целый пост был разведен однажды, ссылку на кот. дал.
У меня есть две моих dll: FSP и RoutingExtension.
FSP была с кучей std::string, переделывать тогда не стал.
RoutingExtension до сего момента была "по стандарту MS", без std::string, счас вот "изгадил". Ну да бог с ним, все одно они в паре идут, первую все одно переделывать не буду, будучи откомпилированной 8.0.50727.762 (SP.050727-7600), полет таких штук много лет нормальный включая std::string, а вот в VS2013 там извините задница с "мультисистемностью", хоть static, хоть dynamic. Но на 2013+ я с C забил.
...
Рейтинг: 0 / 0
08.11.2018, 18:01
    #39730090
Cerebrum
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как распарсить строку например 12345;SEPARATOR=67890 на две 12345 и 67890
Дмитрий77,

это все отмазки, тем кому надо, тот берет и делает
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
#pragma once
#include "windows.h"
#include "strsafe.h"

typedef struct _FOREACHCONTEXT
{
	size_t t_count;
	WCHAR* tokens[10];
} FOREACHCONTEXT, *PFOREACHCONTEXT;

typedef BOOL (CALLBACK* PFNFOREACHTOKEN)(LPWSTR, LPVOID);

size_t GetStringLen(LPCWSTR lpText);
BOOL IsNullString(LPCWSTR lpCheckMe);
LPWSTR CopyString(LPCWSTR lpStrToCopy, int nLen = -1);
int CompareString(LPCWSTR lpL, LPCWSTR lpR, DWORD dwFlags, int nLen);
BOOL ForEachToken(PFNFOREACHTOKEN pfnForEach, LPCTSTR lpText, LPCWSTR lpBreaker, int nLen = -1, BOOL bCaseSensitive = FALSE, LPVOID pvContext = NULL);

BOOL CALLBACK PrintOut(LPWSTR lpToken, LPVOID pvContext)
{
	PFOREACHCONTEXT pCtx = static_cast<PFOREACHCONTEXT>(pvContext);
	pCtx->tokens[pCtx->t_count] = CopyString(lpToken);
	pCtx->t_count++;
	return TRUE;
}

size_t GetStringLen(LPCWSTR lpText)
{
	return IsNullString(lpText) ? 0UL : ::wcslen(lpText);
}

BOOL IsNullString(LPCWSTR lpCheckMe)
{
	return (lpCheckMe == NULL || lpCheckMe[0] == 0);
}

int CompareString(LPCWSTR lpL, LPCWSTR lpR, DWORD dwFlags, int nLen)
{
	if (lpL == lpR)
		return CSTR_EQUAL;

	return ::CompareStringEx(LOCALE_NAME_USER_DEFAULT, dwFlags, lpL, nLen, lpR, nLen, nullptr, NULL, 0L);
}

LPWSTR CopyString(LPCWSTR lpStrToCopy, int nLen)
{
	if (lpStrToCopy == NULL || nLen == 0)
		return NULL;

	size_t sz_len	= (nLen < 0) ? ::wcslen(lpStrToCopy) : nLen;
	size_t sz_mem	= (sz_len + 1UL) * sizeof(wchar_t);
	LPWSTR lpResult = static_cast<LPWSTR>(::malloc(sz_mem));
	if (lpResult == NULL)
		return NULL;

	::ZeroMemory(lpResult, sz_mem);
	HRESULT hRet = ::StringCbCopyNW(lpResult, sz_mem, lpStrToCopy, sz_mem - sizeof(wchar_t));
	if (SUCCEEDED(hRet))
		return lpResult;

	::free(lpResult);
	return NULL;
}

BOOL ForEachToken(PFNFOREACHTOKEN pfnForEach, LPCTSTR lpText, LPCWSTR lpBreaker, int nLen, BOOL bCaseSensitive, LPVOID pvContext)
{
//--------- разбивает строку на подстроки по указанной подстроке разделителю создавая собственные копии полученных подстрок
// pfnForEach ---------------------------------- CALLBACK функция вызываемая для каждой обнаруженной лексемы, NULL - ошибка
// lpText ----------------------------------------------------------------------------- исходная строка, NULL - вернет TRUE
// lpBreaker ------------------------------------------------------------------------- подстрока разделитель, NULL - ошибка
// nLen ------------ длина строки lpText, символов, -1 - null-терминированная строка, расчет длины произвести автоматически
// pvContext ------------------------------------------------------------ пользовательские данные передаваемые в pfnForEach
// bCaseSensitive = TRUE -- поиск подстроки с учетом регистра, FALSE ------------------- поиск подстроки без учета регистра
//*************************************************************************************************************************
	if (pfnForEach == NULL)						return FALSE;
	if (IsNullString(lpText) || nLen == 0)		return TRUE;
	if (IsNullString(lpBreaker))				return FALSE;

	LPCWSTR pEntry	= lpText;
	LPCWSTR pEnd	= lpText + (nLen < 0 ? GetStringLen(lpText) : nLen);

	const size_t szBreakerLen = ::wcslen(lpBreaker);
	DWORD dwFlags = bCaseSensitive ? 0UL : NORM_IGNORECASE;

	BOOL bGoOn = TRUE;

	while (bGoOn && pEntry && pEntry < pEnd)
	{
		LPCWSTR pBrk = pEntry;
		while (pBrk < pEnd && CompareString(pBrk, lpBreaker, dwFlags, (int)szBreakerLen) != CSTR_EQUAL)
			pBrk++;

		if (pBrk > pEntry && pBrk <= pEnd)
		{
			LPWSTR lpToken = CopyString(pEntry, (int)(pBrk - pEntry));
			bGoOn = pfnForEach(lpToken, pvContext);
			::free(lpToken);
		}
		else if (pBrk == pEntry)
		{
			LPWSTR lpToken = ::_wcsdup(L"");
			bGoOn = pfnForEach(lpToken, pvContext);
			::free(lpToken);
		}
		pEntry = pBrk + szBreakerLen;
	}
	return TRUE;
}


Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
int APIENTRY wWinMain(HINSTANCE, HINSTANCE, LPTSTR, int)
{
	FOREACHCONTEXT ctx = {0};

	ForEachToken(PrintOut, L"1234567Separator8903987654321", L"Separator", -1, FALSE, &ctx);

	for (size_t i = 0UL; i < ctx.t_count; i++)
	{
		LPWSTR lpToken = ctx.tokens[i];
		OutputDebugString(L"\t");
		OutputDebugString(lpToken);
		OutputDebugString(L"\n");
		::free(lpToken);
	}
	return 0;
}


output 1234567
8903987654321
без классов написать такой код, который хочешь ты нереально, но тебя они путают...
...
Рейтинг: 0 / 0
08.11.2018, 21:29
    #39730137
Дмитрий77
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как распарсить строку например 12345;SEPARATOR=67890 на две 12345 и 67890
Cerebrum,
вот это вроде красиво выглядит, попробую разобраться. М.б. даже и заменю сразу пока "горячо".
>это все отмазки
Ну я и сам всегда говорил что я пожизненно тупой(C) что касается C+/-.
У меня с WIN API рука очень даже набита, но все делаю в VB.Net (ранее в VB6).
Я отпишусь в теч. нескольких дней.
...
Рейтинг: 0 / 0
09.11.2018, 06:46
    #39730262
Дмитрий77
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как распарсить строку например 12345;SEPARATOR=67890 на две 12345 и 67890
Cerebrum,
Потестировал, склоняюсь к вашему варианту. Я бы сам такой код не осилил. Максимум м.б. содрал бы откуда-то, если б хватило ума нагуглить (на момент написания топика не хватило).
Спасибо.

Позволил себе одну замену, надеюсь не критично
Код: plaintext
1.
2.
	//return ::CompareStringEx(LOCALE_NAME_USER_DEFAULT, dwFlags, lpL, nLen, lpR, nLen, nullptr, NULL, 0L);
	return ::CompareString(LOCALE_USER_DEFAULT, dwFlags, lpL, nLen, lpR, nLen);


CompareStringEx function
Minimum supported client Windows Vista [desktop apps | UWP apps]
Minimum supported server Windows Server 2008 [desktop apps | UWP apps]

Я от поддержки XP формально не отказывался, и не ловит ее VS2005 + SDK 6.1
И поэтому
CompareStringW function
Minimum supported client Windows 2000 Professional [desktop apps only]
Minimum supported server Windows 2000 Server [desktop apps only]


Тестовый код:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
  //!!!Test
  LPCWSTR sFull =L"1234567Separator8903987654321";
  WCHAR sSep[512] = { 0 };
  StringCchCopy(sSep, 512, L"Separator");
  //StringCchCopy(sSep, 512, L"");
  ROUTEDEBUG (( L"   sFull = %s\n", ValidString ( sFull ) ));
  ROUTEDEBUG (( L"   sSep = %s\n", sSep ));

  WCHAR s1[512] = { 0 };
  WCHAR s2[512] = { 0 };
  if (wcslen(sSep) == 0) {
    StringCchCopy(s1, 512, sFull);
  } else {
    FOREACHCONTEXT ctx = {0};
	  ForEachToken(PrintOut, sFull, sSep, -1, FALSE, &ctx);
    if (ctx.tokens[0] != NULL) StringCchCopy(s1, 512, ctx.tokens[0]);
    if (ctx.tokens[1] != NULL) StringCchCopy(s2, 512, ctx.tokens[1]);
  }
  ROUTEDEBUG (( L"   s1 = %s\n", s1 ));
  ROUTEDEBUG (( L"   s2 = %s\n", s2 ));
  //!!!Test


Ну вроде как успешно. Пойдет? Ничего не упустил.

У меня могут быть такие случаи:
1) сепаратор задан и есть обе части им разделенные, заполнить s1 и s2 соответственно
2) сепаратор вообще не задан (L""), тогда копирование sFull->s1 целиком, s2 пустая
3) сепаратор задан, но отсутствует в sFull, тогда тоже копирование sFull->s1 целиком, s2 пустая

Значения NULL в s1 и s2 нежелательны, надо L"".
Пустой sSep в реальном коде всегда считывается как L"" (вроде как)
Код: plaintext
1.
2.
3.
  WCHAR  szDidSeparator[512] = { 0 };
...
  GetPrivateProfileString(L"Options", L"did-separator", L"", szDidSeparator, 512, szIniFilename);


Насчет sFull, это pFaxRoute->CallerId, отдаваемый MS Fax, на всякий случай проверил с NULL тоже, черт его знает что он туда кладет если номер не определен (NMBR=<пусто>, если смотреть в HyperTerminal в модеме).

Если вдруг что-то увидели в тест-коде непотребное, скажите please.
Особенно переживаю за NULL={null}=пусто=Nothing и L""='\0'=строка нулевой.
Стараюсь за этим следить, но иногда что-то упускаю и вдруг стреляет.

Планирую таки воспользоваться этим вариантом и убрать все std::string,
но это уже видимо на след. неделе.

Спасибо.
...
Рейтинг: 0 / 0
09.11.2018, 09:09
    #39730298
Cerebrum
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как распарсить строку например 12345;SEPARATOR=67890 на две 12345 и 67890
Дмитрий77Ну вроде как успешно. Пойдет? Ничего не упустил.
В PrintOut изменить последнюю строчку на
Код: plaintext
1.
2.
3.
4.
{
   // здесь как раньше...
   return (pCtx->t_count < _countof(pCtx->tokens));
}


и помнить, что максимум в структуре FOREACHCONTEXT памяти выделено на 10 лексем, если будет 11 - будет беда, поэтому, чтобы избежать перелета, контролируем CALLBACK. Даже если в строке никогда не будет больше 2х, дополнительная подстраховка от ошибок разработчика не помешает
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
//!!!Test
  LPCWSTR sFull =L"1234567Separator8903987654321";
  WCHAR sSep[512] = { 0 };
  StringCchCopy(sSep, 512, L"Separator");
  //StringCchCopy(sSep, 512, L"");
  ROUTEDEBUG (( L"\tsFull = %s\n", ValidString ( sFull ) ));
  ROUTEDEBUG (( L"\tsSep = %s\n", sSep ));

  WCHAR s1[512] = { 0 };
  WCHAR s2[512] = { 0 };

FOREACHCONTEXT ctx = {0};

// если учет регистра не нужен FALSE меняем на TRUE - снижение затрат на обработку
if (ForEachToken(PrintOut, sFull, sSep, -1, FALSE, &ctx))
{
        if (ctx.t_count > 1UL)
        {
                if (ctx.tokens[0] != NULL) StringCchCopy(s1, 512, ctx.tokens[0]);
                if (ctx.tokens[1] != NULL) StringCchCopy(s2, 512, ctx.tokens[1]);
        }
        else if (ctx.t_count == 1UL) // сепаратор не найден/пустой
        {
                StringCchCopy(s1, 512, sFull);
        }
        else
        {
                 // маловероятно, но хз... что там в продакшн версии
        } 

 	for (size_t i = 0UL; i < ctx.t_count; i++)
	     	::free(ctx.tokens[i]); // не забываем чистить память после использования
}
else
{
       // что-то пошло не так, что будем делать ?
}
  ROUTEDEBUG (( L"\ts1 = %s\n", s1 ));
  ROUTEDEBUG (( L"\ts2 = %s\n", s2 ));
  //!!!Test
...
Рейтинг: 0 / 0
09.11.2018, 11:09
    #39730383
Siemargl
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как распарсить строку например 12345;SEPARATOR=67890 на две 12345 и 67890
Какие то проблемы на ровном месте.

Если не извращаться с со словами-сепараторами, то 1 (одна) строка со strtok

Если даже извращаться - простейший цикл со strstr
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
#include <stdio.h>
#include <string.h>

int main ()
{
  char *str = "12345;SEPARATOR=67890;SEPARATOR=1111";
  char key[] = ";SEPARATOR=";
  char * pch;
  printf ("Original string '%s'\nParsed to : ",str);
  while (pch = strstr(str, key))
  {
  	char tok[200];
  	strncpy(tok, str, pch - str);
  	tok[pch - str] = '\0';
    printf ("%s " , tok);
    str = pch + strlen(key);
  }
  printf ("%s\n", str);  // last tail
  return 0;
}

...
Рейтинг: 0 / 0
09.11.2018, 15:38
    #39730686
Дмитрий77
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как распарсить строку например 12345;SEPARATOR=67890 на две 12345 и 67890
Cerebrum,
ваш код не отсекает случая когда сепаратор отсутствует =L"" и надо вернуть sFull ->s1, поэтому его надо разобрать сразу.
Также лучше сразу отсечь sFull == NULL. (sFull == "" обработается по Else if (ctx.t_count == 1UL)).
Тогда до "маловероятно" и "что-то пошло не так, что будем делать ?" дело вряд ли дойдет, в этих обоих случаях возвращаем L"" в s1 и s2.
Плюс кухню с ForEachToken лучше пихать под условие, у меня в коде ф-ции присутствуют goto Exit;/Exit: , компилятору это не нравится, если объявлять FOREACHCONTEXT ctx = {0}; безусловно.

Я думаю так сойдет (проверил все случаи):
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
  //!!!Test
  LPCWSTR sFull = L"1234567Separator8903987654321";
  //LPCWSTR sFull = NULL; //L""
  WCHAR sSep[512] = { 0 };
  StringCchCopy(sSep, 512, L"Separator");
  //StringCchCopy(sSep, 512, L"");
  ROUTEDEBUG (( L"   sFull = %s\n", ValidString ( sFull ) ));
  ROUTEDEBUG (( L"   sSep = %s\n", sSep ));

  WCHAR s1[512] = { 0 };
  WCHAR s2[512] = { 0 };
  if (sFull == NULL ) { // NULL в исх. строке
    ; // возвращаем пустые s1 и s2
  } else if (wcslen(sSep) == 0) { // сепаратор пустой
    StringCchCopy(s1, 512, sFull);
  } else {
    FOREACHCONTEXT ctx = {0};
    if (ForEachToken(PrintOut, sFull, sSep, -1, TRUE, &ctx)) {
      if (ctx.t_count > 1UL) {
        if (ctx.tokens[0] != NULL) StringCchCopy(s1, 512, ctx.tokens[0]);
        if (ctx.tokens[1] != NULL) StringCchCopy(s2, 512, ctx.tokens[1]);
      } else if (ctx.t_count == 1UL) { // сепаратор не найден
        StringCchCopy(s1, 512, sFull);
      } else { // маловероятно, но хз... что там в продакшн версии
        ; // возвращаем пустые s1 и s2
      }
 	    for (size_t i = 0UL; i < ctx.t_count; i++)
        ::free(ctx.tokens[i]); // не забываем чистить память после использования
    } else {
      ; // что-то пошло не так, что будем делать ? возвращаем пустые s1 и s2
    }
  }
  ROUTEDEBUG (( L"   s1 = %s\n", s1 ));
  ROUTEDEBUG (( L"   s2 = %s\n", s2 ));
  //!!!Test



Cerebrum
Код: plaintext
1.
2.
3.
4.
{
   // здесь как раньше...
   return (pCtx->t_count < _countof(pCtx->tokens));
}



и помнить, что максимум в структуре FOREACHCONTEXT памяти выделено на 10 лексем, если будет 11 - будет беда, поэтому, чтобы избежать перелета, контролируем CALLBACK. Даже если в строке никогда не будет больше 2х, дополнительная подстраховка от ошибок разработчика не помешает
Здесь согласен. Никто не мешает юзеру в качестве разделителя задать "0", тогда 10 нулей из CallerID и CalledNumber легко может превыситься, желаемого результата естественно не будет, но краша ясно дело тоже быть не должно. Ну, юзер тоже головой иногда думать должен, всех защит от дураков не сделаешь. Задавать разделитель жестко не хочу, в тек. версии он уже НЕ жестко и есть эстетический момент: этот разделитель отображается в частности в Fax and Scan в поле CallerID, т.е.
'74951234567;DID=79039876543' -относительно красиво
'74951234567separator79039876543' -относительно фигово смотрится
Так что юзер сам на свой вкус решает, чем разделять.
...
Рейтинг: 0 / 0
09.11.2018, 17:31
    #39730789
Cerebrum
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как распарсить строку например 12345;SEPARATOR=67890 на две 12345 и 67890
Дмитрий77ваш код не отсекает случая когда сепаратор отсутствует =L""
действительно, видимо я проверил с сепаратором, которого нет в анализируемой строке и в ответ получил всю строку целиком, но почему-то посчитал, что проверил на пустой сепаратор

Тогда я н не понимаю, зачем проверять наличие пустой строки считая в ней символы?
Для проверки есть IsNullString, которая как раз и делает то, что требуется без лишнего оверхэда

Код: plaintext
1.
2.
3.
  if (sFull == NULL ) { // NULL в исх. строке
    ; // возвращаем пустые s1 и s2
  } else if (IsNullString(sSep)) { // сепаратор пустой


но поскольку sSep создается на стеке, то вообще можно сократить до
Код: plaintext
1.
if (sSep[0] == 0)


и так и так и по вашему тоже будет работать, но правильный код должен писаться так, чтобы программисту не требовалось его комментировать. Чтобы намерения были ясны.

Когда в коде встречается
Код: plaintext
1.
if (IsNullString())

вместо
Код: plaintext
1.
if (wsclen() == 0)

то сразу становится очевидно что хотел разработчик и никаких дополнительных фантазий (не говоря о лишних трудозатратах CPU на посчитанную длину, которая по сути никому не нужна).
...
Рейтинг: 0 / 0
12.11.2018, 16:16
    #39731847
Дмитрий77
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как распарсить строку например 12345;SEPARATOR=67890 на две 12345 и 67890
CerebrumДля проверки есть IsNullString, которая как раз и делает то, что требуется без лишнего оверхэда

Код: plaintext
1.
2.
3.
  if (sFull == NULL ) { // NULL в исх. строке
    ; // возвращаем пустые s1 и s2
  } else if (IsNullString(sSep)) { // сепаратор пустой



Ну тогда уж пусть будет
Код: plaintext
1.
2.
3.
4.
5.
6.
  if (IsNullString(sFull)) { // NULL или пустая исх. строка; было(sFull == NULL )
    ; // возвращаем пустые s1 и s2
  } else if (IsNullString(sSep)) { // сепаратор пустой; было (wcslen(sSep) == 0)
    StringCchCopy(s1, 512, sFull);
  } else {
    FOREACHCONTEXT ctx = {0};


Просто у меня издержка в первом варианте, если sFull не NULL а L"" (очень даже может быть), то пустые s1 и s2 конечно возвращаются, но после запуска FOREACHCONTEXT, пусть сразу отсекается.
Хотя что касается sSep, то NULL там по логике кода реально никогда не будет.

Ладно, пошел переделывать свой "продакшн".
...
Рейтинг: 0 / 0
Форумы / C++ [игнор отключен] [закрыт для гостей] / Как распарсить строку например 12345;SEPARATOR=67890 на две 12345 и 67890 / 20 сообщений из 20, страница 1 из 1
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]